Imported Upstream version 1.31.1 08/183508/1 upstream/1.31.1
authorSeonah Moon <seonah1.moon@samsung.com>
Fri, 6 Jul 2018 05:56:24 +0000 (14:56 +0900)
committerSeonah Moon <seonah1.moon@samsung.com>
Fri, 6 Jul 2018 05:56:34 +0000 (14:56 +0900)
Change-Id: I24bcfa3f9948d7c84ad23d3a1188ee5715df0733

426 files changed:
AUTHORS
CMakeLists.txt
ChangeLog
Dockerfile.android
INSTALL
Makefile.in
README.rst
aclocal.m4
cmakeconfig.h.in
config.guess
config.h.in
config.sub
configure
configure.ac
contrib/Makefile.in
depcomp
doc/CMakeLists.txt
doc/Makefile.am
doc/Makefile.in
doc/_exts/sphinxcontrib/rubydomain.py
doc/bash_completion/h2load
doc/bash_completion/nghttp
doc/bash_completion/nghttpx
doc/conf.py.in
doc/enums.rst
doc/h2load.1
doc/h2load.1.rst
doc/nghttp.1
doc/nghttp.1.rst
doc/nghttp2_hd_deflate_hd.rst
doc/nghttp2_hd_deflate_hd_vec.rst
doc/nghttp2_option_set_no_http_messaging.rst
doc/nghttp2_rcbuf_is_static.rst [new file with mode: 0644]
doc/nghttp2_session_callbacks_set_error_callback.rst
doc/nghttp2_session_callbacks_set_error_callback2.rst [new file with mode: 0644]
doc/nghttp2_session_set_user_data.rst [new file with mode: 0644]
doc/nghttp2_submit_trailer.rst
doc/nghttpd.1
doc/nghttpx.1
doc/nghttpx.1.rst
doc/programmers-guide.rst
doc/sources/building-android-binary.rst
doc/sources/contribute.rst
doc/sources/h2load-howto.rst
doc/sources/libnghttp2_asio.rst
doc/sources/nghttpx-howto.rst
doc/sources/tutorial-client.rst
doc/types.rst
examples/CMakeLists.txt
examples/Makefile.am
examples/Makefile.in
examples/client.c
examples/deflate.c
examples/libevent-client.c
examples/libevent-server.c
integration-tests/Makefile.am
integration-tests/Makefile.in
integration-tests/nghttpx_http1_test.go
integration-tests/nghttpx_http2_test.go
integration-tests/nghttpx_spdy_test.go [deleted file]
integration-tests/server_tester.go
lib/CMakeLists.txt
lib/Makefile.in
lib/Makefile.msvc
lib/includes/Makefile.in
lib/includes/nghttp2/nghttp2.h
lib/includes/nghttp2/nghttp2ver.h
lib/nghttp2_buf.h
lib/nghttp2_callbacks.c
lib/nghttp2_callbacks.h
lib/nghttp2_debug.c
lib/nghttp2_frame.c
lib/nghttp2_frame.h
lib/nghttp2_hd.c
lib/nghttp2_hd.h
lib/nghttp2_helper.c
lib/nghttp2_http.c
lib/nghttp2_mem.c
lib/nghttp2_outbound_item.h
lib/nghttp2_pq.h
lib/nghttp2_queue.h
lib/nghttp2_rcbuf.c
lib/nghttp2_session.c
lib/nghttp2_session.h
lib/nghttp2_stream.c
lib/nghttp2_submit.c
lib/version.rc.in
python/Makefile.in
script/Makefile.in
src/CMakeLists.txt
src/HttpServer.cc
src/Makefile.am
src/Makefile.in
src/allocator.h
src/app_helper.cc
src/app_helper.h
src/asio_client_session.cc
src/asio_client_session_impl.cc
src/asio_client_session_impl.h
src/asio_client_session_tls_impl.cc
src/asio_client_tls_context.cc
src/asio_common.cc
src/asio_server_http2_handler.h
src/asio_server_http2_impl.cc
src/asio_server_tls_context.cc
src/h2load.cc
src/h2load.h
src/h2load_http2_session.cc
src/h2load_spdy_session.cc [deleted file]
src/h2load_spdy_session.h [deleted file]
src/http2.cc
src/http2.h
src/http2_test.cc
src/includes/Makefile.in
src/includes/nghttp2/asio_http2_client.h
src/inflatehd.cc
src/memchunk.h
src/memchunk_test.cc
src/network.h
src/nghttp.cc
src/nghttp.h
src/nghttp2_gzip.c
src/nghttp2_gzip_test.c
src/nghttpd.cc
src/shrpx-unittest.cc
src/shrpx.cc
src/shrpx_accept_handler.cc
src/shrpx_api_downstream_connection.cc
src/shrpx_api_downstream_connection.h
src/shrpx_client_handler.cc
src/shrpx_client_handler.h
src/shrpx_config.cc
src/shrpx_config.h
src/shrpx_config_test.cc
src/shrpx_connect_blocker.h
src/shrpx_connection.cc
src/shrpx_connection.h
src/shrpx_connection_handler.cc
src/shrpx_connection_handler.h
src/shrpx_dns_resolver.cc
src/shrpx_dns_tracker.cc
src/shrpx_downstream.cc
src/shrpx_downstream.h
src/shrpx_downstream_queue.cc
src/shrpx_downstream_test.cc
src/shrpx_downstream_test.h
src/shrpx_exec.cc
src/shrpx_http.cc
src/shrpx_http.h
src/shrpx_http2_downstream_connection.cc
src/shrpx_http2_session.cc
src/shrpx_http2_upstream.cc
src/shrpx_http_downstream_connection.cc
src/shrpx_http_downstream_connection.h
src/shrpx_http_test.cc
src/shrpx_http_test.h
src/shrpx_https_upstream.cc
src/shrpx_live_check.cc
src/shrpx_log.cc
src/shrpx_log.h
src/shrpx_memcached_connection.cc
src/shrpx_memcached_connection.h
src/shrpx_mruby_module_env.cc
src/shrpx_mruby_module_request.cc
src/shrpx_mruby_module_response.cc
src/shrpx_router.cc
src/shrpx_router.h
src/shrpx_router_test.cc
src/shrpx_router_test.h
src/shrpx_signal.cc
src/shrpx_signal.h
src/shrpx_spdy_upstream.cc [deleted file]
src/shrpx_spdy_upstream.h [deleted file]
src/shrpx_tls.cc [moved from src/shrpx_ssl.cc with 77% similarity]
src/shrpx_tls.h [moved from src/shrpx_ssl.h with 84% similarity]
src/shrpx_tls_test.cc [moved from src/shrpx_ssl_test.cc with 86% similarity]
src/shrpx_tls_test.h [moved from src/shrpx_ssl_test.h with 84% similarity]
src/shrpx_worker.cc
src/shrpx_worker.h
src/shrpx_worker_process.cc
src/shrpx_worker_test.cc
src/ssl_compat.h
src/tls.cc [moved from src/ssl.cc with 88% similarity]
src/tls.h [moved from src/ssl.h with 79% similarity]
src/util.cc
src/util.h
src/util_test.cc
tests/Makefile.in
tests/failmalloc.c
tests/failmalloc_test.c
tests/main.c
tests/malloc_wrapper.c
tests/nghttp2_frame_test.c
tests/nghttp2_hd_test.c
tests/nghttp2_map_test.c
tests/nghttp2_pq_test.c
tests/nghttp2_session_test.c
tests/nghttp2_test_helper.c
tests/testdata/Makefile.in
third-party/Makefile.in
third-party/build_config.rb
third-party/mruby/AUTHORS
third-party/mruby/ChangeLog [deleted file]
third-party/mruby/MITL
third-party/mruby/README.md
third-party/mruby/Rakefile
third-party/mruby/appveyor.yml [new file with mode: 0644]
third-party/mruby/appveyor_config.rb [new file with mode: 0644]
third-party/mruby/build_config.rb
third-party/mruby/doc/guides/compile.md
third-party/mruby/doc/guides/debugger.md
third-party/mruby/doc/guides/gc-arena-howto.md
third-party/mruby/doc/guides/mrbconf.md
third-party/mruby/doc/guides/mrbgems.md
third-party/mruby/doc/limitations.md [new file with mode: 0644]
third-party/mruby/examples/mrbgems/ruby_extension_example/mrbgem.rake
third-party/mruby/examples/targets/build_config_ArduinoDue.rb
third-party/mruby/examples/targets/build_config_IntelEdison.rb [new file with mode: 0644]
third-party/mruby/examples/targets/build_config_RX630.rb [new file with mode: 0644]
third-party/mruby/examples/targets/build_config_android_arm64-v8a.rb [new file with mode: 0644]
third-party/mruby/examples/targets/build_config_android_armeabi.rb [new file with mode: 0644]
third-party/mruby/examples/targets/build_config_android_armeabi_v7a_neon_hard.rb [new file with mode: 0644]
third-party/mruby/examples/targets/build_config_chipKITMax32.rb
third-party/mruby/include/mrbconf.h
third-party/mruby/include/mruby.h
third-party/mruby/include/mruby/array.h
third-party/mruby/include/mruby/boxing_nan.h
third-party/mruby/include/mruby/boxing_word.h
third-party/mruby/include/mruby/class.h
third-party/mruby/include/mruby/common.h
third-party/mruby/include/mruby/compile.h
third-party/mruby/include/mruby/data.h
third-party/mruby/include/mruby/debug.h
third-party/mruby/include/mruby/dump.h
third-party/mruby/include/mruby/error.h
third-party/mruby/include/mruby/gc.h
third-party/mruby/include/mruby/hash.h
third-party/mruby/include/mruby/irep.h
third-party/mruby/include/mruby/istruct.h [new file with mode: 0644]
third-party/mruby/include/mruby/khash.h
third-party/mruby/include/mruby/numeric.h
third-party/mruby/include/mruby/object.h
third-party/mruby/include/mruby/opcode.h
third-party/mruby/include/mruby/proc.h
third-party/mruby/include/mruby/range.h
third-party/mruby/include/mruby/re.h
third-party/mruby/include/mruby/string.h
third-party/mruby/include/mruby/throw.h
third-party/mruby/include/mruby/value.h
third-party/mruby/include/mruby/variable.h
third-party/mruby/include/mruby/version.h
third-party/mruby/minirake
third-party/mruby/mrbgems/default.gembox
third-party/mruby/mrbgems/mruby-array-ext/mrblib/array.rb
third-party/mruby/mrbgems/mruby-array-ext/src/array.c
third-party/mruby/mrbgems/mruby-array-ext/test/array.rb
third-party/mruby/mrbgems/mruby-bin-debugger/bintest/mrdb.rb [changed mode: 0755->0644]
third-party/mruby/mrbgems/mruby-bin-debugger/bintest/print.rb [changed mode: 0755->0644]
third-party/mruby/mrbgems/mruby-bin-debugger/mrbgem.rake [changed mode: 0755->0644]
third-party/mruby/mrbgems/mruby-bin-debugger/tools/mrdb/apibreak.c [changed mode: 0755->0644]
third-party/mruby/mrbgems/mruby-bin-debugger/tools/mrdb/apibreak.h [changed mode: 0755->0644]
third-party/mruby/mrbgems/mruby-bin-debugger/tools/mrdb/apilist.c [changed mode: 0755->0644]
third-party/mruby/mrbgems/mruby-bin-debugger/tools/mrdb/apilist.h [changed mode: 0755->0644]
third-party/mruby/mrbgems/mruby-bin-debugger/tools/mrdb/apiprint.c [changed mode: 0755->0644]
third-party/mruby/mrbgems/mruby-bin-debugger/tools/mrdb/apiprint.h [changed mode: 0755->0644]
third-party/mruby/mrbgems/mruby-bin-debugger/tools/mrdb/cmdbreak.c [changed mode: 0755->0644]
third-party/mruby/mrbgems/mruby-bin-debugger/tools/mrdb/cmdmisc.c [changed mode: 0755->0644]
third-party/mruby/mrbgems/mruby-bin-debugger/tools/mrdb/cmdprint.c [changed mode: 0755->0644]
third-party/mruby/mrbgems/mruby-bin-debugger/tools/mrdb/cmdrun.c [changed mode: 0755->0644]
third-party/mruby/mrbgems/mruby-bin-debugger/tools/mrdb/mrdb.c [changed mode: 0755->0644]
third-party/mruby/mrbgems/mruby-bin-debugger/tools/mrdb/mrdb.h [changed mode: 0755->0644]
third-party/mruby/mrbgems/mruby-bin-debugger/tools/mrdb/mrdbconf.h [changed mode: 0755->0644]
third-party/mruby/mrbgems/mruby-bin-debugger/tools/mrdb/mrdberror.h [changed mode: 0755->0644]
third-party/mruby/mrbgems/mruby-bin-mirb/tools/mirb/mirb.c
third-party/mruby/mrbgems/mruby-bin-mrbc/tools/mrbc/mrbc.c
third-party/mruby/mrbgems/mruby-bin-mruby-config/mrbgem.rake
third-party/mruby/mrbgems/mruby-bin-mruby-config/mruby-config
third-party/mruby/mrbgems/mruby-bin-mruby-config/mruby-config.bat
third-party/mruby/mrbgems/mruby-bin-mruby/bintest/mruby.rb
third-party/mruby/mrbgems/mruby-bin-mruby/mrbgem.rake
third-party/mruby/mrbgems/mruby-bin-mruby/tools/mruby/mruby.c
third-party/mruby/mrbgems/mruby-bin-strip/tools/mruby-strip/mruby-strip.c
third-party/mruby/mrbgems/mruby-class-ext/mrbgem.rake [new file with mode: 0644]
third-party/mruby/mrbgems/mruby-class-ext/src/class.c [new file with mode: 0644]
third-party/mruby/mrbgems/mruby-class-ext/test/module.rb [new file with mode: 0644]
third-party/mruby/mrbgems/mruby-compiler/bintest/mrbc.rb
third-party/mruby/mrbgems/mruby-compiler/core/codegen.c
third-party/mruby/mrbgems/mruby-compiler/core/node.h
third-party/mruby/mrbgems/mruby-compiler/core/parse.y
third-party/mruby/mrbgems/mruby-compiler/mrbgem.rake
third-party/mruby/mrbgems/mruby-enum-ext/mrblib/enum.rb
third-party/mruby/mrbgems/mruby-enum-ext/test/enum.rb
third-party/mruby/mrbgems/mruby-enum-lazy/mrbgem.rake
third-party/mruby/mrbgems/mruby-enum-lazy/mrblib/lazy.rb
third-party/mruby/mrbgems/mruby-enum-lazy/test/lazy.rb
third-party/mruby/mrbgems/mruby-enumerator/mrblib/enumerator.rb
third-party/mruby/mrbgems/mruby-enumerator/test/enumerator.rb
third-party/mruby/mrbgems/mruby-error/mrbgem.rake
third-party/mruby/mrbgems/mruby-error/src/exception.c
third-party/mruby/mrbgems/mruby-error/test/exception.c
third-party/mruby/mrbgems/mruby-error/test/exception.rb
third-party/mruby/mrbgems/mruby-eval/src/eval.c
third-party/mruby/mrbgems/mruby-eval/test/eval.rb
third-party/mruby/mrbgems/mruby-exit/src/mruby-exit.c
third-party/mruby/mrbgems/mruby-fiber/src/fiber.c
third-party/mruby/mrbgems/mruby-hash-ext/mrbgem.rake
third-party/mruby/mrbgems/mruby-hash-ext/mrblib/hash.rb
third-party/mruby/mrbgems/mruby-hash-ext/src/hash-ext.c
third-party/mruby/mrbgems/mruby-hash-ext/test/hash.rb
third-party/mruby/mrbgems/mruby-inline-struct/mrbgem.rake [new file with mode: 0644]
third-party/mruby/mrbgems/mruby-inline-struct/test/inline.c [new file with mode: 0644]
third-party/mruby/mrbgems/mruby-inline-struct/test/inline.rb [new file with mode: 0644]
third-party/mruby/mrbgems/mruby-kernel-ext/src/kernel.c
third-party/mruby/mrbgems/mruby-kernel-ext/test/kernel.rb
third-party/mruby/mrbgems/mruby-math/src/math.c
third-party/mruby/mrbgems/mruby-numeric-ext/mrblib/numeric_ext.rb
third-party/mruby/mrbgems/mruby-numeric-ext/src/numeric_ext.c
third-party/mruby/mrbgems/mruby-numeric-ext/test/numeric.rb
third-party/mruby/mrbgems/mruby-object-ext/src/object.c
third-party/mruby/mrbgems/mruby-object-ext/test/object.rb
third-party/mruby/mrbgems/mruby-objectspace/src/mruby_objectspace.c
third-party/mruby/mrbgems/mruby-print/src/print.c
third-party/mruby/mrbgems/mruby-proc-ext/src/proc.c
third-party/mruby/mrbgems/mruby-proc-ext/test/proc.c
third-party/mruby/mrbgems/mruby-proc-ext/test/proc.rb
third-party/mruby/mrbgems/mruby-random/src/mt19937ar.c
third-party/mruby/mrbgems/mruby-random/src/mt19937ar.h
third-party/mruby/mrbgems/mruby-random/src/random.c
third-party/mruby/mrbgems/mruby-random/src/random.h
third-party/mruby/mrbgems/mruby-random/test/random.rb
third-party/mruby/mrbgems/mruby-range-ext/mrblib/range.rb [new file with mode: 0644]
third-party/mruby/mrbgems/mruby-range-ext/src/range.c
third-party/mruby/mrbgems/mruby-range-ext/test/range.rb
third-party/mruby/mrbgems/mruby-sprintf/src/kernel.c
third-party/mruby/mrbgems/mruby-sprintf/src/sprintf.c
third-party/mruby/mrbgems/mruby-sprintf/test/sprintf.rb
third-party/mruby/mrbgems/mruby-string-ext/mrbgem.rake
third-party/mruby/mrbgems/mruby-string-ext/mrblib/string.rb
third-party/mruby/mrbgems/mruby-string-ext/src/string.c
third-party/mruby/mrbgems/mruby-string-ext/test/string.rb
third-party/mruby/mrbgems/mruby-struct/mrblib/struct.rb
third-party/mruby/mrbgems/mruby-struct/src/struct.c
third-party/mruby/mrbgems/mruby-struct/test/struct.rb
third-party/mruby/mrbgems/mruby-symbol-ext/src/symbol.c
third-party/mruby/mrbgems/mruby-test/driver.c
third-party/mruby/mrbgems/mruby-test/init_mrbtest.c
third-party/mruby/mrbgems/mruby-test/mrbgem.rake
third-party/mruby/mrbgems/mruby-time/src/time.c
third-party/mruby/mrbgems/mruby-time/test/time.rb
third-party/mruby/mrblib/00class.rb [new file with mode: 0644]
third-party/mruby/mrblib/10error.rb [moved from third-party/mruby/mrblib/error.rb with 89% similarity]
third-party/mruby/mrblib/array.rb
third-party/mruby/mrblib/class.rb [deleted file]
third-party/mruby/mrblib/enum.rb
third-party/mruby/mrblib/hash.rb
third-party/mruby/mrblib/init_mrblib.c
third-party/mruby/mrblib/numeric.rb
third-party/mruby/mrblib/range.rb
third-party/mruby/mrblib/string.rb
third-party/mruby/src/array.c
third-party/mruby/src/backtrace.c
third-party/mruby/src/class.c
third-party/mruby/src/codedump.c
third-party/mruby/src/compar.c
third-party/mruby/src/debug.c
third-party/mruby/src/dump.c
third-party/mruby/src/enum.c
third-party/mruby/src/error.c
third-party/mruby/src/error.h
third-party/mruby/src/etc.c
third-party/mruby/src/fmt_fp.c
third-party/mruby/src/gc.c
third-party/mruby/src/hash.c
third-party/mruby/src/init.c
third-party/mruby/src/kernel.c
third-party/mruby/src/load.c
third-party/mruby/src/mruby_core.rake
third-party/mruby/src/numeric.c
third-party/mruby/src/object.c
third-party/mruby/src/opcode.h
third-party/mruby/src/pool.c
third-party/mruby/src/print.c
third-party/mruby/src/proc.c
third-party/mruby/src/range.c
third-party/mruby/src/state.c
third-party/mruby/src/string.c
third-party/mruby/src/symbol.c
third-party/mruby/src/value_array.h
third-party/mruby/src/variable.c
third-party/mruby/src/version.c
third-party/mruby/src/vm.c
third-party/mruby/tasks/gitlab.rake [new file with mode: 0644]
third-party/mruby/tasks/libmruby.rake
third-party/mruby/tasks/mrbgem_spec.rake
third-party/mruby/tasks/mrbgems.rake
third-party/mruby/tasks/mruby_build.rake
third-party/mruby/tasks/mruby_build_commands.rake
third-party/mruby/tasks/mruby_build_gem.rake
third-party/mruby/tasks/toolchains/android.rake
third-party/mruby/tasks/toolchains/clang.rake
third-party/mruby/tasks/toolchains/gcc.rake
third-party/mruby/tasks/toolchains/openwrt.rake [new file with mode: 0644]
third-party/mruby/tasks/toolchains/visualcpp.rake
third-party/mruby/test/assert.rb
third-party/mruby/test/bintest.rb
third-party/mruby/test/t/array.rb
third-party/mruby/test/t/class.rb
third-party/mruby/test/t/codegen.rb [new file with mode: 0644]
third-party/mruby/test/t/exception.rb
third-party/mruby/test/t/float.rb
third-party/mruby/test/t/hash.rb
third-party/mruby/test/t/integer.rb
third-party/mruby/test/t/iterations.rb [new file with mode: 0644]
third-party/mruby/test/t/kernel.rb
third-party/mruby/test/t/lang.rb [new file with mode: 0755]
third-party/mruby/test/t/literals.rb
third-party/mruby/test/t/module.rb
third-party/mruby/test/t/nil.rb
third-party/mruby/test/t/proc.rb
third-party/mruby/test/t/range.rb
third-party/mruby/test/t/string.rb
third-party/mruby/test/t/superclass.rb
third-party/mruby/test/t/symbol.rb
third-party/mruby/test/t/syntax.rb
third-party/mruby/test/t/unicode.rb
third-party/neverbleed/neverbleed.c

diff --git a/AUTHORS b/AUTHORS
index 829ecae..abcced7 100644 (file)
--- a/AUTHORS
+++ b/AUTHORS
@@ -21,21 +21,26 @@ Amir Pakdel
 Anders Bakken
 Andreas Pohl
 Andy Davies
+Angus Gratton
+Anna Henningsen
 Ant Bryan
 Benedikt Christoph Wolters
-Benedikt Christoph Wolters
-Bernard Spil
+Benjamin Peterson
 Bernard Spil
 Brian Card
 Brian Suh
+Daniel Evers
 Daniel Stenberg
 Dave Reisner
 David Beitey
 David Weekly
+Dmitriy Vetutnev
+Dylan Plecki
 Etienne Cimon
 Fabian Möller
 Fabian Wiesel
 Gabi Davar
+Gitai
 Google Inc.
 Jacob Champion
 Jan-E
@@ -50,11 +55,15 @@ Kenny (kang-yen) Peng
 Kenny Peng
 Kit Chan
 Kyle Schomp
+LazyHamster
 Lucas Pardue
 MATSUMOTO Ryosuke
+Marc Bachmann
 Matt Rudary
+Matt Way
 Mike Conlen
 Mike Frysinger
+Mike Lothian
 Nicholas Hurley
 Nora Shoemaker
 Peeyush Aggarwal
@@ -63,15 +72,21 @@ Piotr Sikora
 Raul Gutierrez Segales
 Remo E
 Reza Tavakoli
+Rick Lei
 Ross Smith II
 Scott Mitchell
+Sebastiaan Deckers
+Simone Basso
+Soham Sinha
 Stefan Eissing
 Stephen Ludin
 Sunpoet Po-Chuan Hsieh
 Svante Signell
 Syohei YOSHIDA
+Tapanito
 Tatsuhiko Kubo
 Tatsuhiro Tsujikawa
+Tobias Geerinckx-Rice
 Tom Harwood
 Tomasz Buchert
 Tomasz Torcz
@@ -89,6 +104,7 @@ dalf
 es
 fangdingjun
 kumagi
+lstefani
 makovich
 mod-h2-dev
 moparisthebest
index 6d31e27..9ab152e 100644 (file)
 
 cmake_minimum_required(VERSION 3.0)
 # XXX using 1.8.90 instead of 1.9.0-DEV
-project(nghttp2 VERSION 1.20.0)
+project(nghttp2 VERSION 1.31.1)
 
 # See versioning rule:
 #  http://www.gnu.org/software/libtool/manual/html_node/Updating-version-info.html
-set(LT_CURRENT  27)
-set(LT_REVISION 0)
-set(LT_AGE      13)
+set(LT_CURRENT  30)
+set(LT_REVISION 1)
+set(LT_AGE      16)
 
-set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake")
+set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake" ${CMAKE_MODULE_PATH})
 include(Version)
 
 math(EXPR LT_SOVERSION "${LT_CURRENT} - ${LT_AGE}")
@@ -79,7 +79,7 @@ else()
   set(ENABLE_PYTHON_BINDINGS_DEFAULT OFF)
 endif()
 
-find_package(LibXml2 2.7.7)
+find_package(LibXml2 2.6.26)
 set(WITH_LIBXML2_DEFAULT    ${LIBXML2_FOUND})
 find_package(Jemalloc)
 set(WITH_JEMALLOC_DEFAULT   ${JEMALLOC_FOUND})
@@ -106,23 +106,13 @@ endif()
 foreach(_build_type "Release" "MinSizeRel" "RelWithDebInfo")
   foreach(_lang C CXX)
     string(TOUPPER "CMAKE_${_lang}_FLAGS_${_build_type}" _var)
-    string(REGEX REPLACE "(^| )[/-]D *NDEBUG($| )" "" ${_var} "${${_var}}")
+    string(REGEX REPLACE "(^| )[/-]D *NDEBUG($| )" " " ${_var} "${${_var}}")
   endforeach()
 endforeach()
 
-#
-# 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(CMAKE_C_COMPILER_ID MATCHES "GNU" OR CMAKE_C_COMPILER_ID MATCHES "Clang")
-  set(HINT_UNUSED_PARAM   "__attribute__((unused))")
   set(HINT_NORETURN       "__attribute__((noreturn))")
 else()
-  set(HINT_UNUSED_PARAM)
   set(HINT_NORETURN)
 endif()
 
@@ -312,6 +302,7 @@ check_type_size("time_t"  SIZEOF_TIME_T)
 include(CheckFunctionExists)
 check_function_exists(_Exit     HAVE__EXIT)
 check_function_exists(accept4   HAVE_ACCEPT4)
+check_function_exists(mkostemp  HAVE_MKOSTEMP)
 
 include(CheckSymbolExists)
 # XXX does this correctly detect initgroups (un)availability on cygwin?
index f2ae147..a76e6b9 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
-commit acd05f2aa59b5cfe464648570448b4e99dc99973 (HEAD, tag: v1.20.0, origin/master, origin/HEAD, master)
+commit 1e22b36c61d52bb0446a63f5994b1fbe8c7ce0db (HEAD, tag: v1.31.1, origin/v1.31.x, origin/HEAD, v1.31.x)
 Author:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
-AuthorDate: 2017-02-26
+AuthorDate: 2018-04-07
 Commit:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
-CommitDate: 2017-02-26
+CommitDate: 2018-04-07
 
-    Update bash_completion
+    Update manual pages
 
-commit bb7718a64a5b14f1940ad3b369bbd199ea8511c8
+commit 0f818baf61c5762093d23520f7ee513d6e9e942e
 Author:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
-AuthorDate: 2017-02-26
+AuthorDate: 2018-04-07
 Commit:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
-CommitDate: 2017-02-26
+CommitDate: 2018-04-07
 
-    Update man pages
+    Bump up version number to 1.31.1
 
-commit e59fc725abae043c1b83fb33f65fc03af9c16c2e
+commit c411d16945d658a181d92ca36bfea30853edab37
 Author:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
-AuthorDate: 2017-02-26
+AuthorDate: 2018-04-07
 Commit:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
-CommitDate: 2017-02-26
+CommitDate: 2018-04-07
 
-    Bump up version number to 1.20.0, and LT revision to 27:0:13
-
-commit 4fcd0fc144b5ff42ce513dee4419c1e016a051b2
-Author:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
-AuthorDate: 2017-02-26
-Commit:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
-CommitDate: 2017-02-26
-
-    Update AUTHORS
-
-commit 373be22d7edc8cacd0e374cc8d3166aafd474394
-Author:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
-AuthorDate: 2017-02-23
-Commit:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
-CommitDate: 2017-02-23
-
-    nghttpx: Simpler
-
-commit b647a7c5b7d196d9a81b2aef9e1bafa61cc2bea8
-Author:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
-AuthorDate: 2017-02-23
-Commit:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
-CommitDate: 2017-02-23
-
-    nghttpx: Simplify code using parse_uint
-
-commit 46ba9e080d8c0d4ba1de2cee0ea4ade9343e5a46
-Author:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
-AuthorDate: 2017-02-22
-Commit:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
-CommitDate: 2017-02-22
-
-    Update doc
-
-commit e1b8317ae8886b9e490073f64cddc6ae156f213e
-Author:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
-AuthorDate: 2017-02-22
-Commit:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
-CommitDate: 2017-02-22
-
-    nghttpx: Strip version number from server header field
-
-commit ea67864e0888a1e9319d42e856cc7f6c86d24390
-Author:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
-AuthorDate: 2017-02-21
-Commit:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
-CommitDate: 2017-02-21
-
-    Remove SPDY build instruction for android
-
-commit 6b286e0d94a3c9e446921312e9072d6ee94d78fb
-Author:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
-AuthorDate: 2017-02-21
-Commit:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
-CommitDate: 2017-02-21
-
-    Add SPDY support deprecation warning in README
-
-commit d10ea5ea06f69db7b525e19c4f321bd68acf0c71
-Author:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
-AuthorDate: 2017-02-21
-Commit:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
-CommitDate: 2017-02-21
-
-    Disable spdylay detection by default
-
-commit 2af57c3cfc896a72b60bfdfd74afb4b4f9287e35
-Author:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
-AuthorDate: 2017-02-21
-Commit:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
-CommitDate: 2017-02-21
-
-    nghttpx: Add --single-worker option
-    
-    Previously, nghttpx will use only one single thread inside the worker
-    process if --workers=1 (this is default).  If --workers=N, N > 1, we
-    use additional threads for accepting connections, or API request
-    processing, etc.
-    
-    With this commit, we use the same processing model for N > 1 even if N
-    == 1.  To restore the original single thread execution mode,
-    --single-worker option is added.  If threading is disabled
-    --single-worker is always true.
-
-commit 0c8b1a4f74c256caa134770586c846c3fefffef2
-Author:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
-AuthorDate: 2017-02-21
-Commit:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
-CommitDate: 2017-02-21
-
-    nghttpx: Fix bug that send_reply does not participate graceful shutdown
-
-commit 9d16292fe4e1a782675c291e4719c7b443e390bf
-Author:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
-AuthorDate: 2017-02-20
-Commit:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
-CommitDate: 2017-02-20
-
-    nghttpx: Add --frontend-max-requests option
-
-commit e2b9590c0f7df4d61c1d72ad5b465fb98d1d8efa
-Author:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
-AuthorDate: 2017-02-20
-Commit:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
-CommitDate: 2017-02-20
-
-    nghttpx: Enable stream-write-timeout by default
-
-commit 24fb640a55f91a86966925a3bad0ffce0d9ebefb
-Author:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
-AuthorDate: 2017-02-20
-Commit:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
-CommitDate: 2017-02-20
-
-    nghttpx: Fix stream wtimer handling
-
-commit e6a05cb66d6f0fe778c1ce21a48e9a493989c2ed
-Author:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
-AuthorDate: 2017-02-20
-Commit:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
-CommitDate: 2017-02-20
-
-    Update bash_completion
-
-commit 12f9b09845db8b43d003ff3fd015abf04d265548
-Author:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
-AuthorDate: 2017-02-20
-Commit:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
-CommitDate: 2017-02-20
-
-    Update man pages
-
-commit 102b98813fe6246f062b0071c4044b5c82c9024e
-Merge: 0797e89a 1f55e5d3
-Author:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
-AuthorDate: 2017-02-20
-Commit:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
-CommitDate: 2017-02-20
-
-    Merge branch 'nghttpx-configrevision-api'
-
-commit 1f55e5d34deee1dd123a3d76a74371fb45b78a72
-Author:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
-AuthorDate: 2017-02-19
-Commit:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
-CommitDate: 2017-02-19
-
-    nghttpx: Document configrevision API
-
-commit 5618e1bbc92c9fe69445977c329ddbdc33b593aa
-Author:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
-AuthorDate: 2017-02-19
-Commit:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
-CommitDate: 2017-02-19
-
-    integration: Add configrevision API tests
-
-commit 450ffaa6f0d33d5926da398a6e4c2f212c66f2c4
-Author:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
-AuthorDate: 2017-02-15
-Commit:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
-CommitDate: 2017-02-19
-
-    nghttpx: Add configrevision API endpoint
-    
-    This commit adds configuration revision, which is considered opaque
-    string, and changes after reloading configuration with SIGHUP.  This
-    revision is returned as a response to configrevision API endpoint.
-    This allows external application to know whether nghttpx has finished
-    reloading new configuration or not.  Note that this revision does not
-    change on backendconfig API calls.
-
-commit dc15832030913d13f1f5664e09f42b9028a7e5e5
-Author:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
-AuthorDate: 2017-02-19
-Commit:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
-CommitDate: 2017-02-19
-
-    nghttpx: Refactor API downstream connection to allow more endpoints
-
-commit 0797e89a904a6614a72d2b420028b33500a92012
-Merge: 1bd68930 9f1543f8
-Author:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
-AuthorDate: 2017-02-19
-Commit:     GitHub <noreply@github.com>
-CommitDate: 2017-02-19
-
-    Merge pull request #819 from nghttp2/nghttpx-https-redirect
-    
-    nghttpx: Redirect to HTTPS URI with redirect-if-no-tls parameter in backend option
-
-commit 9f1543f81e6ee13c5afc97ab4a52a08a05d3780f
-Author:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
-AuthorDate: 2017-02-18
-Commit:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
-CommitDate: 2017-02-18
-
-    integration: Add https redirect tests
-
-commit a7c780a73265f2faa6e484738ebcd21b253cf4cc
-Author:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
-AuthorDate: 2017-02-18
-Commit:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
-CommitDate: 2017-02-18
-
-    nghttpx: Redirect to HTTPS URI with redirect-if-not-tls param
-    
-    This commit removes frontend-tls parameter, and adds
-    redirect-if-not-tls parameter parameter to --backend option.  nghttpx
-    now responds to the request with 308 status code to redirect the
-    request to https URI if frontend connection is not TLS encrypted, and
-    redirect-if-no-tls parameter is used in --backend option.  The port
-    number in Location header field is 443 by default (thus omitted), but
-    it can be configurable using --redirect-https-port option.
-
-commit 1bd6893084b778a80b3894fd9a561fdcef021917
-Author:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
-AuthorDate: 2017-02-18
-Commit:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
-CommitDate: 2017-02-18
-
-    integration: Fix deprecation warnings
-
-commit ae21130b13861eb4a819a3df29b4f879b0f0a46e
-Author:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
-AuthorDate: 2017-02-18
-Commit:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
-CommitDate: 2017-02-18
-
-    integration: Redirect nghttpx stdout/stderr to test driver's stdout/stderr
-
-commit e06ed8574749f7d3ecb956ccea508b2d3e91b368
-Author:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
-AuthorDate: 2017-02-17
-Commit:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
-CommitDate: 2017-02-17
-
-    nghttpx: Fix travis gcc compile error
-
-commit 83fd72c97e22812b975facccee904b9ff4ad9ab9
-Author:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
-AuthorDate: 2017-02-17
-Commit:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
-CommitDate: 2017-02-17
-
-    nghttpx: Use std::chrono::duration_cast
-
-commit ace40f298d04a158ef082887e1c6139735e617cf
-Author:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
-AuthorDate: 2017-02-17
-Commit:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
-CommitDate: 2017-02-17
-
-    nghttpx: Update log time stamp in millisecond interval
-
-commit 1133cc0bbc11aa9b886011c5b9d9199261e4ad77
-Author:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
-AuthorDate: 2017-02-16
-Commit:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
-CommitDate: 2017-02-16
-
-    nghttpx: Don't call get_config() repeatedly
-
-commit 6960039aee09443e6b7962cd5f73ef3e84a23cd8
-Author:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
-AuthorDate: 2017-02-16
-Commit:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
-CommitDate: 2017-02-16
-
-    nghttpx: C++ style cast
-
-commit bf5eeb831b0522e95a02953add7b1f068d87d986
-Author:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
-AuthorDate: 2017-02-16
-Commit:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
-CommitDate: 2017-02-16
-
-    nghttpx: Better error message when private key and certificate are missing
-
-commit e5b84fad098064cb57876a73b5a7f890de053190
-Author:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
-AuthorDate: 2017-02-16
-Commit:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
-CommitDate: 2017-02-16
-
-    nghttpx: Fix bug that old config is used during reloading config
-
-commit cfb39171a799ea080e11f968b319d4e2086f8f9a
-Author:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
-AuthorDate: 2017-02-16
-Commit:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
-CommitDate: 2017-02-16
-
-    nghttpx: Remove redundant StringRef ctor invocation
-
-commit 2f6e1ac336dd34603784924f59bcc3c29bb1d3df
-Merge: 368775c2 9e8d9d65
-Author:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
-AuthorDate: 2017-02-15
-Commit:     GitHub <noreply@github.com>
-CommitDate: 2017-02-15
-
-    Merge pull request #816 from nghttp2/tls13
-    
-    Add TLSv1.3 support
-
-commit 9e8d9d658a87a35031c1ef9b182997ce77b2f041
-Author:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
-AuthorDate: 2017-02-15
-Commit:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
-CommitDate: 2017-02-15
-
-    src: Enable TLSv1.3 if OpenSSL supports it
-    
-    If OpenSSL supports TLSv1.3, enable it by default for all applications
-    under src.  BoringSSL can work at the moment although it does not
-    unlock all the features nghttpx offers.  OpenSSL's TLSv1.3 support is
-    still WIP at the time of writing.
-
-commit 6ecfac695436d7987a98b4db033452964a67d959
-Author:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
-AuthorDate: 2017-02-15
-Commit:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
-CommitDate: 2017-02-15
-
-    nghttpx: Parse default TLS min and max versions from string
-
-commit 368775c2edf1043d97be5a48f7a242dd0d2529b9
-Merge: 1e9a094e ceb4dcf3
-Author:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
-AuthorDate: 2017-02-15
-Commit:     GitHub <noreply@github.com>
-CommitDate: 2017-02-15
-
-    Merge pull request #814 from alagoutte/pvs
-    
-    nghttp2_session: fix The 'then' statement is equivalent to the subseq…
-
-commit 1e9a094edd2aa530dde10c2f936b3d02095ea4e6
-Merge: d2ef80fe f3a5a0a0
-Author:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
-AuthorDate: 2017-02-15
-Commit:     GitHub <noreply@github.com>
-CommitDate: 2017-02-15
-
-    Merge pull request #811 from nghttp2/nghttp2_option_no_closed_streams
-    
-    Add nghttp2_option_no_closed_streams
-
-commit ceb4dcf3b27907f69aa385618bfcfd2c9f9264c3
-Author:     Alexis La Goutte <alexis.lagoutte@gmail.com>
-AuthorDate: 2017-01-07
-Commit:     Alexis La Goutte <alexis.lagoutte@gmail.com>
-CommitDate: 2017-02-14
-
-    nghttp2_session: fix The 'then' statement is equivalent to the subsequent code fragment found by PVS Studio (V523)
-
-commit d2ef80fe3a74e56b256c856ba7795b7a585efb5d
-Author:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
-AuthorDate: 2017-02-14
-Commit:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
-CommitDate: 2017-02-14
-
-    Update bash_completion
-
-commit 79ae5aed673df298ad3f3d36d097dcf2de8693e6
-Author:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
-AuthorDate: 2017-02-14
-Commit:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
-CommitDate: 2017-02-14
-
-    Update man pages
-
-commit 56e86cd944fbc4e9315696a45558726d28bdbcbe
-Author:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
-AuthorDate: 2017-02-14
-Commit:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
-CommitDate: 2017-02-14
-
-    src: h2 requires >= TLSv1.2
-
-commit 19a311ad62cd88c80ea9914f9f92a5f2f8d4b3ee
-Merge: 001d45ef b36e53cc
-Author:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
-AuthorDate: 2017-02-14
-Commit:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
-CommitDate: 2017-02-14
-
-    Merge branch 'nghttpx-tls-min-max-proto-version'
-
-commit b36e53cccd1417d2412b550c9dbda52aaae34c51
-Author:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
-AuthorDate: 2017-02-13
-Commit:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
-CommitDate: 2017-02-14
-
-    nghttpx: Specify TLS protocol by version range
-    
-    This commit deprecates --tls-proto-list option, and adds 2 new
-    options: --tls-min-proto-version and --tls-max-proto-version to
-    specify minimum and maximum protocol version respectively.  Versions
-    between the two are enabled.  The deprecated --tls-proto-list has
-    empty default value, and acts like enabling only specific protocol
-    versions in the range for now.
-
-commit f3a5a0a0ecc530cdfbe25838ba346b58d6caa733
-Author:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
-AuthorDate: 2017-02-13
-Commit:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
-CommitDate: 2017-02-13
-
-    Add nghttp2_option_no_closed_streams
-    
-    nghttp2_option_no_closed_streams controls whether closed streams are
-    retained or not.  If nonzero is passed to that function's parameter
-    val, a session does not retain closed streams.  It may hurt the shape
-    of priority tree, but can save memory.
-
-commit 001d45efad9c09aadd1a18f23592633a4bd6751a
-Merge: 4bf3cb2c 56c455bc
-Author:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
-AuthorDate: 2017-02-12
-Commit:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
-CommitDate: 2017-02-12
-
-    Merge branch 'nghttpx-graceful-sigusr2'
-
-commit 56c455bca4e1bbea1871925c5014de45d18edde6
-Author:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
-AuthorDate: 2017-02-09
-Commit:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
-CommitDate: 2017-02-12
-
-    nghttpx: Send SIGQUIT to the original master process
-    
-    Previously, after sending SIGUSR2 to the original master process, and
-    the new master process gets ready, user has to send SIGQUIT to the
-    original master process to shut it down gracefully.  With this commit,
-    the new master process sends SIGQUIT to the original master process
-    when it is ready to serve requests, eliminating for user to send
-    SIGQUIT manually.
-    
-    This works nicely with systemd, because now you can replace nghttpx
-    binary with new one by "systemctl kill -s USR2 --kill-who=main
-    nghttpx".
-
-commit 4bf3cb2cc020cf4bd2a8efcb5c4bf3a44a9e180f
-Author:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
-AuthorDate: 2017-02-12
-Commit:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
-CommitDate: 2017-02-12
-
-    Revert "nghttpx: Don't capitalize h1 header fields"
-    
-    This reverts commit f9946649345ee4500292531a6eb0f62474131f5d.
-
-commit c78528d54b56786c9d9147291cbd038dbc104ddb
-Author:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
-AuthorDate: 2017-02-11
-Commit:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
-CommitDate: 2017-02-11
-
-    nghttpx: Restrict HTTP major and minor in 0 or 1
-
-commit f9946649345ee4500292531a6eb0f62474131f5d
-Author:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
-AuthorDate: 2017-02-11
-Commit:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
-CommitDate: 2017-02-11
-
-    nghttpx: Don't capitalize h1 header fields
-
-commit 44e290da6638779dbfe6c4ce52532243f7c16eb6
-Author:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
-AuthorDate: 2017-02-11
-Commit:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
-CommitDate: 2017-02-11
-
-    clang-format
-
-commit 8aed101585b85ed800de53ae3dd3ae820b5fc5ac
-Merge: 54ba1beb 1c31213a
-Author:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
-AuthorDate: 2017-02-11
-Commit:     GitHub <noreply@github.com>
-CommitDate: 2017-02-11
-
-    Merge pull request #805 from pakdel/graceful_stop
-    
-    graceful stop of nghttp2::asio_http2::server::http2
-
-commit 54ba1bebf2b196a016f92db70c5f1e6e82b83da9
-Author:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
-AuthorDate: 2017-02-10
-Commit:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
-CommitDate: 2017-02-10
-
-    Update doc
-
-commit e44c58282ee0a9c4704a17831fbda7e079d03793
-Author:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
-AuthorDate: 2017-02-10
-Commit:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
-CommitDate: 2017-02-10
-
-    Drop privilege of neverbleed daemon first
-
-commit c02b1041d9d55d7571c9a9cfe3badceafe761fe5
-Author:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
-AuthorDate: 2017-02-10
-Commit:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
-CommitDate: 2017-02-10
-
-    nghttpx: Use nullptr instead of NULL
-
-commit 0d20e074369412b02f6248fa79b471070d8ec513
-Author:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
-AuthorDate: 2017-02-10
-Commit:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
-CommitDate: 2017-02-10
-
-    Show SYSTEMD_CFLAGS in summary
-
-commit b7f956c96750520bf58feaeca6bf01c8f6bf2628
-Author:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
-AuthorDate: 2017-02-10
-Commit:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
-CommitDate: 2017-02-10
-
-    Add --with-systemd option to configure
-
-commit 786f52a81ceff9260ee9c02b6fee4a662b6f35da
-Author:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
-AuthorDate: 2017-02-10
-Commit:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
-CommitDate: 2017-02-10
-
-    Document about systemd support in README
-
-commit 23209baaf5828afc2927adf42b717700ebf62194
-Author:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
-AuthorDate: 2017-02-10
-Commit:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
-CommitDate: 2017-02-10
-
-    clang-format
-
-commit 9d2503f9c0e399e7ae27edb91fca7030b0ac0368
-Merge: 8f888b29 fdb75ba5
-Author:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
-AuthorDate: 2017-02-10
-Commit:     GitHub <noreply@github.com>
-CommitDate: 2017-02-10
-
-    Merge pull request #802 from zdzichu/master
-    
-    nghttpx: add systemd support
-
-commit 1c31213aef50832135146b52d327f2adbf49b7ad
-Author:     Amir Pakdel <pakdel@gmail.com>
-AuthorDate: 2017-02-09
-Commit:     Amir Pakdel <pakdel@gmail.com>
-CommitDate: 2017-02-09
-
-    More graceful stop of nghttp2::asio_http2::server::http2
-    
-    Explicit io_service::stop() will prevent running streams from
-    finishing their task. That means if there are already reposnes
-    that we have called end(std::string) on them and they have not
-    finished sending back their data, they will be closed with a
-    NGHTTP2_INTERNAL_ERROR
-    Instead, we can stop accepting connections and destroy all
-    io_service::work objects to signals end of work.
-
-commit fdb75ba5fe341454853c6335e349d348ba99d69b
-Author:     Tomasz Torcz <tomek@pipebreaker.pl>
-AuthorDate: 2017-02-08
-Commit:     Tomasz Torcz <tomek@pipebreaker.pl>
-CommitDate: 2017-02-09
-
-    nghttpx: add systemd support
-    
-      Add systemd's Type=notify support by sending information about
-     master process PID around forks.
-      Add some hardening option to service unit.
-
-commit 8f888b29bdc5e1d29b2e8324c1d859207d4e3a9b
-Author:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
-AuthorDate: 2017-02-09
-Commit:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
-CommitDate: 2017-02-09
-
-    clang-format
-
-commit 298808f27613c2e81175f64b753a190bd8b598ec
-Author:     clemahieu <clemahieu@gmail.com>
-AuthorDate: 2017-02-08
-Commit:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
-CommitDate: 2017-02-09
-
-    Holding more shared_ptrs instead of raw ptrs to make sure called objects don't get deleted.
-
-commit a231874e1e1e1fd4ad49ee217215a41abc1e3825
-Merge: 2101f4ae 68a724cf
-Author:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
-AuthorDate: 2017-02-08
-Commit:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
-CommitDate: 2017-02-08
-
-    Merge branch 'nghttpx-certs-per-sigalg'
-
-commit 2101f4ae3f9081e8cf9821d350d6c1ca9112ad14
-Merge: 4a06f968 9a85c526
-Author:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
-AuthorDate: 2017-02-08
-Commit:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
-CommitDate: 2017-02-08
-
-    Merge branch 'mruby-send-1xx'
-
-commit 4a06f9684f8c4f047923c41401e5b50a1bf70de0
-Author:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
-AuthorDate: 2017-02-08
-Commit:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
-CommitDate: 2017-02-08
-
-    nghttpx: Fix crash on SIGHUP with multi thread configuration
-
-commit 5f31c09410e124da3001239984bd6ad016062885
-Author:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
-AuthorDate: 2017-02-08
-Commit:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
-CommitDate: 2017-02-08
-
-    Mention about nghttp2_data_source_read_length_callback
-
-commit 9a85c5264aeba2c7156afab814c602bbe4ad86da
-Author:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
-AuthorDate: 2016-11-03
-Commit:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
-CommitDate: 2017-02-08
-
-    nghttpx: Send 1xx non-final response using mruby script
-
-commit fd475e4b2f06c46c88c71e0451e0748cfd32952e
-Merge: 779ec50e 5dc1d116
-Author:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
-AuthorDate: 2017-02-07
-Commit:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
-CommitDate: 2017-02-07
-
-    Merge branch 'oss-fuzz'
-
-commit 5dc1d116c9d88d1befbce6291ca26326c05c1c47
-Author:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
-AuthorDate: 2017-02-06
-Commit:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
-CommitDate: 2017-02-06
-
-    fuzz: Add README
-
-commit c566d3f475ef43d9d3363b1d4e861bb5abe4875d
-Author:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
-AuthorDate: 2017-02-06
-Commit:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
-CommitDate: 2017-02-06
-
-    Add fuzzer for oss-fuzz
-
-commit 68a724cf7b2389ee32d9490c663b5267921f7869
-Author:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
-AuthorDate: 2017-02-04
-Commit:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
-CommitDate: 2017-02-04
-
-    nghttpx: Select certificate by client's supported signature algo
-    
-    nghttpx supports multiple certificates using --subcert option.
-    Previously, SNI hostname is used to select certificate.  With this
-    commit, signature algorithm presented by client is also taken into
-    consideration.  nghttpx now accepts certificates which share the same
-    hostname (CN, SAN), but have different signature algorithm (e.g.,
-    ECDSA+SHA256, RSA+SHA256).
-    
-    Currently, this feature requires OpenSSL >= 1.0.2.  BoringSSL, and
-    LibreSSL do not work since they lack required APIs.
-
-commit 779ec50e73f7179ba15bb22eeaa23a7114472b1d
-Merge: aad3e275 f0b6b950
-Author:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
-AuthorDate: 2017-02-04
-Commit:     GitHub <noreply@github.com>
-CommitDate: 2017-02-04
-
-    Merge pull request #795 from clemahieu/close_stream_iterator
-    
-    close_stream erases from streams_ while it's being iterated over.
-
-commit aad3e275d166edfa37b22187c4a8fed7d2c97f19
-Merge: 7dddac08 1649948e
-Author:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
-AuthorDate: 2017-02-04
-Commit:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
-CommitDate: 2017-02-04
-
-    Merge branch 'clemahieu-acceptor_infinite_loop'
-
-commit 1649948e7851168613cbff72b67da9b63228ac1a
-Author:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
-AuthorDate: 2017-02-04
-Commit:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
-CommitDate: 2017-02-04
-
-    asio: Add curly brackets to avoid possible well known issue
-
-commit 6d3e010ae7f49b14cb1201367e5479a86d9d8e94
-Author:     clemahieu <clemahieu@gmail.com>
-AuthorDate: 2017-01-31
-Commit:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
-CommitDate: 2017-02-04
-
-    Infinite loop in acceptor handler.
-
-commit 7dddac081e42013886b2224ecb87b82f74a43f71
-Author:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
-AuthorDate: 2017-02-04
-Commit:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
-CommitDate: 2017-02-04
-
-    clang-format
-
-commit 588dd332415221de78c241e2030303cd2db1b479
-Merge: 025ec851 14ccb24b
-Author:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
-AuthorDate: 2017-02-04
-Commit:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
-CommitDate: 2017-02-04
-
-    Merge branch 'worenga-github-pr-preload'
-
-commit f0b6b9508de2c1d8be1b9bb226a9ca59a112ed0e
-Author:     clemahieu <clemahieu@gmail.com>
-AuthorDate: 2017-02-03
-Commit:     clemahieu <clemahieu@gmail.com>
-CommitDate: 2017-02-03
-
-    close_stream erases from streams_ while it's being iterated over.
-    
-    The destructor will already clean this structure up.
-
-commit 14ccb24be5dc2df42bf514c6c8cfc70be50bf9a0
-Author:     Benedikt Christoph Wolters <benedikt.wolters@rwth-aachen.de>
-AuthorDate: 2017-02-01
-Commit:     Benedikt Christoph Wolters <benedikt.wolters@rwth-aachen.de>
-CommitDate: 2017-02-01
-
-    add support for link rel="preload" for --get-assets
-
-commit 025ec8514478efec2adef4f360780fc4209bc4b1
-Merge: bd97886d 0b1ddad6
-Author:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
-AuthorDate: 2017-01-31
-Commit:     GitHub <noreply@github.com>
-CommitDate: 2017-01-31
-
-    Merge pull request #790 from nghttp2/nghttpx-backend-frontend-tls-parameter
-    
-    nghttpx: Add frontend-tls parameter to backend to require client TLS
-
-commit bd97886d8ea573854fc9e2f1551f5477f5c0b6b1
-Author:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
-AuthorDate: 2017-01-29
-Commit:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
-CommitDate: 2017-01-29
-
-    nghttpx: Use stack allocated buffer instead of making std::string
-
-commit 0b1ddad62b5b4ee9b7d89f9be4a4bb71107a4351
-Author:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
-AuthorDate: 2017-01-28
-Commit:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
-CommitDate: 2017-01-28
-
-    nghttpx: Add frontend-tls parameter to backend to require client TLS
-
-commit 540853bde86865dfd193c7ce868c378d1d8a0c25
-Author:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
-AuthorDate: 2017-01-28
-Commit:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
-CommitDate: 2017-01-28
-
-    nghttpx: Fix typo
-
-commit c757f7d84807dd8b59bf6b1c5c54e10a6600ee7d
-Author:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
-AuthorDate: 2017-01-28
-Commit:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
-CommitDate: 2017-01-28
-
-    nghttpx: Recommend POST for backendconfig API request
-
-commit 052f3a38719ccda8dedc945591ce2b8eecd39ae3
-Author:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
-AuthorDate: 2017-01-26
-Commit:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
-CommitDate: 2017-01-26
-
-    Update doc
-
-commit 2ae83e871ba78891c19b5f6cd8edbe67b9a0eb3a
-Merge: b72c5f10 1cc08c0a
-Author:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
-AuthorDate: 2017-01-26
-Commit:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
-CommitDate: 2017-01-26
-
-    Merge branch 'Sp1l-master'
-
-commit 1cc08c0a51d273afc0e7b4e11eeb0b889d154ee0
-Author:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
-AuthorDate: 2017-01-26
-Commit:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
-CommitDate: 2017-01-26
-
-    nghttpx: Show warning if PSK options are used but not supported
-
-commit 16be89f9ccba4d37e34ef03f20009a9845efeb99
-Author:     Bernard Spil <brnrd@FreeBSD.org>
-AuthorDate: 2017-01-25
-Commit:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
-CommitDate: 2017-01-26
-
-    nghttpx: Don't build PSK features with LibreSSL
-    
-    LibreSSL removed PSK
-    
-    Signed-off-by: Bernard Spil <brnrd@FreeBSD.org>
-
-commit b72c5f104ea5a7fd28d03db52fa56c3a1b6d4cc9
-Author:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
-AuthorDate: 2017-01-26
-Commit:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
-CommitDate: 2017-01-26
-
-    h2load: Fix wrong req_stat updates
-
-commit 7e6eb7e02a750703125c562199a57b81cc139a28
-Author:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
-AuthorDate: 2017-01-26
-Commit:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
-CommitDate: 2017-01-26
-
-    h2load: Explicitly count the number of requests left and inflight
-
-commit 712b08e8ed92f01e1bde54b7978fd3d2187d3b58
-Author:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
-AuthorDate: 2017-01-25
-Commit:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
-CommitDate: 2017-01-25
-
-    Bump up version number to 1.20.0-DEV
+    Fix frame handling
index 3ea4cf3..e033b46 100644 (file)
 #
 # $ sudo docker run -v /path/to/dest:/out nghttp2-android cp /root/build/nghttp2/src/nghttpx /out
 
-FROM ubuntu:vivid
 
+# Only use standalone-toolchain for reduce size
+FROM ubuntu:xenial
 MAINTAINER Tatsuhiro Tsujikawa
-
-ENV ANDROID_HOME /root/android
-ENV PREFIX $ANDROID_HOME/usr/local
+ENV ANDROID_HOME /root
 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
+ENV NDK_VERSION r14b
 
-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
+RUN apt-get update && \
+    apt-get install -y unzip make binutils autoconf \
+      automake autotools-dev libtool pkg-config git \
+      curl dpkg-dev libxml2-dev genisoimage libc6-i386 \
+      lib32stdc++6 python&& \
+    rm -rf /var/cache/apk/*
 
-WORKDIR /root/build
-RUN curl -L -O http://dl.google.com/android/ndk/android-ndk-r10d-linux-x86_64.bin && \
-    chmod a+x android-ndk-r10d-linux-x86_64.bin && \
-    ./android-ndk-r10d-linux-x86_64.bin && \
-    rm android-ndk-r10d-linux-x86_64.bin
+# Install toolchain
+RUN curl -L -O https://dl.google.com/android/repository/android-ndk-$NDK_VERSION-linux-x86_64.zip && \
+   unzip -q android-ndk-$NDK_VERSION-linux-x86_64.zip && \
+   rm android-ndk-$NDK_VERSION-linux-x86_64.zip && \
+   mkdir -p $ANDROID_HOME/toolchain && \
+   $ANDROID_HOME/android-ndk-$NDK_VERSION/build/tools/make-standalone-toolchain.sh \
+       --install-dir=$ANDROID_HOME/toolchain \
+       --toolchain=arm-linux-androideabi-4.9 \
+       --force && \
+   rm -r android-ndk-$NDK_VERSION
+
+ENV PREFIX /root/usr/local
 
-WORKDIR /root/build/android-ndk-r10d
-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
+# Setup version of libraries
+ENV OPENSSL_VERSION 1.0.2d
+ENV SPDYLAY_VERSION v1.4.0
+ENV LIBEV_VERSION 4.19
+ENV ZLIB_VERSION 1.2.8
+ENV CARES_VERSION 1.13.0
+ENV NGHTTP2_VERSION v1.24.0
 
 WORKDIR /root/build
-RUN git clone https://github.com/tatsuhiro-t/spdylay
+RUN git clone https://github.com/tatsuhiro-t/spdylay -b $SPDYLAY_VERSION --depth 1
 WORKDIR /root/build/spdylay
 RUN autoreconf -i && \
     ./configure \
@@ -59,22 +67,22 @@ RUN autoreconf -i && \
     make install
 
 WORKDIR /root/build
-RUN curl -L -O https://www.openssl.org/source/openssl-1.0.2d.tar.gz && \
-    tar xf openssl-1.0.2d.tar.gz && \
-    rm openssl-1.0.2d.tar.gz
+RUN curl -L -O https://www.openssl.org/source/openssl-$OPENSSL_VERSION.tar.gz && \
+    tar xf openssl-$OPENSSL_VERSION.tar.gz && \
+    rm openssl-$OPENSSL_VERSION.tar.gz
 
-WORKDIR /root/build/openssl-1.0.2d
+WORKDIR /root/build/openssl-$OPENSSL_VERSION
 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 && \
+RUN curl -L -O http://dist.schmorp.de/libev/Attic/libev-$LIBEV_VERSION.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
+    tar xf libev-$LIBEV_VERSION.tar.gz && \
+    rm libev-$LIBEV_VERSION.tar.gz
 
-WORKDIR /root/build/libev-4.19
+WORKDIR /root/build/libev-$LIBEV_VERSION
 RUN patch -p1 < ../libev-4.19-android.patch && \
     ./configure \
     --host=arm-linux-androideabi \
@@ -87,11 +95,11 @@ RUN patch -p1 < ../libev-4.19-android.patch && \
     make install
 
 WORKDIR /root/build
-RUN curl -L -O http://zlib.net/zlib-1.2.8.tar.gz && \
-    tar xf zlib-1.2.8.tar.gz && \
-    rm zlib-1.2.8.tar.gz
+RUN curl -L -O https://downloads.sourceforge.net/project/libpng/zlib/$ZLIB_VERSION/zlib-$ZLIB_VERSION.tar.gz && \
+    tar xf zlib-$ZLIB_VERSION.tar.gz && \
+    rm zlib-$ZLIB_VERSION.tar.gz
 
-WORKDIR /root/build/zlib-1.2.8
+WORKDIR /root/build/zlib-$ZLIB_VERSION
 RUN HOST=arm-linux-androideabi \
     CC=$HOST-gcc \
     AR=$HOST-ar \
@@ -105,11 +113,26 @@ RUN HOST=arm-linux-androideabi \
     --static && \
     make install
 
+
 WORKDIR /root/build
-RUN git clone https://github.com/nghttp2/nghttp2
+RUN curl -L -O https://c-ares.haxx.se/download/c-ares-$CARES_VERSION.tar.gz && \
+    tar xf c-ares-$CARES_VERSION.tar.gz && \
+    rm c-ares-$CARES_VERSION.tar.gz
+
+WORKDIR /root/build/c-ares-$CARES_VERSION
+RUN ./configure \
+      --host=arm-linux-androideabi \
+      --build=`dpkg-architecture -qDEB_BUILD_GNU_TYPE` \
+      --prefix=$PREFIX \
+      --disable-shared && \
+    make install
+
+WORKDIR /root/build
+RUN git clone https://github.com/nghttp2/nghttp2 -b $NGHTTP2_VERSION --depth 1
 WORKDIR /root/build/nghttp2
 RUN autoreconf -i && \
     ./configure \
+    --enable-app \
     --disable-shared \
     --host=arm-linux-androideabi \
     --build=`dpkg-architecture -qDEB_BUILD_GNU_TYPE` \
@@ -118,11 +141,10 @@ RUN autoreconf -i && \
     --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" && \
+      CC="$TOOLCHAIN"/bin/arm-linux-androideabi-clang \
+      CXX="$TOOLCHAIN"/bin/arm-linux-androideabi-clang++ \
+      CPPFLAGS="-fPIE -I$PREFIX/include" \
+      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
index 2099840..8865734 100644 (file)
--- a/INSTALL
+++ b/INSTALL
@@ -1,8 +1,8 @@
 Installation Instructions
 *************************
 
-Copyright (C) 1994-1996, 1999-2002, 2004-2013 Free Software Foundation,
-Inc.
+   Copyright (C) 1994-1996, 1999-2002, 2004-2016 Free Software
+Foundation, Inc.
 
    Copying and distribution of this file, with or without modification,
 are permitted in any medium without royalty provided the copyright
@@ -12,97 +12,96 @@ without warranty of any kind.
 Basic Installation
 ==================
 
-   Briefly, the shell command `./configure && make && make install'
+   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
+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
+'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
+   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
+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').
+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.
+   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
+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
+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 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.
+  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
+     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.
+  2. Type 'make' to compile the package.
 
-  3. Optionally, type `make check' to run any self-tests that come with
+  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
+  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
+     user, and only the 'make install' phase executed with root
      privileges.
 
-  5. Optionally, type `make installcheck' to repeat any self-tests, but
+  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
+     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
+     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
+  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
+  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.
+     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'
+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:
+   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
 
@@ -113,21 +112,21 @@ 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
+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.
+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
+   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
+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
+"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" \
@@ -136,105 +135,104 @@ this:
 
    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.
+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
+   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
+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
+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
+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
+'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
+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
+'${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.
+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
+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
+   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.
+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
+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
+overridden with 'make V=1'; while running './configure
 --disable-silent-rules' sets the default to verbose, which can be
-overridden with `make V=0'.
+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
+   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.
+   HP-UX 'make' updates targets which have the same time stamps as their
+prerequisites, which makes it generally unusable when shipped generated
+files such as 'configure' are involved.  Use GNU 'make' instead.
 
    On OSF/1 a.k.a. Tru64, some versions of the default C compiler cannot
-parse its `<wchar.h>' header file.  The option `-nodtk' can be used as
-a workaround.  If GNU CC is not installed, it is therefore recommended
-to try
+parse its '<wchar.h>' header file.  The option '-nodtk' can be used as a
+workaround.  If GNU CC is not installed, it is therefore recommended to
+try
 
      ./configure CC="cc"
 
@@ -242,26 +240,26 @@ and if that doesn't work, try
 
      ./configure CC="cc -nodtk"
 
-   On Solaris, don't put `/usr/ucb' early in your `PATH'.  This
+   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'.
+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:
+   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
+   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
+_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:
+'--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
 
@@ -270,101 +268,101 @@ 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
+   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
+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'.
+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.
+   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
+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:
+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
+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:
+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' Invocation
 ======================
 
-   `configure' recognizes the following options to control how it
+   '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'
+'-h'
+     Print a summary of all of the options to 'configure', and exit.
 
-`--help=short'
-`--help=recursive'
+'--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.
+     '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'
+'--version'
+'-V'
+     Print the version of Autoconf used to generate the 'configure'
      script, and exit.
 
-`--cache-file=FILE'
+'--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
+     traditionally 'config.cache'.  FILE defaults to '/dev/null' to
      disable caching.
 
-`--config-cache'
-`-C'
-     Alias for `--cache-file=config.cache'.
+'--config-cache'
+'-C'
+     Alias for '--cache-file=config.cache'.
 
-`--quiet'
-`--silent'
-`-q'
+'--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
+     suppress all normal output, redirect it to '/dev/null' (any error
      messages will still be shown).
 
-`--srcdir=DIR'
+'--srcdir=DIR'
      Look for the package's source code in directory DIR.  Usually
-     `configure' can determine that directory automatically.
+     '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.
+'--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'
+'--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.
+'configure' also accepts some other, not widely useful, options.  Run
+'configure --help' for more details.
index ab0a730..0db5ede 100644 (file)
@@ -1,7 +1,7 @@
-# Makefile.in generated by automake 1.15 from Makefile.am.
+# Makefile.in generated by automake 1.15.1 from Makefile.am.
 # @configure_input@
 
-# Copyright (C) 1994-2014 Free Software Foundation, Inc.
+# Copyright (C) 1994-2017 Free Software Foundation, Inc.
 
 # This Makefile.in is free software; the Free Software Foundation
 # gives unlimited permission to copy and/or distribute it,
@@ -320,8 +320,6 @@ LIBMRUBY_CFLAGS = @LIBMRUBY_CFLAGS@
 LIBMRUBY_LIBS = @LIBMRUBY_LIBS@
 LIBOBJS = @LIBOBJS@
 LIBS = @LIBS@
-LIBSPDYLAY_CFLAGS = @LIBSPDYLAY_CFLAGS@
-LIBSPDYLAY_LIBS = @LIBSPDYLAY_LIBS@
 LIBTOOL = @LIBTOOL@
 LIBXML2_CFLAGS = @LIBXML2_CFLAGS@
 LIBXML2_LIBS = @LIBXML2_LIBS@
@@ -730,7 +728,7 @@ distdir: $(DISTFILES)
          ! -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
+       tardir=$(distdir) && $(am__tar) | eval GZIP= gzip $(GZIP_ENV) -c >$(distdir).tar.gz
        $(am__post_remove_distdir)
 
 dist-bzip2: distdir
@@ -756,7 +754,7 @@ dist-shar: distdir
        @echo WARNING: "Support for shar distribution archives 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
+       shar $(distdir) | eval GZIP= gzip $(GZIP_ENV) -c >$(distdir).shar.gz
        $(am__post_remove_distdir)
 
 dist-zip: distdir
@@ -774,7 +772,7 @@ dist dist-all:
 distcheck: dist
        case '$(DIST_ARCHIVES)' in \
        *.tar.gz*) \
-         GZIP=$(GZIP_ENV) gzip -dc $(distdir).tar.gz | $(am__untar) ;;\
+         eval GZIP= gzip $(GZIP_ENV) -dc $(distdir).tar.gz | $(am__untar) ;;\
        *.tar.bz2*) \
          bzip2 -dc $(distdir).tar.bz2 | $(am__untar) ;;\
        *.tar.lz*) \
@@ -784,7 +782,7 @@ distcheck: dist
        *.tar.Z*) \
          uncompress -c $(distdir).tar.Z | $(am__untar) ;;\
        *.shar.gz*) \
-         GZIP=$(GZIP_ENV) gzip -dc $(distdir).shar.gz | unshar ;;\
+         eval GZIP= gzip $(GZIP_ENV) -dc $(distdir).shar.gz | unshar ;;\
        *.zip*) \
          unzip $(distdir).zip ;;\
        esac
index d514735..4e48058 100644 (file)
@@ -4,10 +4,10 @@ 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.
+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.
 
 An HPACK encoder and decoder are available as a public API.
 
@@ -34,8 +34,8 @@ 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
+  This endpoint supports ``h2``, ``h2-16``, ``h2-14``, and
+  ``http/1.1`` via ALPN/NPN and requires TLSv1.2 for HTTP/2
   connection.
 
 * http://nghttp2.org/ (HTTP Upgrade and HTTP/2 Direct)
@@ -76,18 +76,10 @@ ALPN support requires OpenSSL >= 1.0.2 (released 22 January 2015).
 LibreSSL >= 2.2.0 can be used instead of OpenSSL, but OpenSSL has more
 features than LibreSSL at the time of this writing.
 
-To enable the SPDY protocol in the application program ``nghttpx`` and
-``h2load``, the following package is required:
-
-* spdylay >= 1.3.2
-
-We no longer recommend to build nghttp2 with SPDY protocol support
-enabled.  SPDY support will be removed soon.
-
 To enable ``-a`` option (getting linked assets from the downloaded
 resource) in ``nghttp``, the following package is required:
 
-* libxml2 >= 2.7.7
+* libxml2 >= 2.6.26
 
 To enable systemd support in nghttpx, the following package is
 required:
@@ -130,13 +122,9 @@ and above, run the following to install the required packages:
 
     sudo apt-get install g++ make binutils autoconf automake autotools-dev libtool pkg-config \
       zlib1g-dev libcunit1-dev libssl-dev libxml2-dev libev-dev libevent-dev libjansson-dev \
-      libc-ares-dev libjemalloc-dev libsystemd-dev libspdylay-dev \
+      libc-ares-dev libjemalloc-dev libsystemd-dev \
       cython python3-dev python-setuptools
 
-Since Ubuntu 15.10, spdylay has been available as a package named
-`libspdylay-dev`.  For the earlier Ubuntu release, you need to build
-it yourself: http://tatsuhiro-t.github.io/spdylay/
-
 To enable mruby support for nghttpx, `mruby
 <https://github.com/mruby/mruby>`_ is required.  We need to build
 mruby with C++ ABI explicitly turned on, and probably need other
@@ -157,22 +145,8 @@ minimizes the risk of private key leakage when serious bug like
 Heartbleed is exploited.  The neverbleed is disabled by default.  To
 enable it, use ``--with-neverbleed`` configure option.
 
-Building from git
------------------
-
-Building from git is easy, but please be sure that at least autoconf 2.68 is
-used:
-
-.. code-block:: text
-
-    $ git submodule update --init
-    $ autoreconf -i
-    $ automake
-    $ autoconf
-    $ ./configure
-    $ make
-
-To compile the source code, gcc >= 4.8.3 or clang >= 3.4 is required.
+In order to compile the source code, gcc >= 4.8.3 or clang >= 3.4 is
+required.
 
 .. note::
 
@@ -197,6 +171,62 @@ To compile the source code, gcc >= 4.8.3 or clang >= 3.4 is required.
    applications were not built, then using ``--enable-app`` may find
    that cause, such as the missing dependency.
 
+.. note::
+
+   In order to detect third party libraries, pkg-config is used
+   (however we don't use pkg-config for some libraries (e.g., libev)).
+   By default, pkg-config searches ``*.pc`` file in the standard
+   locations (e.g., /usr/lib/pkgconfig).  If it is necessary to use
+   ``*.pc`` file in the custom location, specify paths to
+   ``PKG_CONFIG_PATH`` environment variable, and pass it to configure
+   script, like so:
+
+   .. code-block:: text
+
+       $ ./configure PKG_CONFIG_PATH=/path/to/pkgconfig
+
+   For pkg-config managed libraries, ``*_CFLAG`` and ``*_LIBS``
+   environment variables are defined (e.g., ``OPENSSL_CFLAGS``,
+   ``OPENSSL_LIBS``).  Specifying non-empty string to these variables
+   completely overrides pkg-config.  In other words, if they are
+   specified, pkg-config is not used for detection, and user is
+   responsible to specify the correct values to these variables.  For
+   complete list of these variables, run ``./configure -h``.
+
+Building nghttp2 from release tar archive
+-----------------------------------------
+
+The nghttp2 project regularly releases tar archives which includes
+nghttp2 source code, and generated build files.  They can be
+downloaded from `Releases
+<https://github.com/nghttp2/nghttp2/releases>`_ page.
+
+Building nghttp2 from git requires autotools development packages.
+Building from tar archives does not require them, and thus it is much
+easier.  The usual build step is as follows:
+
+.. code-block:: text
+
+    $ tar xf nghttp2-X.Y.Z.tar.bz2
+    $ cd nghttp2-X.Y.Z
+    $ ./configure
+    $ make
+
+Building from git
+-----------------
+
+Building from git is easy, but please be sure that at least autoconf 2.68 is
+used:
+
+.. code-block:: text
+
+    $ git submodule update --init
+    $ autoreconf -i
+    $ automake
+    $ autoconf
+    $ ./configure
+    $ make
+
 Notes for building on Windows (MSVC)
 ------------------------------------
 
@@ -243,6 +273,18 @@ 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.
 
+Notes for installation on Linux systems
+--------------------------------------------
+After installing nghttp2 tool suite with ``make install`` one might experience a similar error:
+
+.. code-block:: text
+
+    nghttpx: error while loading shared libraries: libnghttp2.so.14: cannot open shared object file: No such file or directory
+
+This means that the tool is unable to locate the ``libnghttp2.so`` shared library.
+
+To update the shared library cache run ``sudo ldconfig``.
+
 Building the documentation
 --------------------------
 
@@ -278,7 +320,6 @@ its testing framework.  We depend on the following libraries:
 * golang.org/x/net/http2
 * golang.org/x/net/websocket
 * https://github.com/tatsuhiro-t/go-nghttp2
-* https://github.com/tatsuhiro-t/spdy
 
 To download the above packages, after settings ``GOPATH``, run the
 following command under ``integration-tests`` directory:
@@ -296,11 +337,6 @@ To run the tests, run the following command under
 
 Inside the tests, we use port 3009 to run the test subject server.
 
-.. note::
-
-   github.com/tatsuhiro-t/spdy is a copy used to be available at
-   golang.org/x/net/spdy, but it is now gone.
-
 Migration from v0.7.15 or earlier
 ---------------------------------
 
@@ -701,7 +737,7 @@ information.  Here is sample output from ``nghttpd``:
 nghttpx - proxy
 +++++++++++++++
 
-``nghttpx`` is a multi-threaded reverse proxy for HTTP/2, SPDY and
+``nghttpx`` is a multi-threaded reverse proxy for HTTP/2, and
 HTTP/1.1, and powers http://nghttp2.org and supports HTTP/2 server
 push.
 
@@ -716,31 +752,30 @@ to know how to migrate from earlier releases.
 ``nghttpx`` implements `important performance-oriented features
 <https://istlsfastyet.com/#server-performance>`_ 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`` also offers the functionality to share session
-cache and ticket keys among multiple ``nghttpx`` instances via
-memcached.
+stapling, dynamic record sizing, ALPN/NPN, forward secrecy and HTTP/2.
+``nghttpx`` also offers the functionality to share session cache and
+ticket keys among multiple ``nghttpx`` instances via memcached.
 
 ``nghttpx`` has 2 operation modes:
 
-================== ====================== ================ =============
-Mode option        Frontend               Backend          Note
-================== ====================== ================ =============
-default mode       HTTP/2, SPDY, HTTP/1.1 HTTP/1.1, HTTP/2 Reverse proxy
-``--http2-proxy``  HTTP/2, SPDY, HTTP/1.1 HTTP/1.1, HTTP/2 Forward proxy
-================== ====================== ================ =============
+================== ================ ================ =============
+Mode option        Frontend         Backend          Note
+================== ================ ================ =============
+default mode       HTTP/2, HTTP/1.1 HTTP/1.1, HTTP/2 Reverse proxy
+``--http2-proxy``  HTTP/2, HTTP/1.1 HTTP/1.1, HTTP/2 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
+a reverse proxy and listens for HTTP/2, and HTTP/1.1 and can be
 deployed as a SSL/TLS terminator for existing web server.
 
 In all modes, the frontend connections are encrypted by SSL/TLS by
 default.  To disable encryption, use the ``no-tls`` keyword in
-``--frontend`` option.  If encryption is disabled, SPDY is disabled in
-the frontend and incoming HTTP/1.1 connections can be upgraded to
-HTTP/2 through HTTP Upgrade.  On the other hard, backend connections
-are not encrypted by default.  To encrypt backend connections, use
-``tls`` keyword in ``--backend`` option.
+``--frontend`` option.  If encryption is disabled, incoming HTTP/1.1
+connections can be upgraded to HTTP/2 through HTTP Upgrade.  On the
+other hard, backend connections are not encrypted by default.  To
+encrypt backend connections, use ``tls`` keyword in ``--backend``
+option.
 
 ``nghttpx`` supports a configuration file.  See the ``--conf`` option and
 sample configuration file ``nghttpx.conf.sample``.
@@ -750,16 +785,16 @@ server:
 
 .. code-block:: text
 
-    Client <-- (HTTP/2, SPDY, HTTP/1.1) --> nghttpx <-- (HTTP/1.1, HTTP/2) --> Web Server
-                                          [reverse proxy]
+    Client <-- (HTTP/2, HTTP/1.1) --> nghttpx <-- (HTTP/1.1, HTTP/2) --> Web Server
+                                    [reverse proxy]
 
 With the ``--http2-proxy`` option, it works as forward proxy, and it
-is so called secure HTTP/2 proxy (aka SPDY proxy):
+is so called secure HTTP/2 proxy:
 
 .. code-block:: text
 
-    Client <-- (HTTP/2, SPDY, HTTP/1.1) --> nghttpx <-- (HTTP/1.1) --> Proxy
-                                           [secure proxy]          (e.g., Squid, ATS)
+    Client <-- (HTTP/2, 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.
@@ -791,7 +826,7 @@ proxy through an HTTP proxy:
 
 .. code-block:: text
 
-    Client <-- (HTTP/2, SPDY, HTTP/1.1) --> nghttpx <-- (HTTP/2) --
+    Client <-- (HTTP/2, HTTP/1.1) --> nghttpx <-- (HTTP/2) --
 
             --===================---> HTTP/2 Proxy
               (HTTP proxy tunnel)     (e.g., nghttpx -s)
@@ -799,9 +834,8 @@ proxy through an HTTP proxy:
 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``
+The ``h2load`` program is a benchmarking tool for HTTP/2.  The UI of
+``h2load`` is heavily inspired by ``weighttp``
 (https://github.com/lighttpd/weighttp).  The typical usage is as
 follows:
 
index 8ca7067..decfb76 100644 (file)
@@ -1,6 +1,6 @@
-# generated automatically by aclocal 1.15 -*- Autoconf -*-
+# generated automatically by aclocal 1.15.1 -*- Autoconf -*-
 
-# Copyright (C) 1996-2014 Free Software Foundation, Inc.
+# Copyright (C) 1996-2017 Free Software Foundation, Inc.
 
 # This file is free software; the Free Software Foundation
 # gives unlimited permission to copy and/or distribute it,
@@ -296,7 +296,7 @@ AS_VAR_COPY([$1], [pkg_cv_][$1])
 AS_VAR_IF([$1], [""], [$5], [$4])dnl
 ])dnl PKG_CHECK_VAR
 
-# Copyright (C) 2002-2014 Free Software Foundation, Inc.
+# Copyright (C) 2002-2017 Free Software Foundation, Inc.
 #
 # This file is free software; the Free Software Foundation
 # gives unlimited permission to copy and/or distribute it,
@@ -311,7 +311,7 @@ AC_DEFUN([AM_AUTOMAKE_VERSION],
 [am__api_version='1.15'
 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.15], [],
+m4_if([$1], [1.15.1], [],
       [AC_FATAL([Do not call $0, use AM_INIT_AUTOMAKE([$1]).])])dnl
 ])
 
@@ -327,14 +327,14 @@ m4_define([_AM_AUTOCONF_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.15])dnl
+[AM_AUTOMAKE_VERSION([1.15.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-2014 Free Software Foundation, Inc.
+# Copyright (C) 2001-2017 Free Software Foundation, Inc.
 #
 # This file is free software; the Free Software Foundation
 # gives unlimited permission to copy and/or distribute it,
@@ -386,7 +386,7 @@ am_aux_dir=`cd "$ac_aux_dir" && pwd`
 
 # AM_CONDITIONAL                                            -*- Autoconf -*-
 
-# Copyright (C) 1997-2014 Free Software Foundation, Inc.
+# Copyright (C) 1997-2017 Free Software Foundation, Inc.
 #
 # This file is free software; the Free Software Foundation
 # gives unlimited permission to copy and/or distribute it,
@@ -417,7 +417,7 @@ AC_CONFIG_COMMANDS_PRE(
 Usually this means the macro was only invoked conditionally.]])
 fi])])
 
-# Copyright (C) 1999-2014 Free Software Foundation, Inc.
+# Copyright (C) 1999-2017 Free Software Foundation, Inc.
 #
 # This file is free software; the Free Software Foundation
 # gives unlimited permission to copy and/or distribute it,
@@ -608,7 +608,7 @@ _AM_SUBST_NOTMAKE([am__nodep])dnl
 
 # Generate code to set up dependency tracking.              -*- Autoconf -*-
 
-# Copyright (C) 1999-2014 Free Software Foundation, Inc.
+# Copyright (C) 1999-2017 Free Software Foundation, Inc.
 #
 # This file is free software; the Free Software Foundation
 # gives unlimited permission to copy and/or distribute it,
@@ -684,7 +684,7 @@ AC_DEFUN([AM_OUTPUT_DEPENDENCY_COMMANDS],
 
 # Do all the work for Automake.                             -*- Autoconf -*-
 
-# Copyright (C) 1996-2014 Free Software Foundation, Inc.
+# Copyright (C) 1996-2017 Free Software Foundation, Inc.
 #
 # This file is free software; the Free Software Foundation
 # gives unlimited permission to copy and/or distribute it,
@@ -881,7 +881,7 @@ for _am_header in $config_headers :; do
 done
 echo "timestamp for $_am_arg" >`AS_DIRNAME(["$_am_arg"])`/stamp-h[]$_am_stamp_count])
 
-# Copyright (C) 2001-2014 Free Software Foundation, Inc.
+# Copyright (C) 2001-2017 Free Software Foundation, Inc.
 #
 # This file is free software; the Free Software Foundation
 # gives unlimited permission to copy and/or distribute it,
@@ -902,7 +902,7 @@ if test x"${install_sh+set}" != xset; then
 fi
 AC_SUBST([install_sh])])
 
-# Copyright (C) 2003-2014 Free Software Foundation, Inc.
+# Copyright (C) 2003-2017 Free Software Foundation, Inc.
 #
 # This file is free software; the Free Software Foundation
 # gives unlimited permission to copy and/or distribute it,
@@ -923,7 +923,7 @@ AC_SUBST([am__leading_dot])])
 
 # Check to see how 'make' treats includes.                 -*- Autoconf -*-
 
-# Copyright (C) 2001-2014 Free Software Foundation, Inc.
+# Copyright (C) 2001-2017 Free Software Foundation, Inc.
 #
 # This file is free software; the Free Software Foundation
 # gives unlimited permission to copy and/or distribute it,
@@ -973,7 +973,7 @@ rm -f confinc confmf
 
 # Fake the existence of programs that GNU maintainers use.  -*- Autoconf -*-
 
-# Copyright (C) 1997-2014 Free Software Foundation, Inc.
+# Copyright (C) 1997-2017 Free Software Foundation, Inc.
 #
 # This file is free software; the Free Software Foundation
 # gives unlimited permission to copy and/or distribute it,
@@ -1012,7 +1012,7 @@ fi
 
 # Helper functions for option handling.                     -*- Autoconf -*-
 
-# Copyright (C) 2001-2014 Free Software Foundation, Inc.
+# Copyright (C) 2001-2017 Free Software Foundation, Inc.
 #
 # This file is free software; the Free Software Foundation
 # gives unlimited permission to copy and/or distribute it,
@@ -1041,7 +1041,7 @@ AC_DEFUN([_AM_SET_OPTIONS],
 AC_DEFUN([_AM_IF_OPTION],
 [m4_ifset(_AM_MANGLE_OPTION([$1]), [$2], [$3])])
 
-# Copyright (C) 1999-2014 Free Software Foundation, Inc.
+# Copyright (C) 1999-2017 Free Software Foundation, Inc.
 #
 # This file is free software; the Free Software Foundation
 # gives unlimited permission to copy and/or distribute it,
@@ -1088,7 +1088,7 @@ AC_LANG_POP([C])])
 # For backward compatibility.
 AC_DEFUN_ONCE([AM_PROG_CC_C_O], [AC_REQUIRE([AC_PROG_CC])])
 
-# Copyright (C) 1999-2014 Free Software Foundation, Inc.
+# Copyright (C) 1999-2017 Free Software Foundation, Inc.
 #
 # This file is free software; the Free Software Foundation
 # gives unlimited permission to copy and/or distribute it,
@@ -1121,8 +1121,9 @@ 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).
+  dnl FIXME: Remove the need to hard-code Python versions here.
   m4_define_default([_AM_PYTHON_INTERPRETER_LIST],
-[python python2 python3 python3.3 python3.2 python3.1 python3.0 python2.7 dnl
+[python python2 python3 python3.8 python3.7 python3.6 python3.5 python3.4 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])
@@ -1323,7 +1324,7 @@ 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-2014 Free Software Foundation, Inc.
+# Copyright (C) 2001-2017 Free Software Foundation, Inc.
 #
 # This file is free software; the Free Software Foundation
 # gives unlimited permission to copy and/or distribute it,
@@ -1342,7 +1343,7 @@ AC_DEFUN([AM_RUN_LOG],
 
 # Check to make sure that the build environment is sane.    -*- Autoconf -*-
 
-# Copyright (C) 1996-2014 Free Software Foundation, Inc.
+# Copyright (C) 1996-2017 Free Software Foundation, Inc.
 #
 # This file is free software; the Free Software Foundation
 # gives unlimited permission to copy and/or distribute it,
@@ -1423,7 +1424,7 @@ AC_CONFIG_COMMANDS_PRE(
 rm -f conftest.file
 ])
 
-# Copyright (C) 2009-2014 Free Software Foundation, Inc.
+# Copyright (C) 2009-2017 Free Software Foundation, Inc.
 #
 # This file is free software; the Free Software Foundation
 # gives unlimited permission to copy and/or distribute it,
@@ -1483,7 +1484,7 @@ AC_SUBST([AM_BACKSLASH])dnl
 _AM_SUBST_NOTMAKE([AM_BACKSLASH])dnl
 ])
 
-# Copyright (C) 2001-2014 Free Software Foundation, Inc.
+# Copyright (C) 2001-2017 Free Software Foundation, Inc.
 #
 # This file is free software; the Free Software Foundation
 # gives unlimited permission to copy and/or distribute it,
@@ -1511,7 +1512,7 @@ fi
 INSTALL_STRIP_PROGRAM="\$(install_sh) -c -s"
 AC_SUBST([INSTALL_STRIP_PROGRAM])])
 
-# Copyright (C) 2006-2014 Free Software Foundation, Inc.
+# Copyright (C) 2006-2017 Free Software Foundation, Inc.
 #
 # This file is free software; the Free Software Foundation
 # gives unlimited permission to copy and/or distribute it,
@@ -1530,7 +1531,7 @@ AC_DEFUN([AM_SUBST_NOTMAKE], [_AM_SUBST_NOTMAKE($@)])
 
 # Check how to create a tarball.                            -*- Autoconf -*-
 
-# Copyright (C) 2004-2014 Free Software Foundation, Inc.
+# Copyright (C) 2004-2017 Free Software Foundation, Inc.
 #
 # This file is free software; the Free Software Foundation
 # gives unlimited permission to copy and/or distribute it,
index fb798f4..d67b540 100644 (file)
@@ -1,7 +1,3 @@
-
-/* Hint to the compiler that a function parameter is not used  */
-#define _U_ @HINT_UNUSED_PARAM@
-
 /* Hint to the compiler that a function never returns */
 #define NGHTTP2_NORETURN @HINT_NORETURN@
 
@@ -38,6 +34,9 @@
 /* Define to 1 if you have the `accept4` function. */
 #cmakedefine HAVE_ACCEPT4 1
 
+/* Define to 1 if you have the `mkostemp` function. */
+#cmakedefine HAVE_MKOSTEMP 1
+
 /* Define to 1 if you have the `initgroups` function. */
 #cmakedefine01 HAVE_DECL_INITGROUPS
 
index 2e9ad7f..31e01ef 100755 (executable)
@@ -1,8 +1,8 @@
 #! /bin/sh
 # Attempt to guess a canonical system name.
-#   Copyright 1992-2016 Free Software Foundation, Inc.
+#   Copyright 1992-2017 Free Software Foundation, Inc.
 
-timestamp='2016-10-02'
+timestamp='2017-11-07'
 
 # 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
@@ -15,7 +15,7 @@ timestamp='2016-10-02'
 # General Public License for more details.
 #
 # You should have received a copy of the GNU General Public License
-# along with this program; if not, see <http://www.gnu.org/licenses/>.
+# along with this program; if not, see <https://www.gnu.org/licenses/>.
 #
 # As a special exception to the GNU General Public License, if you
 # distribute this file as part of a program that contains a
@@ -27,7 +27,7 @@ timestamp='2016-10-02'
 # Originally written by Per Bothner; maintained since 2000 by Ben Elliston.
 #
 # 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
+# https://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.guess
 #
 # Please send patches to <config-patches@gnu.org>.
 
@@ -39,7 +39,7 @@ Usage: $0 [OPTION]
 
 Output the configuration name of the system \`$me' is run on.
 
-Operation modes:
+Options:
   -h, --help         print this help, then exit
   -t, --time-stamp   print date of last modification, then exit
   -v, --version      print version number, then exit
@@ -50,7 +50,7 @@ version="\
 GNU config.guess ($timestamp)
 
 Originally written by Per Bothner.
-Copyright 1992-2016 Free Software Foundation, Inc.
+Copyright 1992-2017 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."
@@ -244,6 +244,9 @@ case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in
        UNAME_MACHINE_ARCH=`arch | sed 's/^.*BSD\.//'`
        echo ${UNAME_MACHINE_ARCH}-unknown-libertybsd${UNAME_RELEASE}
        exit ;;
+    *:MidnightBSD:*:*)
+       echo ${UNAME_MACHINE}-unknown-midnightbsd${UNAME_RELEASE}
+       exit ;;
     *:ekkoBSD:*:*)
        echo ${UNAME_MACHINE}-unknown-ekkobsd${UNAME_RELEASE}
        exit ;;
@@ -259,6 +262,9 @@ case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in
     *:Sortix:*:*)
        echo ${UNAME_MACHINE}-unknown-sortix
        exit ;;
+    *:Redox:*:*)
+       echo ${UNAME_MACHINE}-unknown-redox
+       exit ;;
     alpha:OSF1:*:*)
        case $UNAME_RELEASE in
        *4.0)
@@ -315,15 +321,6 @@ case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in
        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 ;;
@@ -485,13 +482,13 @@ case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in
 #endif
        #if defined (host_mips) && defined (MIPSEB)
        #if defined (SYSTYPE_SYSV)
-         printf ("mips-mips-riscos%ssysv\n", argv[1]); exit (0);
+         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);
+         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);
+         printf ("mips-mips-riscos%sbsd\\n", argv[1]); exit (0);
        #endif
        #endif
          exit (-1);
@@ -614,7 +611,7 @@ EOF
     *:AIX:*:*)
        echo rs6000-ibm-aix
        exit ;;
-    ibmrt:4.4BSD:*|romp-ibm:BSD:*)
+    ibmrt:4.4BSD:*|romp-ibm:4.4BSD:*)
        echo romp-ibm-bsd4.4
        exit ;;
     ibmrt:*BSD:*|romp-ibm:BSD:*)            # covers RT/PC BSD and
@@ -635,8 +632,8 @@ EOF
     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/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`
@@ -749,7 +746,7 @@ EOF
                { echo "$SYSTEM_NAME"; exit; }
        echo unknown-hitachi-hiuxwe2
        exit ;;
-    9000/7??:4.3bsd:*:* | 9000/8?[79]:4.3bsd:*:* )
+    9000/7??:4.3bsd:*:* | 9000/8?[79]:4.3bsd:*:*)
        echo hppa1.1-hp-bsd
        exit ;;
     9000/8??:4.3bsd:*:*)
@@ -758,7 +755,7 @@ EOF
     *9??*:MPE/iX:*:* | *3000*:MPE/iX:*:*)
        echo hppa1.0-hp-mpeix
        exit ;;
-    hp7??:OSF1:*:* | hp8?[79]:OSF1:*:* )
+    hp7??:OSF1:*:* | hp8?[79]:OSF1:*:*)
        echo hppa1.1-hp-osf
        exit ;;
     hp8??:OSF1:*:*)
@@ -837,10 +834,11 @@ EOF
        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/[-(].*//'` ;;
+               UNAME_PROCESSOR=x86_64 ;;
+           i386)
+               UNAME_PROCESSOR=i586 ;;
        esac
+       echo ${UNAME_PROCESSOR}-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'`
        exit ;;
     i*:CYGWIN*:*)
        echo ${UNAME_MACHINE}-pc-cygwin
@@ -854,10 +852,6 @@ EOF
     *: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 ;;
@@ -873,27 +867,12 @@ EOF
                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 ;;
@@ -1096,7 +1075,7 @@ EOF
     i*86:*DOS:*:*)
        echo ${UNAME_MACHINE}-pc-msdosdjgpp
        exit ;;
-    i*86:*:4.*:* | i*86:SYSTEM_V:4.*:*)
+    i*86:*: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}
@@ -1303,14 +1282,21 @@ EOF
        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
+                      (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
+               # On 10.4-10.6 one might compile for PowerPC via gcc -arch ppc
+               if (echo '#ifdef __POWERPC__'; echo IS_PPC; echo '#endif') | \
+                      (CCOPTS="" $CC_FOR_BUILD -E - 2>/dev/null) | \
+                      grep IS_PPC >/dev/null
+               then
+                   UNAME_PROCESSOR=powerpc
+               fi
            fi
        elif test "$UNAME_PROCESSOR" = i386 ; then
            # Avoid executing cc on OS X 10.9, as it ships with a stub
@@ -1334,15 +1320,18 @@ EOF
     *:QNX:*:4*)
        echo i386-pc-qnx
        exit ;;
-    NEO-?:NONSTOP_KERNEL:*:*)
+    NEO-*:NONSTOP_KERNEL:*:*)
        echo neo-tandem-nsk${UNAME_RELEASE}
        exit ;;
     NSE-*:NONSTOP_KERNEL:*:*)
        echo nse-tandem-nsk${UNAME_RELEASE}
        exit ;;
-    NSR-?:NONSTOP_KERNEL:*:*)
+    NSR-*:NONSTOP_KERNEL:*:*)
        echo nsr-tandem-nsk${UNAME_RELEASE}
        exit ;;
+    NSX-*:NONSTOP_KERNEL:*:*)
+       echo nsx-tandem-nsk${UNAME_RELEASE}
+       exit ;;
     *:NonStop-UX:*:*)
        echo mips-compaq-nonstopux
        exit ;;
@@ -1414,16 +1403,28 @@ EOF
        exit ;;
 esac
 
+echo "$0: unable to guess system type" >&2
+
+case "${UNAME_MACHINE}:${UNAME_SYSTEM}" in
+    mips:Linux | mips64:Linux)
+       # If we got here on MIPS GNU/Linux, output extra information.
+       cat >&2 <<EOF
+
+NOTE: MIPS GNU/Linux systems require a C compiler to fully recognize
+the system type. Please install a C compiler and try again.
+EOF
+       ;;
+esac
+
 cat >&2 <<EOF
-$0: unable to guess system type
 
 This script (version $timestamp), has failed to recognize the
-operating system you are using. If your script is old, overwrite
-config.guess and config.sub with the latest versions from:
+operating system you are using. If your script is old, overwrite *all*
+copies of config.guess and config.sub with the latest versions from:
 
-  http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.guess
+  https://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.guess
 and
-  http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.sub
+  https://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.sub
 
 If $0 has already been updated, send the following data and any
 information you think might be pertinent to config-patches@gnu.org to
@@ -1455,7 +1456,7 @@ EOF
 exit 1
 
 # Local variables:
-# eval: (add-hook 'write-file-hooks 'time-stamp)
+# eval: (add-hook 'write-file-functions 'time-stamp)
 # time-stamp-start: "timestamp='"
 # time-stamp-format: "%:y-%02m-%02d"
 # time-stamp-end: "'"
index 242f38a..43fa5b1 100644 (file)
@@ -89,6 +89,9 @@
 /* Define to 1 if you have the `memset' function. */
 #undef HAVE_MEMSET
 
+/* Define to 1 if you have the `mkostemp' function. */
+#undef HAVE_MKOSTEMP
+
 /* Define to 1 if you have `mruby` library. */
 #undef HAVE_MRUBY
 
 /* 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 below would cause a syntax error. */
 #undef _UINT8_T
 
-/* Hint to the compiler that a function parameter is not used */
-#undef _U_
-
 /* Define to `int' if <sys/types.h> doesn't define. */
 #undef gid_t
 
index dd2ca93..00f68b8 100755 (executable)
@@ -1,8 +1,8 @@
 #! /bin/sh
 # Configuration validation subroutine script.
-#   Copyright 1992-2016 Free Software Foundation, Inc.
+#   Copyright 1992-2017 Free Software Foundation, Inc.
 
-timestamp='2016-11-04'
+timestamp='2017-11-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
@@ -15,7 +15,7 @@ timestamp='2016-11-04'
 # General Public License for more details.
 #
 # You should have received a copy of the GNU General Public License
-# along with this program; if not, see <http://www.gnu.org/licenses/>.
+# along with this program; if not, see <https://www.gnu.org/licenses/>.
 #
 # As a special exception to the GNU General Public License, if you
 # distribute this file as part of a program that contains a
@@ -33,7 +33,7 @@ timestamp='2016-11-04'
 # 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
+# https://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.sub
 
 # This file is supposed to be the same for all GNU packages
 # and recognize all the CPU types, system types and aliases
@@ -57,7 +57,7 @@ Usage: $0 [OPTION] CPU-MFR-OPSYS or ALIAS
 
 Canonicalize a configuration name.
 
-Operation modes:
+Options:
   -h, --help         print this help, then exit
   -t, --time-stamp   print date of last modification, then exit
   -v, --version      print version number, then exit
@@ -67,7 +67,7 @@ Report bugs and patches to <config-patches@gnu.org>."
 version="\
 GNU config.sub ($timestamp)
 
-Copyright 1992-2016 Free Software Foundation, Inc.
+Copyright 1992-2017 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."
@@ -229,9 +229,6 @@ case $os in
        -ptx*)
                basic_machine=`echo $1 | sed -e 's/86-.*/86-sequent/'`
                ;;
-       -windowsnt*)
-               os=`echo $os | sed -e 's/windowsnt/winnt/'`
-               ;;
        -psos*)
                os=-psos
                ;;
@@ -263,7 +260,7 @@ case $basic_machine in
        | fido | fr30 | frv | ft32 \
        | h8300 | h8500 | hppa | hppa1.[01] | hppa2.0 | hppa2.0[nw] | hppa64 \
        | hexagon \
-       | i370 | i860 | i960 | ia64 \
+       | i370 | i860 | i960 | ia16 | ia64 \
        | ip2k | iq2000 \
        | k1om \
        | le32 | le64 \
@@ -315,7 +312,7 @@ case $basic_machine in
        | ubicom32 \
        | v850 | v850e | v850e1 | v850e2 | v850es | v850e2v3 \
        | visium \
-       | we32k \
+       | wasm32 \
        | x86 | xc16x | xstormy16 | xtensa \
        | z8k | z80)
                basic_machine=$basic_machine-unknown
@@ -388,7 +385,7 @@ case $basic_machine in
        | h8300-* | h8500-* \
        | hppa-* | hppa1.[01]-* | hppa2.0-* | hppa2.0[nw]-* | hppa64-* \
        | hexagon-* \
-       | i*86-* | i860-* | i960-* | ia64-* \
+       | i*86-* | i860-* | i960-* | ia16-* | ia64-* \
        | ip2k-* | iq2000-* \
        | k1om-* \
        | le32-* | le64-* \
@@ -446,6 +443,7 @@ case $basic_machine in
        | v850-* | v850e-* | v850e1-* | v850es-* | v850e2-* | v850e2v3-* \
        | vax-* \
        | visium-* \
+       | wasm32-* \
        | we32k-* \
        | x86-* | x86_64-* | xc16x-* | xps100-* \
        | xstormy16-* | xtensa*-* \
@@ -641,7 +639,7 @@ case $basic_machine in
                basic_machine=rs6000-bull
                os=-bosx
                ;;
-       dpx2* | dpx2*-bull)
+       dpx2*)
                basic_machine=m68k-bull
                os=-sysv3
                ;;
@@ -903,7 +901,7 @@ case $basic_machine in
                basic_machine=v70-nec
                os=-sysv
                ;;
-       next | m*-next )
+       next | m*-next)
                basic_machine=m68k-next
                case $os in
                    -nextstep* )
@@ -948,6 +946,9 @@ case $basic_machine in
        nsr-tandem)
                basic_machine=nsr-tandem
                ;;
+       nsx-tandem)
+               basic_machine=nsx-tandem
+               ;;
        op50n-* | op60c-*)
                basic_machine=hppa1.1-oki
                os=-proelf
@@ -1243,6 +1244,9 @@ case $basic_machine in
                basic_machine=a29k-wrs
                os=-vxworks
                ;;
+       wasm32)
+               basic_machine=wasm32-unknown
+               ;;
        w65*)
                basic_machine=w65-wdc
                os=-none
@@ -1251,6 +1255,9 @@ case $basic_machine in
                basic_machine=hppa1.1-winbond
                os=-proelf
                ;;
+       x64)
+               basic_machine=x86_64-pc
+               ;;
        xbox)
                basic_machine=i686-pc
                os=-mingw32
@@ -1358,8 +1365,8 @@ esac
 if [ x"$os" != x"" ]
 then
 case $os in
-       # First match some system type aliases
-       # that might get confused with valid system types.
+       # 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
@@ -1379,9 +1386,9 @@ case $os in
        -gnu/linux*)
                os=`echo $os | sed -e 's|gnu/linux|linux-gnu|'`
                ;;
-       # First accept the basic system types.
+       # Now accept the basic system types.
        # The portable systems comes first.
-       # Each alternative MUST END IN A *, to match a version number.
+       # 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]*\
@@ -1397,7 +1404,7 @@ case $os in
              | -bosx* | -nextstep* | -cxux* | -aout* | -elf* | -oabi* \
              | -ptx* | -coff* | -ecoff* | -winnt* | -domain* | -vsta* \
              | -udi* | -eabi* | -lites* | -ieee* | -go32* | -aux* \
-             | -chorusos* | -chorusrdb* | -cegcc* \
+             | -chorusos* | -chorusrdb* | -cegcc* | -glidix* \
              | -cygwin* | -msys* | -pe* | -psos* | -moss* | -proelf* | -rtems* \
              | -midipix* | -mingw32* | -mingw64* | -linux-gnu* | -linux-android* \
              | -linux-newlib* | -linux-musl* | -linux-uclibc* \
@@ -1409,7 +1416,7 @@ case $os in
              | -morphos* | -superux* | -rtmk* | -rtmk-nova* | -windiss* \
              | -powermax* | -dnix* | -nx6 | -nx7 | -sei* | -dragonfly* \
              | -skyos* | -haiku* | -rdos* | -toppers* | -drops* | -es* \
-             | -onefs* | -tirtos* | -phoenix* | -fuchsia*)
+             | -onefs* | -tirtos* | -phoenix* | -fuchsia* | -redox*)
        # Remember, each alternative MUST END IN *, to match a version number.
                ;;
        -qnx*)
@@ -1484,7 +1491,7 @@ case $os in
        -nova*)
                os=-rtmk-nova
                ;;
-       -ns2 )
+       -ns2)
                os=-nextstep2
                ;;
        -nsk*)
@@ -1539,6 +1546,19 @@ case $os in
        -dicos*)
                os=-dicos
                ;;
+       -pikeos*)
+               # Until real need of OS specific support for
+               # particular features comes up, bare metal
+               # configurations are quite functional.
+               case $basic_machine in
+                   arm*)
+                       os=-eabi
+                       ;;
+                   *)
+                       os=-elf
+                       ;;
+               esac
+               ;;
        -nacl*)
                ;;
        -ios)
@@ -1638,6 +1658,9 @@ case $basic_machine in
        sparc-* | *-sun)
                os=-sunos4.1.1
                ;;
+       pru-*)
+               os=-elf
+               ;;
        *-be)
                os=-beos
                ;;
@@ -1683,7 +1706,7 @@ case $basic_machine in
        m88k-omron*)
                os=-luna
                ;;
-       *-next )
+       *-next)
                os=-nextstep
                ;;
        *-sequent)
@@ -1818,7 +1841,7 @@ echo $basic_machine$os
 exit
 
 # Local variables:
-# eval: (add-hook 'write-file-hooks 'time-stamp)
+# eval: (add-hook 'write-file-functions 'time-stamp)
 # time-stamp-start: "timestamp='"
 # time-stamp-format: "%:y-%02m-%02d"
 # time-stamp-end: "'"
index aac5366..1a3ce29 100755 (executable)
--- a/configure
+++ b/configure
@@ -1,6 +1,6 @@
 #! /bin/sh
 # Guess values for system-dependent variables and create Makefiles.
-# Generated by GNU Autoconf 2.69 for nghttp2 1.20.0.
+# Generated by GNU Autoconf 2.69 for nghttp2 1.31.1.
 #
 # Report bugs to <t-tujikawa@users.sourceforge.net>.
 #
@@ -590,8 +590,8 @@ MAKEFLAGS=
 # Identity of this package.
 PACKAGE_NAME='nghttp2'
 PACKAGE_TARNAME='nghttp2'
-PACKAGE_VERSION='1.20.0'
-PACKAGE_STRING='nghttp2 1.20.0'
+PACKAGE_VERSION='1.31.1'
+PACKAGE_STRING='nghttp2 1.31.1'
 PACKAGE_BUGREPORT='t-tujikawa@users.sourceforge.net'
 PACKAGE_URL=''
 
@@ -669,10 +669,6 @@ 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
@@ -870,7 +866,6 @@ enable_failmalloc
 enable_lib_only
 with_libxml2
 with_jemalloc
-with_spdylay
 with_systemd
 with_mruby
 with_neverbleed
@@ -918,9 +913,7 @@ JANSSON_LIBS
 SYSTEMD_CFLAGS
 SYSTEMD_LIBS
 LIBXML2_CFLAGS
-LIBXML2_LIBS
-LIBSPDYLAY_CFLAGS
-LIBSPDYLAY_LIBS'
+LIBXML2_LIBS'
 
 
 # Initialize some variables set by options.
@@ -1471,7 +1464,7 @@ 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.20.0 to adapt to many kinds of systems.
+\`configure' configures nghttp2 1.31.1 to adapt to many kinds of systems.
 
 Usage: $0 [OPTION]... [VAR=VALUE]...
 
@@ -1543,7 +1536,7 @@ fi
 
 if test -n "$ac_init_help"; then
   case $ac_init_help in
-     short | recursive ) echo "Configuration of nghttp2 1.20.0:";;
+     short | recursive ) echo "Configuration of nghttp2 1.31.1:";;
    esac
   cat <<\_ACEOF
 
@@ -1592,7 +1585,6 @@ Optional Packages:
                           compiler's sysroot if not specified).
   --with-libxml2          Use libxml2 [default=check]
   --with-jemalloc         Use jemalloc [default=check]
-  --with-spdylay          Use spdylay [default=no]
   --with-systemd          Enable systemd support in nghttpx [default=check]
   --with-mruby            Use mruby [default=no]
   --with-neverbleed       Use neverbleed [default=no]
@@ -1673,10 +1665,6 @@ Some influential environment variables:
               C compiler flags for LIBXML2, overriding pkg-config
   LIBXML2_LIBS
               linker flags for LIBXML2, 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.
@@ -1744,7 +1732,7 @@ fi
 test -n "$ac_init_help" && exit $ac_status
 if $ac_init_version; then
   cat <<\_ACEOF
-nghttp2 configure 1.20.0
+nghttp2 configure 1.31.1
 generated by GNU Autoconf 2.69
 
 Copyright (C) 2012 Free Software Foundation, Inc.
@@ -2704,7 +2692,7 @@ 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.20.0, which was
+It was created by nghttp2 $as_me 1.31.1, which was
 generated by GNU Autoconf 2.69.  Invocation command line was
 
   $ $0 $@
@@ -12672,7 +12660,7 @@ fi
 
 # Define the identity of the package.
  PACKAGE='nghttp2'
- VERSION='1.20.0'
+ VERSION='1.31.1'
 
 
 cat >>confdefs.h <<_ACEOF
 AM_BACKSLASH='\'
 
 
-LT_CURRENT=27
+LT_CURRENT=30
 
-LT_REVISION=0
+LT_REVISION=1
 
-LT_AGE=13
+LT_AGE=16
 
 
 major=`echo $PACKAGE_VERSION |cut -d. -f1 | sed -e "s/^0-9//g"`
 
 
 
-# Check whether --with-spdylay was given.
-if test "${with_spdylay+set}" = set; then :
-  withval=$with_spdylay; request_spdylay=$withval
-else
-  request_spdylay=no
-fi
-
-
-
 # Check whether --with-systemd was given.
 if test "${with_systemd+set}" = set; then :
   withval=$with_systemd; request_systemd=$withval
@@ -17749,7 +17728,7 @@ 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
+       for am_cv_pathless_PYTHON in python python2 python3 python3.8 python3.7 python3.6 python3.5 python3.4 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
@@ -18433,26 +18412,12 @@ else
 
 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
-
-
 $as_echo "#define NGHTTP2_NORETURN __attribute__((noreturn))" >>confdefs.h
 
 else
 
-$as_echo "#define _U_ /**/" >>confdefs.h
-
-
 $as_echo "#define NGHTTP2_NORETURN /**/" >>confdefs.h
 
 fi
@@ -19533,12 +19498,12 @@ if test -n "$LIBXML2_CFLAGS"; then
     pkg_cv_LIBXML2_CFLAGS="$LIBXML2_CFLAGS"
  elif test -n "$PKG_CONFIG"; then
     if test -n "$PKG_CONFIG" && \
-    { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libxml-2.0 >= 2.7.7\""; } >&5
-  ($PKG_CONFIG --exists --print-errors "libxml-2.0 >= 2.7.7") 2>&5
+    { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libxml-2.0 >= 2.6.26\""; } >&5
+  ($PKG_CONFIG --exists --print-errors "libxml-2.0 >= 2.6.26") 2>&5
   ac_status=$?
   $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
   test $ac_status = 0; }; then
-  pkg_cv_LIBXML2_CFLAGS=`$PKG_CONFIG --cflags "libxml-2.0 >= 2.7.7" 2>/dev/null`
+  pkg_cv_LIBXML2_CFLAGS=`$PKG_CONFIG --cflags "libxml-2.0 >= 2.6.26" 2>/dev/null`
                      test "x$?" != "x0" && pkg_failed=yes
 else
   pkg_failed=yes
@@ -19550,12 +19515,12 @@ if test -n "$LIBXML2_LIBS"; then
     pkg_cv_LIBXML2_LIBS="$LIBXML2_LIBS"
  elif test -n "$PKG_CONFIG"; then
     if test -n "$PKG_CONFIG" && \
-    { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libxml-2.0 >= 2.7.7\""; } >&5
-  ($PKG_CONFIG --exists --print-errors "libxml-2.0 >= 2.7.7") 2>&5
+    { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libxml-2.0 >= 2.6.26\""; } >&5
+  ($PKG_CONFIG --exists --print-errors "libxml-2.0 >= 2.6.26") 2>&5
   ac_status=$?
   $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
   test $ac_status = 0; }; then
-  pkg_cv_LIBXML2_LIBS=`$PKG_CONFIG --libs "libxml-2.0 >= 2.7.7" 2>/dev/null`
+  pkg_cv_LIBXML2_LIBS=`$PKG_CONFIG --libs "libxml-2.0 >= 2.6.26" 2>/dev/null`
                      test "x$?" != "x0" && pkg_failed=yes
 else
   pkg_failed=yes
@@ -19576,9 +19541,9 @@ else
         _pkg_short_errors_supported=no
 fi
         if test $_pkg_short_errors_supported = yes; then
-               LIBXML2_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "libxml-2.0 >= 2.7.7" 2>&1`
+               LIBXML2_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "libxml-2.0 >= 2.6.26" 2>&1`
         else
-               LIBXML2_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "libxml-2.0 >= 2.7.7" 2>&1`
+               LIBXML2_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "libxml-2.0 >= 2.6.26" 2>&1`
         fi
        # Put the nasty error message in config.log where it belongs
        echo "$LIBXML2_PKG_ERRORS" >&5
@@ -19759,106 +19724,6 @@ if test "x${request_jemalloc}" = "xyes" &&
   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
 
@@ -22332,6 +22197,7 @@ for ac_func in  \
   memchr \
   memmove \
   memset \
+  mkostemp \
   socket \
   sqrt \
   strchr \
@@ -23985,6 +23851,43 @@ else
   :
 fi
 
+    # Disable noexcept-type warning of g++-7.  This is not harmful as
+    # long as all source files are compiled with the same compiler.
+    { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether C++ compiler accepts -Wno-noexcept-type" >&5
+$as_echo_n "checking whether C++ compiler accepts -Wno-noexcept-type... " >&6; }
+if ${ax_cv_check_cxxflags___Wno_noexcept_type+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+
+  ax_check_save_flags=$CXXFLAGS
+  CXXFLAGS="$CXXFLAGS  -Wno-noexcept-type"
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_cxx_try_compile "$LINENO"; then :
+  ax_cv_check_cxxflags___Wno_noexcept_type=yes
+else
+  ax_cv_check_cxxflags___Wno_noexcept_type=no
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+  CXXFLAGS=$ax_check_save_flags
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ax_cv_check_cxxflags___Wno_noexcept_type" >&5
+$as_echo "$ax_cv_check_cxxflags___Wno_noexcept_type" >&6; }
+if test "x$ax_cv_check_cxxflags___Wno_noexcept_type" = xyes; then :
+  CXXFLAGS="$CXXFLAGS -Wno-noexcept-type"
+else
+  :
+fi
+
     ac_ext=c
 ac_cpp='$CPP $CPPFLAGS'
 ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
@@ -24218,10 +24121,6 @@ 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
@@ -24664,7 +24563,7 @@ cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
 # 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.20.0, which was
+This file was extended by nghttp2 $as_me 1.31.1, which was
 generated by GNU Autoconf 2.69.  Invocation command line was
 
   CONFIG_FILES    = $CONFIG_FILES
@@ -24730,7 +24629,7 @@ _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.20.0
+nghttp2 config.status 1.31.1
 configured by $0, generated by GNU Autoconf 2.69,
   with options \\"\$ac_cs_config\\"
 
@@ -26759,7 +26658,6 @@ fi
       Libev:          ${have_libev} (CFLAGS='${LIBEV_CFLAGS}' LIBS='${LIBEV_LIBS}')
       Libc-ares       ${have_libcares} (CFLAGS='${LIBCARES_CFLAGS}' LIBS='${LIBCARES_LIBS}')
       Libevent(SSL):  ${have_libevent_openssl} (CFLAGS='${LIBEVENT_OPENSSL_CFLAGS}' LIBS='${LIBEVENT_OPENSSL_LIBS}')
-      Spdylay:        ${have_spdylay} (CFLAGS='${LIBSPDYLAY_CFLAGS}' LIBS='${LIBSPDYLAY_LIBS}')
       Jansson:        ${have_jansson} (CFLAGS='${JANSSON_CFLAGS}' LIBS='${JANSSON_LIBS}')
       Jemalloc:       ${have_jemalloc} (LIBS='${JEMALLOC_LIBS}')
       Zlib:           ${have_zlib} (CFLAGS='${ZLIB_CFLAGS}' LIBS='${ZLIB_LIBS}')
@@ -26824,7 +26722,6 @@ $as_echo "$as_me: summary of build options:
       Libev:          ${have_libev} (CFLAGS='${LIBEV_CFLAGS}' LIBS='${LIBEV_LIBS}')
       Libc-ares       ${have_libcares} (CFLAGS='${LIBCARES_CFLAGS}' LIBS='${LIBCARES_LIBS}')
       Libevent(SSL):  ${have_libevent_openssl} (CFLAGS='${LIBEVENT_OPENSSL_CFLAGS}' LIBS='${LIBEVENT_OPENSSL_LIBS}')
-      Spdylay:        ${have_spdylay} (CFLAGS='${LIBSPDYLAY_CFLAGS}' LIBS='${LIBSPDYLAY_LIBS}')
       Jansson:        ${have_jansson} (CFLAGS='${JANSSON_CFLAGS}' LIBS='${JANSSON_LIBS}')
       Jemalloc:       ${have_jemalloc} (LIBS='${JEMALLOC_LIBS}')
       Zlib:           ${have_zlib} (CFLAGS='${ZLIB_CFLAGS}' LIBS='${ZLIB_LIBS}')
index 085f517..b489609 100644 (file)
@@ -25,7 +25,7 @@ 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.20.0], [t-tujikawa@users.sourceforge.net])
+AC_INIT([nghttp2], [1.31.1], [t-tujikawa@users.sourceforge.net])
 AC_CONFIG_AUX_DIR([.])
 AC_CONFIG_MACRO_DIR([m4])
 AC_CONFIG_HEADERS([config.h])
@@ -44,9 +44,9 @@ m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])])
 
 dnl See versioning rule:
 dnl  http://www.gnu.org/software/libtool/manual/html_node/Updating-version-info.html
-AC_SUBST(LT_CURRENT, 27)
-AC_SUBST(LT_REVISION, 0)
-AC_SUBST(LT_AGE, 13)
+AC_SUBST(LT_CURRENT, 30)
+AC_SUBST(LT_REVISION, 1)
+AC_SUBST(LT_AGE, 16)
 
 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"`
@@ -117,11 +117,6 @@ AC_ARG_WITH([jemalloc],
                     [Use jemalloc [default=check]])],
     [request_jemalloc=$withval], [request_jemalloc=check])
 
-AC_ARG_WITH([spdylay],
-    [AS_HELP_STRING([--with-spdylay],
-                    [Use spdylay [default=no]])],
-    [request_spdylay=$withval], [request_spdylay=no])
-
 AC_ARG_WITH([systemd],
     [AS_HELP_STRING([--with-systemd],
                     [Enable systemd support in nghttpx [default=check]])],
@@ -176,19 +171,9 @@ else
   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])
   AC_DEFINE([NGHTTP2_NORETURN], [__attribute__((noreturn))], [Hint to the compiler that a function never return])
 else
-  AC_DEFINE([_U_], , [Hint to the compiler that a function parameter is not used])
   AC_DEFINE([NGHTTP2_NORETURN], , [Hint to the compiler that a function never return])
 fi
 
@@ -420,7 +405,7 @@ if test "x${request_systemd}" = "xyes" &&
 fi
 
 # libxml2 (for src/nghttp)
-PKG_CHECK_MODULES([LIBXML2], [libxml-2.0 >= 2.7.7],
+PKG_CHECK_MODULES([LIBXML2], [libxml-2.0 >= 2.6.26],
                   [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.])
@@ -468,26 +453,6 @@ if test "x${request_jemalloc}" = "xyes" &&
   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
 
@@ -723,6 +688,7 @@ AC_CHECK_FUNCS([ \
   memchr \
   memmove \
   memset \
+  mkostemp \
   socket \
   sqrt \
   strchr \
@@ -814,6 +780,9 @@ if test "x$werror" != "xno"; then
     AX_CHECK_COMPILE_FLAG([-Werror], [CXXFLAGS="$CXXFLAGS -Werror"])
     AX_CHECK_COMPILE_FLAG([-Wformat-security], [CXXFLAGS="$CXXFLAGS -Wformat-security"])
     AX_CHECK_COMPILE_FLAG([-Wsometimes-uninitialized], [CXXFLAGS="$CXXFLAGS -Wsometimes-uninitialized"])
+    # Disable noexcept-type warning of g++-7.  This is not harmful as
+    # long as all source files are compiled with the same compiler.
+    AX_CHECK_COMPILE_FLAG([-Wno-noexcept-type], [CXXFLAGS="$CXXFLAGS -Wno-noexcept-type"])
     AC_LANG_POP()
 fi
 
@@ -934,7 +903,6 @@ AC_MSG_NOTICE([summary of build options:
       Libev:          ${have_libev} (CFLAGS='${LIBEV_CFLAGS}' LIBS='${LIBEV_LIBS}')
       Libc-ares       ${have_libcares} (CFLAGS='${LIBCARES_CFLAGS}' LIBS='${LIBCARES_LIBS}')
       Libevent(SSL):  ${have_libevent_openssl} (CFLAGS='${LIBEVENT_OPENSSL_CFLAGS}' LIBS='${LIBEVENT_OPENSSL_LIBS}')
-      Spdylay:        ${have_spdylay} (CFLAGS='${LIBSPDYLAY_CFLAGS}' LIBS='${LIBSPDYLAY_LIBS}')
       Jansson:        ${have_jansson} (CFLAGS='${JANSSON_CFLAGS}' LIBS='${JANSSON_LIBS}')
       Jemalloc:       ${have_jemalloc} (LIBS='${JEMALLOC_LIBS}')
       Zlib:           ${have_zlib} (CFLAGS='${ZLIB_CFLAGS}' LIBS='${ZLIB_LIBS}')
index 5d650e0..697304b 100644 (file)
@@ -1,7 +1,7 @@
-# Makefile.in generated by automake 1.15 from Makefile.am.
+# Makefile.in generated by automake 1.15.1 from Makefile.am.
 # @configure_input@
 
-# Copyright (C) 1994-2014 Free Software Foundation, Inc.
+# Copyright (C) 1994-2017 Free Software Foundation, Inc.
 
 # This Makefile.in is free software; the Free Software Foundation
 # gives unlimited permission to copy and/or distribute it,
@@ -214,8 +214,6 @@ LIBMRUBY_CFLAGS = @LIBMRUBY_CFLAGS@
 LIBMRUBY_LIBS = @LIBMRUBY_LIBS@
 LIBOBJS = @LIBOBJS@
 LIBS = @LIBS@
-LIBSPDYLAY_CFLAGS = @LIBSPDYLAY_CFLAGS@
-LIBSPDYLAY_LIBS = @LIBSPDYLAY_LIBS@
 LIBTOOL = @LIBTOOL@
 LIBXML2_CFLAGS = @LIBXML2_CFLAGS@
 LIBXML2_LIBS = @LIBXML2_LIBS@
diff --git a/depcomp b/depcomp
index fc98710..b39f98f 100755 (executable)
--- a/depcomp
+++ b/depcomp
@@ -1,9 +1,9 @@
 #! /bin/sh
 # depcomp - compile a program generating dependencies as side-effects
 
-scriptversion=2013-05-30.07; # UTC
+scriptversion=2016-01-11.22; # UTC
 
-# Copyright (C) 1999-2014 Free Software Foundation, Inc.
+# Copyright (C) 1999-2017 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
@@ -786,6 +786,6 @@ exit 0
 # 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-time-zone: "UTC0"
 # time-stamp-end: "; # UTC"
 # End:
index 5bca75d..34c0279 100644 (file)
@@ -49,6 +49,7 @@ set(APIDOCS
   nghttp2_rcbuf_decref.rst
   nghttp2_rcbuf_get_buf.rst
   nghttp2_rcbuf_incref.rst
+  nghttp2_rcbuf_is_static.rst
   nghttp2_select_next_protocol.rst
   nghttp2_session_callbacks_del.rst
   nghttp2_session_callbacks_new.rst
index 9511b49..07cd34e 100644 (file)
@@ -74,12 +74,14 @@ APIDOCS= \
        nghttp2_rcbuf_decref.rst \
        nghttp2_rcbuf_get_buf.rst \
        nghttp2_rcbuf_incref.rst \
+       nghttp2_rcbuf_is_static.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_error_callback.rst \
+       nghttp2_session_callbacks_set_error_callback2.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 \
@@ -141,6 +143,7 @@ APIDOCS= \
        nghttp2_session_set_local_window_size.rst \
        nghttp2_session_set_next_stream_id.rst \
        nghttp2_session_set_stream_user_data.rst \
+       nghttp2_session_set_user_data.rst \
        nghttp2_session_terminate_session.rst \
        nghttp2_session_terminate_session2.rst \
        nghttp2_session_upgrade.rst \
@@ -267,7 +270,7 @@ apiref.rst: \
 $(APIDOCS): apiref.rst
 
 clean-local:
-       [ $(srcdir) = $(builddir) ] || for i in $(RST_FILES); do [ -e $(builddir)/$$i ] && rm -f $(builddir)/$$i; done
+       if [ $(srcdir) != $(builddir) ]; then for i in $(RST_FILES); do rm -f $(builddir)/$$i; done fi
        -rm -f apiref.rst
        -rm -f $(APIDOCS)
        -rm -rf $(BUILDDIR)/*
index 196357f..2f83a16 100644 (file)
@@ -1,7 +1,7 @@
-# Makefile.in generated by automake 1.15 from Makefile.am.
+# Makefile.in generated by automake 1.15.1 from Makefile.am.
 # @configure_input@
 
-# Copyright (C) 1994-2014 Free Software Foundation, Inc.
+# Copyright (C) 1994-2017 Free Software Foundation, Inc.
 
 # This Makefile.in is free software; the Free Software Foundation
 # gives unlimited permission to copy and/or distribute it,
@@ -261,8 +261,6 @@ LIBMRUBY_CFLAGS = @LIBMRUBY_CFLAGS@
 LIBMRUBY_LIBS = @LIBMRUBY_LIBS@
 LIBOBJS = @LIBOBJS@
 LIBS = @LIBS@
-LIBSPDYLAY_CFLAGS = @LIBSPDYLAY_CFLAGS@
-LIBSPDYLAY_LIBS = @LIBSPDYLAY_LIBS@
 LIBTOOL = @LIBTOOL@
 LIBXML2_CFLAGS = @LIBXML2_CFLAGS@
 LIBXML2_LIBS = @LIBXML2_LIBS@
@@ -433,12 +431,14 @@ APIDOCS = \
        nghttp2_rcbuf_decref.rst \
        nghttp2_rcbuf_get_buf.rst \
        nghttp2_rcbuf_incref.rst \
+       nghttp2_rcbuf_is_static.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_error_callback.rst \
+       nghttp2_session_callbacks_set_error_callback2.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 \
@@ -500,6 +500,7 @@ APIDOCS = \
        nghttp2_session_set_local_window_size.rst \
        nghttp2_session_set_next_stream_id.rst \
        nghttp2_session_set_stream_user_data.rst \
+       nghttp2_session_set_user_data.rst \
        nghttp2_session_terminate_session.rst \
        nghttp2_session_terminate_session2.rst \
        nghttp2_session_upgrade.rst \
@@ -901,7 +902,7 @@ apiref.rst: \
 $(APIDOCS): apiref.rst
 
 clean-local:
-       [ $(srcdir) = $(builddir) ] || for i in $(RST_FILES); do [ -e $(builddir)/$$i ] && rm -f $(builddir)/$$i; done
+       if [ $(srcdir) != $(builddir) ]; then for i in $(RST_FILES); do rm -f $(builddir)/$$i; done fi
        -rm -f apiref.rst
        -rm -f $(APIDOCS)
        -rm -rf $(BUILDDIR)/*
index 79dbf65..14114cb 100644 (file)
@@ -13,6 +13,7 @@ import re
 
 from docutils import nodes
 from docutils.parsers.rst import directives
+from docutils.parsers.rst import Directive
 
 from sphinx import addnodes
 from sphinx import version_info
@@ -21,10 +22,8 @@ from sphinx.locale import l_, _
 from sphinx.domains import Domain, ObjType, Index
 from sphinx.directives import ObjectDescription
 from sphinx.util.nodes import make_refnode
-from sphinx.util.compat import Directive
 from sphinx.util.docfields import Field, GroupedField, TypedField
 
-
 # REs for Ruby signatures
 rb_sig_re = re.compile(
     r'''^ ([\w.]*\.)?            # class name(s)
index 5067ca3..c90776a 100644 (file)
@@ -8,7 +8,7 @@ _h2load()
     _get_comp_words_by_ref cur prev
     case $cur in
         -*)
-            COMPREPLY=( $( compgen -W '--connection-window-bits --clients --verbose --ciphers --rate --no-tls-proto --header-table-size --requests --base-uri --h1 --threads --npn-list --rate-period --data --version --connection-inactivity-timeout --timing-script-file --encoder-header-table-size --max-concurrent-streams --connection-active-timeout --input-file --help --window-bits --header ' -- "$cur" ) )
+            COMPREPLY=( $( compgen -W '--connection-window-bits --clients --verbose --ciphers --rate --no-tls-proto --header-table-size --requests --base-uri --h1 --threads --npn-list --rate-period --data --version --connection-inactivity-timeout --timing-script-file --encoder-header-table-size --max-concurrent-streams --connection-active-timeout --input-file --help --window-bits --warm-up-time --duration --header ' -- "$cur" ) )
             ;;
         *)
             _filedir
index 76ba7a0..9a4cf19 100644 (file)
@@ -8,7 +8,7 @@ _nghttp()
     _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 --encoder-header-table-size --padding --hexdump --max-concurrent-streams --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 --expect-continue --stat --header ' -- "$cur" ) )
+            COMPREPLY=( $( compgen -W '--no-push --verbose --no-dep --get-assets --har --header-table-size --multiply --encoder-header-table-size --padding --hexdump --max-concurrent-streams --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 --expect-continue --stat --no-verify-peer --header ' -- "$cur" ) )
             ;;
         *)
             _filedir
index c53c092..0c140d7 100644 (file)
@@ -8,7 +8,7 @@ _nghttpx()
     _get_comp_words_by_ref cur prev
     case $cur in
         -*)
-            COMPREPLY=( $( compgen -W '--worker-read-rate --include --frontend-http2-dump-response-header --tls-ticket-key-file --verify-client-cacert --max-response-header-fields --backend-http2-window-size --frontend-keep-alive-timeout --backend-request-buffer --max-request-header-fields --fastopen --backend-connect-timeout --tls-max-proto-version --conf --dns-lookup-timeout --backend-http2-max-concurrent-streams --worker-write-burst --npn-list --dns-max-try --fetch-ocsp-response-file --no-via --tls-session-cache-memcached-cert-file --no-http2-cipher-black-list --mruby-file --client-no-http2-cipher-black-list --stream-read-timeout --client-ciphers --forwarded-for --accesslog-syslog --dns-cache-timeout --frontend-http2-read-timeout --listener-disable-timeout --ciphers --client-psk-secrets --strip-incoming-x-forwarded-for --no-server-rewrite --private-key-passwd-file --backend-keep-alive-timeout --backend-http-proxy-uri --frontend-max-requests --rlimit-nofile --tls-ticket-key-memcached-cert-file --ocsp-update-interval --forwarded-by --tls-session-cache-memcached-private-key-file --error-page --backend-write-timeout --tls-dyn-rec-warmup-threshold --tls-ticket-key-memcached-max-retry --frontend-http2-window-size --http2-no-cookie-crumbling --worker-read-burst --dh-param-file --accesslog-format --errorlog-syslog --redirect-https-port --request-header-field-buffer --api-max-request-body --frontend-http2-decoder-dynamic-table-size --errorlog-file --frontend-http2-max-concurrent-streams --psk-secrets --frontend-write-timeout --tls-ticket-key-cipher --read-burst --backend --server-name --insecure --backend-max-backoff --log-level --host-rewrite --tls-ticket-key-memcached-interval --frontend-http2-setting-timeout --frontend-http2-connection-window-size --worker-frontend-connections --syslog-facility --no-server-push --no-location-rewrite --single-thread --tls-session-cache-memcached --no-ocsp --backend-response-buffer --tls-min-proto-version --workers --add-forwarded --worker-write-rate --add-request-header --backend-http2-settings-timeout --subcert --ecdh-curves --no-kqueue --help --frontend-frame-debug --tls-sct-dir --pid-file --frontend-http2-dump-request-header --daemon --write-rate --altsvc --backend-http2-decoder-dynamic-table-size --user --add-x-forwarded-for --frontend-read-timeout --tls-ticket-key-memcached-max-fail --backlog --write-burst --backend-connections-per-host --response-header-field-buffer --tls-ticket-key-memcached-address-family --padding --tls-session-cache-memcached-address-family --stream-write-timeout --cacert --tls-ticket-key-memcached-private-key-file --accesslog-write-early --backend-address-family --backend-http2-connection-window-size --version --add-response-header --backend-read-timeout --frontend-http2-optimize-window-size --frontend --accesslog-file --http2-proxy --backend-http2-encoder-dynamic-table-size --client-private-key-file --client-cert-file --tls-ticket-key-memcached --tls-dyn-rec-idle-timeout --frontend-http2-optimize-write-buffer-size --verify-client --frontend-http2-encoder-dynamic-table-size --read-rate --backend-connections-per-frontend --strip-incoming-forwarded ' -- "$cur" ) )
+            COMPREPLY=( $( compgen -W '--worker-read-rate --include --frontend-http2-dump-response-header --tls-ticket-key-file --verify-client-cacert --max-response-header-fields --backend-http2-window-size --frontend-keep-alive-timeout --backend-request-buffer --max-request-header-fields --backend-connect-timeout --tls-max-proto-version --conf --dns-lookup-timeout --backend-http2-max-concurrent-streams --worker-write-burst --npn-list --dns-max-try --fetch-ocsp-response-file --no-via --tls-session-cache-memcached-cert-file --no-http2-cipher-black-list --mruby-file --add-forwarded --client-no-http2-cipher-black-list --stream-read-timeout --client-ciphers --ocsp-update-interval --forwarded-for --accesslog-syslog --dns-cache-timeout --frontend-http2-read-timeout --listener-disable-timeout --ciphers --client-psk-secrets --strip-incoming-x-forwarded-for --no-server-rewrite --private-key-passwd-file --backend-keep-alive-timeout --backend-http-proxy-uri --frontend-max-requests --rlimit-nofile --no-strip-incoming-x-forwarded-proto --tls-ticket-key-memcached-cert-file --no-verify-ocsp --forwarded-by --tls-session-cache-memcached-private-key-file --error-page --ocsp-startup --backend-write-timeout --tls-dyn-rec-warmup-threshold --tls-ticket-key-memcached-max-retry --frontend-http2-window-size --http2-no-cookie-crumbling --worker-read-burst --dh-param-file --accesslog-format --errorlog-syslog --redirect-https-port --request-header-field-buffer --api-max-request-body --frontend-http2-decoder-dynamic-table-size --errorlog-file --frontend-http2-max-concurrent-streams --psk-secrets --frontend-write-timeout --tls-ticket-key-cipher --read-burst --no-add-x-forwarded-proto --backend --server-name --insecure --backend-max-backoff --log-level --host-rewrite --tls-ticket-key-memcached-interval --frontend-http2-setting-timeout --frontend-http2-connection-window-size --worker-frontend-connections --syslog-facility --fastopen --no-location-rewrite --single-thread --tls-session-cache-memcached --no-ocsp --backend-response-buffer --tls-min-proto-version --workers --add-x-forwarded-for --no-server-push --worker-write-rate --add-request-header --backend-http2-settings-timeout --subcert --ecdh-curves --no-kqueue --help --frontend-frame-debug --tls-sct-dir --pid-file --frontend-http2-dump-request-header --daemon --write-rate --altsvc --backend-http2-decoder-dynamic-table-size --user --verify-client-tolerate-expired --frontend-read-timeout --tls-ticket-key-memcached-max-fail --backlog --write-burst --backend-connections-per-host --response-header-field-buffer --tls-ticket-key-memcached-address-family --padding --tls-session-cache-memcached-address-family --stream-write-timeout --cacert --tls-ticket-key-memcached-private-key-file --accesslog-write-early --backend-address-family --backend-http2-connection-window-size --version --add-response-header --backend-read-timeout --frontend-http2-optimize-window-size --frontend --accesslog-file --http2-proxy --backend-http2-encoder-dynamic-table-size --client-private-key-file --single-process --client-cert-file --tls-ticket-key-memcached --tls-dyn-rec-idle-timeout --frontend-http2-optimize-write-buffer-size --verify-client --frontend-http2-encoder-dynamic-table-size --read-rate --backend-connections-per-frontend --strip-incoming-forwarded ' -- "$cur" ) )
             ;;
         *)
             _filedir
index 0cc5a5a..bc6401e 100644 (file)
@@ -157,7 +157,7 @@ html_theme_path = ['@top_srcdir@/doc/_themes']
 
 # If true, SmartyPants will be used to convert quotes and dashes to
 # typographically correct entities.
-html_use_smartypants = False
+#html_use_smartypants = False
 
 # Custom sidebar templates, maps document names to template names.
 html_sidebars = {
index 97ae34a..5dfaee0 100644 (file)
@@ -165,6 +165,11 @@ Enums
 
         (``-535``) 
         Indicates that a processing was canceled.
+    .. macro:: NGHTTP2_ERR_SETTINGS_EXPECTED
+
+        (``-536``) 
+        When a local endpoint expects to receive SETTINGS frame, it
+        receives an other type of frame.
     .. macro:: NGHTTP2_ERR_FATAL
 
         (``-900``) 
index 830a9b7..46dfe62 100644 (file)
@@ -1,6 +1,6 @@
 .\" Man page generated from reStructuredText.
 .
-.TH "H2LOAD" "1" "Feb 26, 2017" "1.20.0" "nghttp2"
+.TH "H2LOAD" "1" "Apr 07, 2018" "1.31.1" "nghttp2"
 .SH NAME
 h2load \- HTTP/2 benchmarking tool
 .
@@ -35,7 +35,7 @@ level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
 \fBh2load\fP [OPTIONS]... [URI]...
 .SH DESCRIPTION
 .sp
-benchmarking tool for HTTP/2 and SPDY server
+benchmarking tool for HTTP/2 server
 .INDENT 0.0
 .TP
 .B <URI>
@@ -54,7 +54,9 @@ scheme, host or port values.
 Number of  requests across all  clients.  If it  is used
 with \fI\%\-\-timing\-script\-file\fP option,  this option specifies
 the number of requests  each client performs rather than
-the number of requests across all clients.
+the number of requests  across all clients.  This option
+is ignored if timing\-based  benchmarking is enabled (see
+\fI\%\-\-duration\fP option).
 .sp
 Default: \fB1\fP
 .UNINDENT
@@ -99,7 +101,6 @@ Default: \fB1\fP
 .TP
 .B \-w, \-\-window\-bits=<N>
 Sets the stream level initial window size to (2**<N>)\-1.
-For SPDY, 2**<N> is used instead.
 .sp
 Default: \fB30\fP
 .UNINDENT
@@ -107,9 +108,7 @@ Default: \fB30\fP
 .TP
 .B \-W, \-\-connection\-window\-bits=<N>
 Sets  the  connection  level   initial  window  size  to
-(2**<N>)\-1.  For SPDY, if <N>  is strictly less than 16,
-this option  is ignored.   Otherwise 2**<N> is  used for
-SPDY.
+(2**<N>)\-1.
 .sp
 Default: \fB30\fP
 .UNINDENT
@@ -124,15 +123,14 @@ Add/Override a header to the requests.
 Set allowed  cipher list.  The  format of the  string is
 described in OpenSSL ciphers(1).
 .sp
-Default: \fBECDHE\-ECDSA\-CHACHA20\-POLY1305:ECDHE\-RSA\-CHACHA20\-POLY1305:ECDHE\-ECDSA\-AES128\-GCM\-SHA256:ECDHE\-RSA\-AES128\-GCM\-SHA256:ECDHE\-ECDSA\-AES256\-GCM\-SHA384:ECDHE\-RSA\-AES256\-GCM\-SHA384:DHE\-RSA\-AES128\-GCM\-SHA256:DHE\-RSA\-AES256\-GCM\-SHA384:ECDHE\-ECDSA\-AES128\-SHA256:ECDHE\-RSA\-AES128\-SHA256:ECDHE\-ECDSA\-AES128\-SHA:ECDHE\-RSA\-AES256\-SHA384:ECDHE\-RSA\-AES128\-SHA:ECDHE\-ECDSA\-AES256\-SHA384:ECDHE\-ECDSA\-AES256\-SHA:ECDHE\-RSA\-AES256\-SHA:DHE\-RSA\-AES128\-SHA256:DHE\-RSA\-AES128\-SHA:DHE\-RSA\-AES256\-SHA256:DHE\-RSA\-AES256\-SHA:ECDHE\-ECDSA\-DES\-CBC3\-SHA:ECDHE\-RSA\-DES\-CBC3\-SHA:EDH\-RSA\-DES\-CBC3\-SHA:AES128\-GCM\-SHA256:AES256\-GCM\-SHA384:AES128\-SHA256:AES256\-SHA256:AES128\-SHA:AES256\-SHA:DES\-CBC3\-SHA:!DSS\fP
+Default: \fBECDHE\-ECDSA\-AES256\-GCM\-SHA384:ECDHE\-RSA\-AES256\-GCM\-SHA384:ECDHE\-ECDSA\-CHACHA20\-POLY1305:ECDHE\-RSA\-CHACHA20\-POLY1305:ECDHE\-ECDSA\-AES128\-GCM\-SHA256:ECDHE\-RSA\-AES128\-GCM\-SHA256:ECDHE\-ECDSA\-AES256\-SHA384:ECDHE\-RSA\-AES256\-SHA384:ECDHE\-ECDSA\-AES128\-SHA256:ECDHE\-RSA\-AES128\-SHA256\fP
 .UNINDENT
 .INDENT 0.0
 .TP
 .B \-p, \-\-no\-tls\-proto=<PROTOID>
 Specify ALPN identifier of the  protocol to be used when
 accessing http URI without SSL/TLS.
-Available protocols: h2c and
-http/1.1
+Available protocols: h2c and http/1.1
 .sp
 Default: \fBh2c\fP
 .UNINDENT
@@ -170,6 +168,19 @@ option is 1s.
 .UNINDENT
 .INDENT 0.0
 .TP
+.B \-D, \-\-duration=<N>
+Specifies the main duration for the measurements in case
+of timing\-based benchmarking.
+.UNINDENT
+.INDENT 0.0
+.TP
+.B \-\-warm\-up\-time=<DURATION>
+Specifies the  time  period  before  starting the actual
+measurements, in  case  of  timing\-based benchmarking.
+Needs to provided along with \fI\%\-D\fP option.
+.UNINDENT
+.INDENT 0.0
+.TP
 .B \-T, \-\-connection\-active\-timeout=<DURATION>
 Specifies  the maximum  time that  h2load is  willing to
 keep a  connection open,  regardless of the  activity on
@@ -335,8 +346,7 @@ compression.  Let \fBdecompressed(headers)\fP to the number of bytes
 used for header fields after decompression.  The \fBspace savings\fP
 is calculated  by (1 \- \fBheaders\fP  / \fBdecompressed(headers)\fP) *
 100.  For HTTP/1.1, this is usually  0.00%, since it does not have
-header compression.  For HTTP/2 and SPDY, it shows some insightful
-numbers.
+header compression.  For HTTP/2, it shows some insightful numbers.
 .TP
 .B data
 The number of response body bytes received from the server.
@@ -433,7 +443,7 @@ 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.
+window size described in HTTP/2 protocol specification.
 .SH SEE ALSO
 .sp
 \fBnghttp(1)\fP, \fBnghttpd(1)\fP, \fBnghttpx(1)\fP
index 8ad5531..1a8171f 100644 (file)
@@ -14,7 +14,7 @@ SYNOPSIS
 DESCRIPTION
 -----------
 
-benchmarking tool for HTTP/2 and SPDY server
+benchmarking tool for HTTP/2 server
 
 .. describe:: <URI>
 
@@ -34,7 +34,9 @@ OPTIONS
     Number of  requests across all  clients.  If it  is used
     with :option:`--timing-script-file` option,  this option specifies
     the number of requests  each client performs rather than
-    the number of requests across all clients.
+    the number of requests  across all clients.  This option
+    is ignored if timing-based  benchmarking is enabled (see
+    :option:`--duration` option).
 
     Default: ``1``
 
@@ -74,16 +76,13 @@ OPTIONS
 .. option:: -w, --window-bits=<N>
 
     Sets the stream level initial window size to (2\*\*<N>)-1.
-    For SPDY, 2\*\*<N> is used instead.
 
     Default: ``30``
 
 .. option:: -W, --connection-window-bits=<N>
 
     Sets  the  connection  level   initial  window  size  to
-    (2\*\*<N>)-1.  For SPDY, if <N>  is strictly less than 16,
-    this option  is ignored.   Otherwise 2\*\*<N> is  used for
-    SPDY.
+    (2\*\*<N>)-1.
 
     Default: ``30``
 
@@ -96,14 +95,13 @@ OPTIONS
     Set allowed  cipher list.  The  format of the  string is
     described in OpenSSL ciphers(1).
 
-    Default: ``ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA:ECDHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA256:DHE-RSA-AES256-SHA:ECDHE-ECDSA-DES-CBC3-SHA:ECDHE-RSA-DES-CBC3-SHA:EDH-RSA-DES-CBC3-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:DES-CBC3-SHA:!DSS``
+    Default: ``ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256``
 
 .. option:: -p, --no-tls-proto=<PROTOID>
 
     Specify ALPN identifier of the  protocol to be used when
     accessing http URI without SSL/TLS.
-    Available protocols: h2c and
-    http/1.1
+    Available protocols: h2c and http/1.1
 
     Default: ``h2c``
 
@@ -136,6 +134,17 @@ OPTIONS
     the rate option is not used.  The default value for this
     option is 1s.
 
+.. option:: -D, --duration=<N>
+
+    Specifies the main duration for the measurements in case
+    of timing-based benchmarking.
+
+.. option:: --warm-up-time=<DURATION>
+
+    Specifies the  time  period  before  starting the actual
+    measurements, in  case  of  timing-based benchmarking.
+    Needs to provided along with :option:`-D` option.
+
 .. option:: -T, --connection-active-timeout=<DURATION>
 
     Specifies  the maximum  time that  h2load is  willing to
@@ -284,8 +293,7 @@ traffic
     used for header fields after decompression.  The ``space savings``
     is calculated  by (1 - ``headers``  / ``decompressed(headers)``) *
     100.  For HTTP/1.1, this is usually  0.00%, since it does not have
-    header compression.  For HTTP/2 and SPDY, it shows some insightful
-    numbers.
+    header compression.  For HTTP/2, it shows some insightful numbers.
   data
     The number of response body bytes received from the server.
 
@@ -353,7 +361,7 @@ 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.
+window size described in HTTP/2 protocol specification.
 
 SEE ALSO
 --------
index 105b5f6..cf7d525 100644 (file)
@@ -1,6 +1,6 @@
 .\" Man page generated from reStructuredText.
 .
-.TH "NGHTTP" "1" "Feb 26, 2017" "1.20.0" "nghttp2"
+.TH "NGHTTP" "1" "Apr 07, 2018" "1.31.1" "nghttp2"
 .SH NAME
 nghttp \- HTTP/2 client
 .
@@ -236,6 +236,12 @@ combined with the \fI\%\-d\fP option.
 .UNINDENT
 .INDENT 0.0
 .TP
+.B \-y, \-\-no\-verify\-peer
+Suppress  warning  on  server  certificate  verification
+failure.
+.UNINDENT
+.INDENT 0.0
+.TP
 .B \-\-version
 Display version information and exit.
 .UNINDENT
index a40eb2e..ae4ce03 100644 (file)
@@ -186,6 +186,11 @@ OPTIONS
     Continue interim response. This option is ignored unless
     combined with the :option:`-d` option.
 
+.. option:: -y, --no-verify-peer
+
+    Suppress  warning  on  server  certificate  verification
+    failure.
+
 .. option:: --version
 
     Display version information and exit.
index 4c80bfc..f7a533a 100644 (file)
@@ -24,8 +24,8 @@ Synopsis
     
     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:
+    This function returns the number of bytes written to *buf* if it
+    succeeds, or one of the following negative error codes:
     
     :macro:`NGHTTP2_ERR_NOMEM`
         Out of memory.
index 784b4f3..4ad3bae 100644 (file)
@@ -24,8 +24,8 @@ Synopsis
     
     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:
+    This function returns the number of bytes written to *vec* if it
+    succeeds, or one of the following negative error codes:
     
     :macro:`NGHTTP2_ERR_NOMEM`
         Out of memory.
index 602fe3b..ef10aa9 100644 (file)
@@ -15,4 +15,7 @@ Synopsis
     <https://tools.ietf.org/html/rfc7540#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.
+    disable this enforcement.  Please note that disabling this feature
+    does not change the fundamental client and server model of HTTP.
+    That is, even if the validation is disabled, only client can send
+    requests.
diff --git a/doc/nghttp2_rcbuf_is_static.rst b/doc/nghttp2_rcbuf_is_static.rst
new file mode 100644 (file)
index 0000000..50ba5a3
--- /dev/null
@@ -0,0 +1,15 @@
+
+nghttp2_rcbuf_is_static
+=======================
+
+Synopsis
+--------
+
+*#include <nghttp2/nghttp2.h>*
+
+.. function:: int nghttp2_rcbuf_is_static(const nghttp2_rcbuf *rcbuf)
+
+    
+    Returns nonzero if the underlying buffer is statically allocated,
+    and 0 otherwise. This can be useful for language bindings that wish
+    to avoid creating duplicate strings for these buffers.
index 249502f..49547f6 100644 (file)
@@ -12,3 +12,10 @@ Synopsis
     
     Sets callback function invoked when library tells error message to
     the application.
+    
+    This function is deprecated.  The new application should use
+    `nghttp2_session_callbacks_set_error_callback2()`.
+    
+    If both :type:`nghttp2_error_callback` and
+    :type:`nghttp2_error_callback2` are set, the latter takes
+    precedence.
diff --git a/doc/nghttp2_session_callbacks_set_error_callback2.rst b/doc/nghttp2_session_callbacks_set_error_callback2.rst
new file mode 100644 (file)
index 0000000..f3d48d0
--- /dev/null
@@ -0,0 +1,18 @@
+
+nghttp2_session_callbacks_set_error_callback2
+=============================================
+
+Synopsis
+--------
+
+*#include <nghttp2/nghttp2.h>*
+
+.. function:: void nghttp2_session_callbacks_set_error_callback2( nghttp2_session_callbacks *cbs, nghttp2_error_callback2 error_callback2)
+
+    
+    Sets callback function invoked when library tells error code, and
+    message to the application.
+    
+    If both :type:`nghttp2_error_callback` and
+    :type:`nghttp2_error_callback2` are set, the latter takes
+    precedence.
diff --git a/doc/nghttp2_session_set_user_data.rst b/doc/nghttp2_session_set_user_data.rst
new file mode 100644 (file)
index 0000000..4d2b167
--- /dev/null
@@ -0,0 +1,15 @@
+
+nghttp2_session_set_user_data
+=============================
+
+Synopsis
+--------
+
+*#include <nghttp2/nghttp2.h>*
+
+.. function:: void nghttp2_session_set_user_data(nghttp2_session *session, void *user_data)
+
+    
+    Sets *user_data* to *session*, overwriting the existing user data
+    specified in `nghttp2_session_client_new()`, or
+    `nghttp2_session_server_new()`.
index 7859e11..fa27688 100644 (file)
@@ -13,9 +13,8 @@ Synopsis
     Submits trailer fields 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
-    pseudo-header fields (header field whose name starts with ":") in
-    *nva*.
+    *nvlen* elements.  The application must not include pseudo-header
+    fields (headers whose names 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
index a7c6020..6051b60 100644 (file)
@@ -1,6 +1,6 @@
 .\" Man page generated from reStructuredText.
 .
-.TH "NGHTTPD" "1" "Feb 26, 2017" "1.20.0" "nghttp2"
+.TH "NGHTTPD" "1" "Apr 07, 2018" "1.31.1" "nghttp2"
 .SH NAME
 nghttpd \- HTTP/2 server
 .
index 139aae7..b442168 100644 (file)
@@ -1,6 +1,6 @@
 .\" Man page generated from reStructuredText.
 .
-.TH "NGHTTPX" "1" "Feb 26, 2017" "1.20.0" "nghttp2"
+.TH "NGHTTPX" "1" "Apr 07, 2018" "1.31.1" "nghttp2"
 .SH NAME
 nghttpx \- HTTP/2 proxy
 .
@@ -35,7 +35,7 @@ level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
 \fBnghttpx\fP [OPTIONS]... [<PRIVATE_KEY> <CERT>]
 .SH DESCRIPTION
 .sp
-A reverse proxy for HTTP/2, HTTP/1 and SPDY.
+A reverse proxy for HTTP/2, and HTTP/1.
 .INDENT 0.0
 .TP
 .B <PRIVATE_KEY>
@@ -62,8 +62,7 @@ domain socket  can be  specified by prefixing  path name
 with "unix:" (e.g., unix:/var/run/backend.sock).
 .sp
 Optionally, if <PATTERN>s are given, the backend address
-is  only  used  if  request  matches  the  pattern.   If
-\fI\%\-\-http2\-proxy\fP  is  used,  <PATTERN>s are  ignored.   The
+is  only  used  if  request matches  the  pattern.   The
 pattern  matching is  closely  designed  to ServeMux  in
 net/http package of  Go programming language.  <PATTERN>
 consists of  path, host +  path or just host.   The path
@@ -74,11 +73,16 @@ path which ends  with "\fI/\fP" also matches  the request path
 which  only  lacks  trailing  \(aq\fI/\fP\(aq  (e.g.,  path  "\fI/foo/\fP"
 matches request path  "\fI/foo\fP").  If it does  not end with
 "\fI/\fP", it  performs exact match against  the request path.
-If host  is given, it  performs exact match  against the
-request host.  If  host alone is given,  "\fI/\fP" is appended
-to it,  so that it  matches all request paths  under the
-host   (e.g.,   specifying   "nghttp2.org"   equals   to
-"nghttp2.org/").
+If  host  is given,  it  performs  a match  against  the
+request host.   For a  request received on  the frontend
+listener with  "sni\-fwd" parameter enabled, SNI  host is
+used instead of a request host.  If host alone is given,
+"\fI/\fP" is  appended to it,  so that it matches  all request
+paths  under the  host  (e.g., specifying  "nghttp2.org"
+equals  to "nghttp2.org/").   CONNECT method  is treated
+specially.  It  does not have  path, and we  don\(aqt allow
+empty path.  To workaround  this, we assume that CONNECT
+method has "\fI/\fP" as path.
 .sp
 Patterns with  host take  precedence over  patterns with
 just path.   Then, longer patterns take  precedence over
@@ -92,6 +96,18 @@ host    pattern    "*.nghttp2.org"    matches    against
 match  against  "nghttp2.org".   The exact  hosts  match
 takes precedence over the wildcard hosts match.
 .sp
+If path  part ends with  "*", it is treated  as wildcard
+path.  The  wildcard path  behaves differently  from the
+normal path.  For normal path,  match is made around the
+boundary of path component  separator,"\fI/\fP".  On the other
+hand, the wildcard  path does not take  into account the
+path component  separator.  All paths which  include the
+wildcard  path  without  last  "*" as  prefix,  and  are
+strictly longer than wildcard  path without last "*" are
+matched.  "*"  must match  at least one  character.  For
+example,  the   pattern  "\fI/foo*\fP"  matches   "\fI/foo/\fP"  and
+"\fI/foobar\fP".  But it does not match "\fI/foo\fP", or "\fI/fo\fP".
+.sp
 If <PATTERN> is omitted or  empty string, "\fI/\fP" is used as
 pattern,  which  matches  all request  paths  (catch\-all
 pattern).  The catch\-all backend must be given.
@@ -164,16 +180,32 @@ state, and this is the default behaviour.
 The     session     affinity    is     enabled     using
 "affinity=<METHOD>"  parameter.   If  "ip" is  given  in
 <METHOD>, client  IP based session affinity  is enabled.
-If  "none" is  given  in <METHOD>,  session affinity  is
-disabled, and this is the default.  The session affinity
-is enabled per  <PATTERN>.  If at least  one backend has
-"affinity" parameter,  and its  <METHOD> is  not "none",
-session  affinity is  enabled  for  all backend  servers
-sharing  the  same  <PATTERN>.   It is  advised  to  set
-"affinity"  parameter  to   all  backend  explicitly  if
-session affinity  is desired.  The session  affinity may
-break if one of the backend gets unreachable, or backend
-settings are reloaded or replaced by API.
+If "cookie"  is given in <METHOD>,  cookie based session
+affinity is  enabled.  If  "none" is given  in <METHOD>,
+session affinity  is disabled, and this  is the default.
+The session  affinity is  enabled per <PATTERN>.   If at
+least  one backend  has  "affinity"  parameter, and  its
+<METHOD> is not "none",  session affinity is enabled for
+all backend  servers sharing the same  <PATTERN>.  It is
+advised  to  set  "affinity" parameter  to  all  backend
+explicitly if session affinity  is desired.  The session
+affinity  may   break  if   one  of  the   backend  gets
+unreachable,  or   backend  settings  are   reloaded  or
+replaced by API.
+.sp
+If   "affinity=cookie"    is   used,    the   additional
+configuration                is                required.
+"affinity\-cookie\-name=<NAME>" must be  used to specify a
+name     of     cookie      to     use.      Optionally,
+"affinity\-cookie\-path=<PATH>" can  be used to  specify a
+path   which   cookie    is   applied.    The   optional
+"affinity\-cookie\-secure=<SECURE>"  controls  the  Secure
+attribute of a cookie.  The default value is "auto", and
+the Secure attribute is  determined by a request scheme.
+If a request scheme is "https", then Secure attribute is
+set.  Otherwise, it  is not set.  If  <SECURE> is "yes",
+the  Secure attribute  is  always set.   If <SECURE>  is
+"no", the Secure attribute is always omitted.
 .sp
 By default, name resolution of backend host name is done
 at  start  up,  or reloading  configuration.   If  "dns"
@@ -196,6 +228,13 @@ the   same   <PATTERN>.    It    is   advised   to   set
 "redirect\-if\-no\-tls"    parameter   to    all   backends
 explicitly if this feature is desired.
 .sp
+If "upgrade\-scheme"  parameter is used along  with "tls"
+parameter, HTTP/2 :scheme pseudo header field is changed
+to "https" from "http" when forwarding a request to this
+particular backend.  This is  a workaround for a backend
+server  which  requires  "https" :scheme  pseudo  header
+field on TLS encrypted connection.
+.sp
 Since ";" and ":" are  used as delimiter, <PATTERN> must
 not  contain these  characters.  Since  ";" has  special
 meaning in shell, the option value must be quoted.
@@ -219,6 +258,11 @@ parameters are mutually exclusive.
 Optionally, TLS  can be disabled by  specifying "no\-tls"
 parameter.  TLS is enabled by default.
 .sp
+If "sni\-fwd" parameter is  used, when performing a match
+to select a backend server,  SNI host name received from
+the client  is used  instead of  the request  host.  See
+\fI\%\-\-backend\fP option about the pattern match.
+.sp
 To  make this  frontend as  API endpoint,  specify "api"
 parameter.   This   is  disabled  by  default.    It  is
 important  to  limit the  access  to  the API  frontend.
@@ -434,8 +478,7 @@ this option will be simply ignored.
 .INDENT 0.0
 .TP
 .B \-\-frontend\-http2\-read\-timeout=<DURATION>
-Specify  read  timeout  for  HTTP/2  and  SPDY  frontend
-connection.
+Specify read timeout for HTTP/2 frontend connection.
 .sp
 Default: \fB3m\fP
 .UNINDENT
@@ -464,16 +507,16 @@ Default: \fB1m\fP
 .INDENT 0.0
 .TP
 .B \-\-stream\-read\-timeout=<DURATION>
-Specify  read timeout  for HTTP/2  and SPDY  streams.  0
-means no timeout.
+Specify  read timeout  for HTTP/2  streams.  0  means no
+timeout.
 .sp
 Default: \fB0\fP
 .UNINDENT
 .INDENT 0.0
 .TP
 .B \-\-stream\-write\-timeout=<DURATION>
-Specify write  timeout for  HTTP/2 and SPDY  streams.  0
-means no timeout.
+Specify write  timeout for  HTTP/2 streams.  0  means no
+timeout.
 .sp
 Default: \fB1m\fP
 .UNINDENT
@@ -553,7 +596,7 @@ Default: \fB2m\fP
 Set allowed  cipher list  for frontend  connection.  The
 format of the string is described in OpenSSL ciphers(1).
 .sp
-Default: \fBECDHE\-ECDSA\-CHACHA20\-POLY1305:ECDHE\-RSA\-CHACHA20\-POLY1305:ECDHE\-ECDSA\-AES128\-GCM\-SHA256:ECDHE\-RSA\-AES128\-GCM\-SHA256:ECDHE\-ECDSA\-AES256\-GCM\-SHA384:ECDHE\-RSA\-AES256\-GCM\-SHA384:DHE\-RSA\-AES128\-GCM\-SHA256:DHE\-RSA\-AES256\-GCM\-SHA384:ECDHE\-ECDSA\-AES128\-SHA256:ECDHE\-RSA\-AES128\-SHA256:ECDHE\-ECDSA\-AES128\-SHA:ECDHE\-RSA\-AES256\-SHA384:ECDHE\-RSA\-AES128\-SHA:ECDHE\-ECDSA\-AES256\-SHA384:ECDHE\-ECDSA\-AES256\-SHA:ECDHE\-RSA\-AES256\-SHA:DHE\-RSA\-AES128\-SHA256:DHE\-RSA\-AES128\-SHA:DHE\-RSA\-AES256\-SHA256:DHE\-RSA\-AES256\-SHA:ECDHE\-ECDSA\-DES\-CBC3\-SHA:ECDHE\-RSA\-DES\-CBC3\-SHA:EDH\-RSA\-DES\-CBC3\-SHA:AES128\-GCM\-SHA256:AES256\-GCM\-SHA384:AES128\-SHA256:AES256\-SHA256:AES128\-SHA:AES256\-SHA:DES\-CBC3\-SHA:!DSS\fP
+Default: \fBECDHE\-ECDSA\-AES256\-GCM\-SHA384:ECDHE\-RSA\-AES256\-GCM\-SHA384:ECDHE\-ECDSA\-CHACHA20\-POLY1305:ECDHE\-RSA\-CHACHA20\-POLY1305:ECDHE\-ECDSA\-AES128\-GCM\-SHA256:ECDHE\-RSA\-AES128\-GCM\-SHA256:ECDHE\-ECDSA\-AES256\-SHA384:ECDHE\-RSA\-AES256\-SHA384:ECDHE\-ECDSA\-AES128\-SHA256:ECDHE\-RSA\-AES128\-SHA256\fP
 .UNINDENT
 .INDENT 0.0
 .TP
@@ -561,7 +604,7 @@ Default: \fBECDHE\-ECDSA\-CHACHA20\-POLY1305:ECDHE\-RSA\-CHACHA20\-POLY1305:ECDH
 Set  allowed cipher  list for  backend connection.   The
 format of the string is described in OpenSSL ciphers(1).
 .sp
-Default: \fBECDHE\-ECDSA\-CHACHA20\-POLY1305:ECDHE\-RSA\-CHACHA20\-POLY1305:ECDHE\-ECDSA\-AES128\-GCM\-SHA256:ECDHE\-RSA\-AES128\-GCM\-SHA256:ECDHE\-ECDSA\-AES256\-GCM\-SHA384:ECDHE\-RSA\-AES256\-GCM\-SHA384:DHE\-RSA\-AES128\-GCM\-SHA256:DHE\-RSA\-AES256\-GCM\-SHA384:ECDHE\-ECDSA\-AES128\-SHA256:ECDHE\-RSA\-AES128\-SHA256:ECDHE\-ECDSA\-AES128\-SHA:ECDHE\-RSA\-AES256\-SHA384:ECDHE\-RSA\-AES128\-SHA:ECDHE\-ECDSA\-AES256\-SHA384:ECDHE\-ECDSA\-AES256\-SHA:ECDHE\-RSA\-AES256\-SHA:DHE\-RSA\-AES128\-SHA256:DHE\-RSA\-AES128\-SHA:DHE\-RSA\-AES256\-SHA256:DHE\-RSA\-AES256\-SHA:ECDHE\-ECDSA\-DES\-CBC3\-SHA:ECDHE\-RSA\-DES\-CBC3\-SHA:EDH\-RSA\-DES\-CBC3\-SHA:AES128\-GCM\-SHA256:AES256\-GCM\-SHA384:AES128\-SHA256:AES256\-SHA256:AES128\-SHA:AES256\-SHA:DES\-CBC3\-SHA:!DSS\fP
+Default: \fBECDHE\-ECDSA\-AES256\-GCM\-SHA384:ECDHE\-RSA\-AES256\-GCM\-SHA384:ECDHE\-ECDSA\-CHACHA20\-POLY1305:ECDHE\-RSA\-CHACHA20\-POLY1305:ECDHE\-ECDSA\-AES128\-GCM\-SHA256:ECDHE\-RSA\-AES128\-GCM\-SHA256:ECDHE\-ECDSA\-AES256\-SHA384:ECDHE\-RSA\-AES256\-SHA384:ECDHE\-ECDSA\-AES128\-SHA256:ECDHE\-RSA\-AES128\-SHA256\fP
 .UNINDENT
 .INDENT 0.0
 .TP
@@ -583,11 +626,14 @@ enabled for backend connections.
 .INDENT 0.0
 .TP
 .B \-\-cacert=<PATH>
-Set path to trusted CA  certificate file used in backend
-TLS connections.   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.
+Set path to trusted CA  certificate file.  It is used in
+backend  TLS connections  to verify  peer\(aqs certificate.
+It is also used to  verify OCSP response from the script
+set by \fI\%\-\-fetch\-ocsp\-response\-file\fP\&.  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
@@ -602,12 +648,12 @@ password protected it\(aqll be requested interactively.
 Specify  additional certificate  and  private key  file.
 nghttpx will  choose certificates based on  the hostname
 indicated by client using TLS SNI extension.  If nghttpx
-is  built with  OpenSSL >=  1.0.2, signature  algorithms
-(e.g., ECDSA+SHA256, RSA+SHA256) presented by client are
-also taken  into consideration.  This allows  nghttpx to
-send ECDSA certificate to  modern clients, while sending
-RSA based certificate to older clients.  This option can
-be  used multiple  times.  To  make OCSP  stapling work,
+is  built with  OpenSSL  >= 1.0.2,  the shared  elliptic
+curves (e.g., P\-256) between  client and server are also
+taken into  consideration.  This allows nghttpx  to send
+ECDSA certificate  to modern clients, while  sending RSA
+based certificate to older  clients.  This option can be
+used  multiple  times.   To  make  OCSP  stapling  work,
 <CERTPATH> must be absolute path.
 .sp
 Additional parameter  can be specified in  <PARAM>.  The
@@ -652,6 +698,14 @@ can contain multiple certificates.
 .UNINDENT
 .INDENT 0.0
 .TP
+.B \-\-verify\-client\-tolerate\-expired
+Accept  expired  client  certificate.   Operator  should
+handle  the expired  client  certificate  by some  means
+(e.g.,  mruby  script).   Otherwise, this  option  might
+cause a security risk.
+.UNINDENT
+.INDENT 0.0
+.TP
 .B \-\-client\-private\-key\-file=<PATH>
 Path to  file that contains  client private key  used in
 backend client authentication.
@@ -670,10 +724,14 @@ done in  case\-insensitive manner.  The  versions between
 \fI\%\-\-tls\-min\-proto\-version\fP and  \fI\%\-\-tls\-max\-proto\-version\fP are
 enabled.  If the protocol list advertised by client does
 not  overlap  this range,  you  will  receive the  error
-message "unknown protocol".  The available versions are:
+message "unknown protocol".  If a protocol version lower
+than TLSv1.2 is specified, make sure that the compatible
+ciphers are  included in \fI\%\-\-ciphers\fP option.   The default
+cipher  list  only   includes  ciphers  compatible  with
+TLSv1.2 or above.  The available versions are:
 TLSv1.2, TLSv1.1, and TLSv1.0
 .sp
-Default: \fBTLSv1.1\fP
+Default: \fBTLSv1.2\fP
 .UNINDENT
 .INDENT 0.0
 .TP
@@ -803,6 +861,20 @@ Default: \fB4h\fP
 .UNINDENT
 .INDENT 0.0
 .TP
+.B \-\-ocsp\-startup
+Start  accepting connections  after initial  attempts to
+get OCSP responses  finish.  It does not  matter some of
+the  attempts  fail.  This  feature  is  useful if  OCSP
+responses   must    be   available    before   accepting
+connections.
+.UNINDENT
+.INDENT 0.0
+.TP
+.B \-\-no\-verify\-ocsp
+nghttpx does not verify OCSP response.
+.UNINDENT
+.INDENT 0.0
+.TP
 .B \-\-no\-ocsp
 Disable OCSP stapling.
 .UNINDENT
@@ -925,14 +997,14 @@ HTTP/2.   To  use  those   cipher  suites  with  HTTP/2,
 consider   to  use   \fI\%\-\-client\-no\-http2\-cipher\-black\-list\fP
 option.  But be aware its implications.
 .UNINDENT
-.SS HTTP/2 and SPDY
+.SS HTTP/2
 .INDENT 0.0
 .TP
 .B \-c, \-\-frontend\-http2\-max\-concurrent\-streams=<N>
 Set the maximum number of  the concurrent streams in one
-frontend HTTP/2 and SPDY session.
+frontend HTTP/2 session.
 .sp
-Default: \(ga\(ga 100\(ga\(ga
+Default: \fB100\fP
 .UNINDENT
 .INDENT 0.0
 .TP
@@ -947,17 +1019,16 @@ Default: \fB100\fP
 .INDENT 0.0
 .TP
 .B \-\-frontend\-http2\-window\-size=<SIZE>
-Sets the  per\-stream initial  window size of  HTTP/2 and
-SPDY frontend connection.
+Sets  the  per\-stream  initial  window  size  of  HTTP/2
+frontend connection.
 .sp
 Default: \fB65535\fP
 .UNINDENT
 .INDENT 0.0
 .TP
 .B \-\-frontend\-http2\-connection\-window\-size=<SIZE>
-Sets the  per\-connection window size of  HTTP/2 and SPDY
-frontend  connection.  For  SPDY  connection, the  value
-less than 64KiB is rounded up to 64KiB.
+Sets the  per\-connection window size of  HTTP/2 frontend
+connection.
 .sp
 Default: \fB65535\fP
 .UNINDENT
@@ -998,8 +1069,7 @@ default mode and HTTP/2  frontend via Link header field.
 It is  also supported if  both frontend and  backend are
 HTTP/2 in default mode.  In  this case, server push from
 backend session is relayed  to frontend, and server push
-via Link header field  is also supported.  SPDY frontend
-does not support server push.
+via Link header field is also supported.
 .UNINDENT
 .INDENT 0.0
 .TP
@@ -1070,7 +1140,7 @@ Default: \fB4K\fP
 .INDENT 0.0
 .TP
 .B (default mode)
-Accept HTTP/2, SPDY and HTTP/1.1 over SSL/TLS.  "no\-tls"
+Accept  HTTP/2,  and  HTTP/1.1 over  SSL/TLS.   "no\-tls"
 parameter is  used in  \fI\%\-\-frontend\fP option,  accept HTTP/2
 and HTTP/1.1 over cleartext  TCP.  The incoming HTTP/1.1
 connection  can  be  upgraded  to  HTTP/2  through  HTTP
@@ -1140,15 +1210,32 @@ $alpn: ALPN identifier of the protocol which generates
 the response.   For HTTP/1,  ALPN is  always http/1.1,
 regardless of minor version.
 .IP \(bu 2
-$ssl_cipher: cipher used for SSL/TLS connection.
+$tls_cipher: cipher used for SSL/TLS connection.
+.IP \(bu 2
+$tls_client_fingerprint_sha256: SHA\-256 fingerprint of
+client certificate.
+.IP \(bu 2
+$tls_client_fingerprint_sha1:  SHA\-1   fingerprint  of
+client certificate.
 .IP \(bu 2
-$ssl_protocol: protocol for SSL/TLS connection.
+$tls_client_subject_name:   subject  name   in  client
+certificate.
 .IP \(bu 2
-$ssl_session_id: session ID for SSL/TLS connection.
+$tls_client_issuer_name:   issuer   name   in   client
+certificate.
 .IP \(bu 2
-$ssl_session_reused:  "r"   if  SSL/TLS   session  was
+$tls_client_serial:    serial    number   in    client
+certificate.
+.IP \(bu 2
+$tls_protocol: protocol for SSL/TLS connection.
+.IP \(bu 2
+$tls_session_id: session ID for SSL/TLS connection.
+.IP \(bu 2
+$tls_session_reused:  "r"   if  SSL/TLS   session  was
 reused.  Otherwise, "."
 .IP \(bu 2
+$tls_sni: SNI server name for SSL/TLS connection.
+.IP \(bu 2
 $backend_host:  backend  host   used  to  fulfill  the
 request.  "\-" if backend host is not available.
 .IP \(bu 2
@@ -1205,6 +1292,21 @@ requests.
 .UNINDENT
 .INDENT 0.0
 .TP
+.B \-\-no\-add\-x\-forwarded\-proto
+Don\(aqt append  additional X\-Forwarded\-Proto  header field
+to  the   backend  request.   If  inbound   client  sets
+X\-Forwarded\-Proto,                                   and
+\fI\%\-\-no\-strip\-incoming\-x\-forwarded\-proto\fP  option  is  used,
+they are passed to the backend.
+.UNINDENT
+.INDENT 0.0
+.TP
+.B \-\-no\-strip\-incoming\-x\-forwarded\-proto
+Don\(aqt strip X\-Forwarded\-Proto  header field from inbound
+client requests.
+.UNINDENT
+.INDENT 0.0
+.TP
 .B \-\-add\-forwarded=<LIST>
 Append RFC  7239 Forwarded header field  with parameters
 specified in comma delimited list <LIST>.  The supported
@@ -1375,7 +1477,7 @@ Default: \fB443\fP
 .B \-\-api\-max\-request\-body=<SIZE>
 Set the maximum size of request body for API request.
 .sp
-Default: \fB16K\fP
+Default: \fB32M\fP
 .UNINDENT
 .SS DNS
 .INDENT 0.0
@@ -1458,6 +1560,17 @@ Set path to save PID of this program.
 Run this program as <USER>.   This option is intended to
 be used to drop root privileges.
 .UNINDENT
+.INDENT 0.0
+.TP
+.B \-\-single\-process
+Run this program in a  single process mode for debugging
+purpose.  Without this option,  nghttpx creates at least
+2  processes:  master  and worker  processes.   If  this
+option is  used, master  and worker  are unified  into a
+single process.  nghttpx still spawns additional process
+if neverbleed is used.  In  the single process mode, the
+signal handling feature is disabled.
+.UNINDENT
 .SS Scripting
 .INDENT 0.0
 .TP
@@ -1468,7 +1581,9 @@ Set mruby script file
 .INDENT 0.0
 .TP
 .B \-\-conf=<PATH>
-Load configuration from <PATH>.
+Load  configuration  from   <PATH>.   Please  note  that
+nghttpx always  tries to read the  default configuration
+file if \fI\%\-\-conf\fP is not given.
 .sp
 Default: \fB/etc/nghttpx/nghttpx.conf\fP
 .UNINDENT
@@ -1562,7 +1677,7 @@ follows:
 .INDENT 7.0
 .TP
 .B <datetime>
-It is a conbination of date and time when the log is written.  It
+It is a combination of date and time when the log is written.  It
 is in ISO 8601 format.
 .TP
 .B <master\-pid>
@@ -1691,6 +1806,22 @@ be customized using \fI\%\-\-fetch\-ocsp\-response\-file\fP option.
 .sp
 If OCSP query is failed, previous OCSP response, if any, is continued
 to be used.
+.sp
+\fI\%\-\-fetch\-ocsp\-response\-file\fP option provides wide range of
+possibility to manage OCSP response.  It can take an arbitrary script
+or executable.  The requirement is that it supports the command\-line
+interface of \fBfetch\-ocsp\-response\fP script, and it must return a
+valid DER encoded OCSP response on success.  It must return exit code
+0 on success, and 75 for temporary error, and the other error code for
+generic failure.  For large cluster of servers, it is not efficient
+for each server to perform OCSP query using \fBfetch\-ocsp\-response\fP\&.
+Instead, you can retrieve OCSP response in some way, and store it in a
+disk or a shared database.  Then specify a program in
+\fI\%\-\-fetch\-ocsp\-response\-file\fP to fetch it from those stores.
+This could provide a way to share the OCSP response between fleet of
+servers, and also any OCSP query strategy can be applied which may be
+beyond the ability of nghttpx itself or \fBfetch\-ocsp\-response\fP
+script.
 .SH TLS SESSION RESUMPTION
 .sp
 nghttpx supports TLS session resumption through both session ID and
@@ -1701,7 +1832,7 @@ By default, session ID is shared by all worker threads.
 .sp
 If \fI\%\-\-tls\-session\-cache\-memcached\fP is given, nghttpx will
 insert serialized session data to memcached with
-\fBnghttpx:tls\-session\-cache:\fP + lowercased hex string of session ID
+\fBnghttpx:tls\-session\-cache:\fP + lowercase hex string of session ID
 as a memcached entry key, with expiry time 12 hours.  Session timeout
 is set to 12 hours.
 .sp
@@ -1783,6 +1914,17 @@ API is subject to change in the future release.
 .UNINDENT
 .UNINDENT
 .sp
+\fBWARNING:\fP
+.INDENT 0.0
+.INDENT 3.5
+Almost all string value returned from method, or attribute is a
+fresh new mruby string, which involves memory allocation, and
+copies.  Therefore, it is strongly recommended to store a return
+value in a local variable, and use it, instead of calling method or
+accessing attribute repeatedly.
+.UNINDENT
+.UNINDENT
+.sp
 nghttpx allows users to extend its capability using mruby scripts.
 nghttpx has 2 hook points to execute mruby script: request phase and
 response phase.  The request phase hook is invoked after all request
@@ -1831,7 +1973,7 @@ Return \fI\%Response\fP object.
 .TP
 .B attribute [R] ctx
 Return Ruby hash object.  It persists until request finishes.
-So values set in request phase hoo can be retrieved in
+So values set in request phase hook can be retrieved in
 response phase hook.
 .UNINDENT
 .INDENT 7.0
@@ -1869,6 +2011,68 @@ Return true if TLS is used on the connection.
 .B attribute [R] tls_sni
 Return the TLS SNI value which client sent in this connection.
 .UNINDENT
+.INDENT 7.0
+.TP
+.B attribute [R] tls_client_fingerprint_sha256
+Return the SHA\-256 fingerprint of a client certificate.
+.UNINDENT
+.INDENT 7.0
+.TP
+.B attribute [R] tls_client_fingerprint_sha1
+Return the SHA\-1 fingerprint of a client certificate.
+.UNINDENT
+.INDENT 7.0
+.TP
+.B attribute [R] tls_client_issuer_name
+Return the issuer name of a client certificate.
+.UNINDENT
+.INDENT 7.0
+.TP
+.B attribute [R] tls_client_subject_name
+Return the subject name of a client certificate.
+.UNINDENT
+.INDENT 7.0
+.TP
+.B attribute [R] tls_client_serial
+Return the serial number of a client certificate.
+.UNINDENT
+.INDENT 7.0
+.TP
+.B attribute [R] tls_client_not_before
+Return the start date of a client certificate in seconds since
+the epoch.
+.UNINDENT
+.INDENT 7.0
+.TP
+.B attribute [R] tls_client_not_after
+Return the end date of a client certificate in seconds since
+the epoch.
+.UNINDENT
+.INDENT 7.0
+.TP
+.B attribute [R] tls_cipher
+Return a TLS cipher negotiated in this connection.
+.UNINDENT
+.INDENT 7.0
+.TP
+.B attribute [R] tls_protocol
+Return a TLS protocol version negotiated in this connection.
+.UNINDENT
+.INDENT 7.0
+.TP
+.B attribute [R] tls_session_id
+Return a session ID for this connection in hex string.
+.UNINDENT
+.INDENT 7.0
+.TP
+.B attribute [R] tls_session_reused
+Return true if, and only if a SSL/TLS session is reused.
+.UNINDENT
+.INDENT 7.0
+.TP
+.B attribute [R] alpn
+Return ALPN identifier negotiated in this connection.
+.UNINDENT
 .UNINDENT
 .INDENT 0.0
 .TP
@@ -2146,7 +2350,7 @@ The replacement is done instantly without breaking existing
 connections or requests.  It also avoids any process creation as is
 the case with hot swapping with signals.
 .sp
-The one limitation is that only numeric IP address is allowd in
+The one limitation is that only numeric IP address is allowed in
 \fI\%backend\fP in request body unless "dns" parameter
 is used while non numeric hostname is allowed in command\-line or
 configuration file is read using \fI\%\-\-conf\fP\&.
index e433b9b..d040cbf 100644 (file)
@@ -14,7 +14,7 @@ SYNOPSIS
 DESCRIPTION
 -----------
 
-A reverse proxy for HTTP/2, HTTP/1 and SPDY.
+A reverse proxy for HTTP/2, and HTTP/1.
 
 .. describe:: <PRIVATE_KEY>
 
@@ -46,8 +46,7 @@ Connections
     with "unix:" (e.g., unix:/var/run/backend.sock).
 
     Optionally, if <PATTERN>s are given, the backend address
-    is  only  used  if  request  matches  the  pattern.   If
-    :option:`--http2-proxy`  is  used,  <PATTERN>s are  ignored.   The
+    is  only  used  if  request matches  the  pattern.   The
     pattern  matching is  closely  designed  to ServeMux  in
     net/http package of  Go programming language.  <PATTERN>
     consists of  path, host +  path or just host.   The path
@@ -58,11 +57,16 @@ Connections
     which  only  lacks  trailing  '*/*'  (e.g.,  path  "*/foo/*"
     matches request path  "*/foo*").  If it does  not end with
     "*/*", it  performs exact match against  the request path.
-    If host  is given, it  performs exact match  against the
-    request host.  If  host alone is given,  "*/*" is appended
-    to it,  so that it  matches all request paths  under the
-    host   (e.g.,   specifying   "nghttp2.org"   equals   to
-    "nghttp2.org/").
+    If  host  is given,  it  performs  a match  against  the
+    request host.   For a  request received on  the frontend
+    listener with  "sni-fwd" parameter enabled, SNI  host is
+    used instead of a request host.  If host alone is given,
+    "*/*" is  appended to it,  so that it matches  all request
+    paths  under the  host  (e.g., specifying  "nghttp2.org"
+    equals  to "nghttp2.org/").   CONNECT method  is treated
+    specially.  It  does not have  path, and we  don't allow
+    empty path.  To workaround  this, we assume that CONNECT
+    method has "*/*" as path.
 
     Patterns with  host take  precedence over  patterns with
     just path.   Then, longer patterns take  precedence over
@@ -76,6 +80,18 @@ Connections
     match  against  "nghttp2.org".   The exact  hosts  match
     takes precedence over the wildcard hosts match.
 
+    If path  part ends with  "\*", it is treated  as wildcard
+    path.  The  wildcard path  behaves differently  from the
+    normal path.  For normal path,  match is made around the
+    boundary of path component  separator,"*/*".  On the other
+    hand, the wildcard  path does not take  into account the
+    path component  separator.  All paths which  include the
+    wildcard  path  without  last  "\*" as  prefix,  and  are
+    strictly longer than wildcard  path without last "\*" are
+    matched.  "\*"  must match  at least one  character.  For
+    example,  the   pattern  "*/foo\**"  matches   "*/foo/*"  and
+    "*/foobar*".  But it does not match "*/foo*", or "*/fo*".
+
     If <PATTERN> is omitted or  empty string, "*/*" is used as
     pattern,  which  matches  all request  paths  (catch-all
     pattern).  The catch-all backend must be given.
@@ -148,16 +164,32 @@ Connections
     The     session     affinity    is     enabled     using
     "affinity=<METHOD>"  parameter.   If  "ip" is  given  in
     <METHOD>, client  IP based session affinity  is enabled.
-    If  "none" is  given  in <METHOD>,  session affinity  is
-    disabled, and this is the default.  The session affinity
-    is enabled per  <PATTERN>.  If at least  one backend has
-    "affinity" parameter,  and its  <METHOD> is  not "none",
-    session  affinity is  enabled  for  all backend  servers
-    sharing  the  same  <PATTERN>.   It is  advised  to  set
-    "affinity"  parameter  to   all  backend  explicitly  if
-    session affinity  is desired.  The session  affinity may
-    break if one of the backend gets unreachable, or backend
-    settings are reloaded or replaced by API.
+    If "cookie"  is given in <METHOD>,  cookie based session
+    affinity is  enabled.  If  "none" is given  in <METHOD>,
+    session affinity  is disabled, and this  is the default.
+    The session  affinity is  enabled per <PATTERN>.   If at
+    least  one backend  has  "affinity"  parameter, and  its
+    <METHOD> is not "none",  session affinity is enabled for
+    all backend  servers sharing the same  <PATTERN>.  It is
+    advised  to  set  "affinity" parameter  to  all  backend
+    explicitly if session affinity  is desired.  The session
+    affinity  may   break  if   one  of  the   backend  gets
+    unreachable,  or   backend  settings  are   reloaded  or
+    replaced by API.
+
+    If   "affinity=cookie"    is   used,    the   additional
+    configuration                is                required.
+    "affinity-cookie-name=<NAME>" must be  used to specify a
+    name     of     cookie      to     use.      Optionally,
+    "affinity-cookie-path=<PATH>" can  be used to  specify a
+    path   which   cookie    is   applied.    The   optional
+    "affinity-cookie-secure=<SECURE>"  controls  the  Secure
+    attribute of a cookie.  The default value is "auto", and
+    the Secure attribute is  determined by a request scheme.
+    If a request scheme is "https", then Secure attribute is
+    set.  Otherwise, it  is not set.  If  <SECURE> is "yes",
+    the  Secure attribute  is  always set.   If <SECURE>  is
+    "no", the Secure attribute is always omitted.
 
     By default, name resolution of backend host name is done
     at  start  up,  or reloading  configuration.   If  "dns"
@@ -180,6 +212,13 @@ Connections
     "redirect-if-no-tls"    parameter   to    all   backends
     explicitly if this feature is desired.
 
+    If "upgrade-scheme"  parameter is used along  with "tls"
+    parameter, HTTP/2 :scheme pseudo header field is changed
+    to "https" from "http" when forwarding a request to this
+    particular backend.  This is  a workaround for a backend
+    server  which  requires  "https" :scheme  pseudo  header
+    field on TLS encrypted connection.
+
     Since ";" and ":" are  used as delimiter, <PATTERN> must
     not  contain these  characters.  Since  ";" has  special
     meaning in shell, the option value must be quoted.
@@ -203,6 +242,11 @@ Connections
     Optionally, TLS  can be disabled by  specifying "no-tls"
     parameter.  TLS is enabled by default.
 
+    If "sni-fwd" parameter is  used, when performing a match
+    to select a backend server,  SNI host name received from
+    the client  is used  instead of  the request  host.  See
+    :option:`--backend` option about the pattern match.
+
     To  make this  frontend as  API endpoint,  specify "api"
     parameter.   This   is  disabled  by  default.    It  is
     important  to  limit the  access  to  the API  frontend.
@@ -403,8 +447,7 @@ Timeout
 
 .. option:: --frontend-http2-read-timeout=<DURATION>
 
-    Specify  read  timeout  for  HTTP/2  and  SPDY  frontend
-    connection.
+    Specify read timeout for HTTP/2 frontend connection.
 
     Default: ``3m``
 
@@ -429,15 +472,15 @@ Timeout
 
 .. option:: --stream-read-timeout=<DURATION>
 
-    Specify  read timeout  for HTTP/2  and SPDY  streams.  0
-    means no timeout.
+    Specify  read timeout  for HTTP/2  streams.  0  means no
+    timeout.
 
     Default: ``0``
 
 .. option:: --stream-write-timeout=<DURATION>
 
-    Specify write  timeout for  HTTP/2 and SPDY  streams.  0
-    means no timeout.
+    Specify write  timeout for  HTTP/2 streams.  0  means no
+    timeout.
 
     Default: ``1m``
 
@@ -511,14 +554,14 @@ SSL/TLS
     Set allowed  cipher list  for frontend  connection.  The
     format of the string is described in OpenSSL ciphers(1).
 
-    Default: ``ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA:ECDHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA256:DHE-RSA-AES256-SHA:ECDHE-ECDSA-DES-CBC3-SHA:ECDHE-RSA-DES-CBC3-SHA:EDH-RSA-DES-CBC3-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:DES-CBC3-SHA:!DSS``
+    Default: ``ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256``
 
 .. option:: --client-ciphers=<SUITE>
 
     Set  allowed cipher  list for  backend connection.   The
     format of the string is described in OpenSSL ciphers(1).
 
-    Default: ``ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA:ECDHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA256:DHE-RSA-AES256-SHA:ECDHE-ECDSA-DES-CBC3-SHA:ECDHE-RSA-DES-CBC3-SHA:EDH-RSA-DES-CBC3-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:DES-CBC3-SHA:!DSS``
+    Default: ``ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256``
 
 .. option:: --ecdh-curves=<LIST>
 
@@ -537,11 +580,14 @@ SSL/TLS
 
 .. option:: --cacert=<PATH>
 
-    Set path to trusted CA  certificate file used in backend
-    TLS connections.   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.
+    Set path to trusted CA  certificate file.  It is used in
+    backend  TLS connections  to verify  peer's certificate.
+    It is also used to  verify OCSP response from the script
+    set by :option:`--fetch-ocsp-response-file`\.  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>
 
@@ -554,12 +600,12 @@ SSL/TLS
     Specify  additional certificate  and  private key  file.
     nghttpx will  choose certificates based on  the hostname
     indicated by client using TLS SNI extension.  If nghttpx
-    is  built with  OpenSSL >=  1.0.2, signature  algorithms
-    (e.g., ECDSA+SHA256, RSA+SHA256) presented by client are
-    also taken  into consideration.  This allows  nghttpx to
-    send ECDSA certificate to  modern clients, while sending
-    RSA based certificate to older clients.  This option can
-    be  used multiple  times.  To  make OCSP  stapling work,
+    is  built with  OpenSSL  >= 1.0.2,  the shared  elliptic
+    curves (e.g., P-256) between  client and server are also
+    taken into  consideration.  This allows nghttpx  to send
+    ECDSA certificate  to modern clients, while  sending RSA
+    based certificate to older  clients.  This option can be
+    used  multiple  times.   To  make  OCSP  stapling  work,
     <CERTPATH> must be absolute path.
 
     Additional parameter  can be specified in  <PARAM>.  The
@@ -598,6 +644,13 @@ SSL/TLS
     client certificate.  The file must be in PEM format.  It
     can contain multiple certificates.
 
+.. option:: --verify-client-tolerate-expired
+
+    Accept  expired  client  certificate.   Operator  should
+    handle  the expired  client  certificate  by some  means
+    (e.g.,  mruby  script).   Otherwise, this  option  might
+    cause a security risk.
+
 .. option:: --client-private-key-file=<PATH>
 
     Path to  file that contains  client private key  used in
@@ -615,10 +668,14 @@ SSL/TLS
     :option:`--tls-min-proto-version` and  :option:`\--tls-max-proto-version` are
     enabled.  If the protocol list advertised by client does
     not  overlap  this range,  you  will  receive the  error
-    message "unknown protocol".  The available versions are:
+    message "unknown protocol".  If a protocol version lower
+    than TLSv1.2 is specified, make sure that the compatible
+    ciphers are  included in :option:`--ciphers` option.   The default
+    cipher  list  only   includes  ciphers  compatible  with
+    TLSv1.2 or above.  The available versions are:
     TLSv1.2, TLSv1.1, and TLSv1.0
 
-    Default: ``TLSv1.1``
+    Default: ``TLSv1.2``
 
 .. option:: --tls-max-proto-version=<VER>
 
@@ -734,6 +791,18 @@ SSL/TLS
 
     Default: ``4h``
 
+.. option:: --ocsp-startup
+
+    Start  accepting connections  after initial  attempts to
+    get OCSP responses  finish.  It does not  matter some of
+    the  attempts  fail.  This  feature  is  useful if  OCSP
+    responses   must    be   available    before   accepting
+    connections.
+
+.. option:: --no-verify-ocsp
+
+    nghttpx does not verify OCSP response.
+
 .. option:: --no-ocsp
 
     Disable OCSP stapling.
@@ -847,15 +916,15 @@ SSL/TLS
     option.  But be aware its implications.
 
 
-HTTP/2 and SPDY
-~~~~~~~~~~~~~~~
+HTTP/2
+~~~~~~
 
 .. option:: -c, --frontend-http2-max-concurrent-streams=<N>
 
     Set the maximum number of  the concurrent streams in one
-    frontend HTTP/2 and SPDY session.
+    frontend HTTP/2 session.
 
-    Default: `` 100``
+    Default: ``100``
 
 .. option:: --backend-http2-max-concurrent-streams=<N>
 
@@ -868,16 +937,15 @@ HTTP/2 and SPDY
 
 .. option:: --frontend-http2-window-size=<SIZE>
 
-    Sets the  per-stream initial  window size of  HTTP/2 and
-    SPDY frontend connection.
+    Sets  the  per-stream  initial  window  size  of  HTTP/2
+    frontend connection.
 
     Default: ``65535``
 
 .. option:: --frontend-http2-connection-window-size=<SIZE>
 
-    Sets the  per-connection window size of  HTTP/2 and SPDY
-    frontend  connection.  For  SPDY  connection, the  value
-    less than 64KiB is rounded up to 64KiB.
+    Sets the  per-connection window size of  HTTP/2 frontend
+    connection.
 
     Default: ``65535``
 
@@ -913,8 +981,7 @@ HTTP/2 and SPDY
     It is  also supported if  both frontend and  backend are
     HTTP/2 in default mode.  In  this case, server push from
     backend session is relayed  to frontend, and server push
-    via Link header field  is also supported.  SPDY frontend
-    does not support server push.
+    via Link header field is also supported.
 
 .. option:: --frontend-http2-optimize-write-buffer-size
 
@@ -982,7 +1049,7 @@ Mode
 .. describe:: (default mode)
 
     
-    Accept HTTP/2, SPDY and HTTP/1.1 over SSL/TLS.  "no-tls"
+    Accept  HTTP/2,  and  HTTP/1.1 over  SSL/TLS.   "no-tls"
     parameter is  used in  :option:`--frontend` option,  accept HTTP/2
     and HTTP/1.1 over cleartext  TCP.  The incoming HTTP/1.1
     connection  can  be  upgraded  to  HTTP/2  through  HTTP
@@ -1037,11 +1104,22 @@ Logging
     * $alpn: ALPN identifier of the protocol which generates
       the response.   For HTTP/1,  ALPN is  always http/1.1,
       regardless of minor version.
-    * $ssl_cipher: cipher used for SSL/TLS connection.
-    * $ssl_protocol: protocol for SSL/TLS connection.
-    * $ssl_session_id: session ID for SSL/TLS connection.
-    * $ssl_session_reused:  "r"   if  SSL/TLS   session  was
+    * $tls_cipher: cipher used for SSL/TLS connection.
+    * $tls_client_fingerprint_sha256: SHA-256 fingerprint of
+      client certificate.
+    * $tls_client_fingerprint_sha1:  SHA-1   fingerprint  of
+      client certificate.
+    * $tls_client_subject_name:   subject  name   in  client
+      certificate.
+    * $tls_client_issuer_name:   issuer   name   in   client
+      certificate.
+    * $tls_client_serial:    serial    number   in    client
+      certificate.
+    * $tls_protocol: protocol for SSL/TLS connection.
+    * $tls_session_id: session ID for SSL/TLS connection.
+    * $tls_session_reused:  "r"   if  SSL/TLS   session  was
       reused.  Otherwise, "."
+    * $tls_sni: SNI server name for SSL/TLS connection.
     * $backend_host:  backend  host   used  to  fulfill  the
       request.  "-" if backend host is not available.
     * $backend_port:  backend  port   used  to  fulfill  the
@@ -1092,6 +1170,19 @@ HTTP
     Strip X-Forwarded-For  header field from  inbound client
     requests.
 
+.. option:: --no-add-x-forwarded-proto
+
+    Don't append  additional X-Forwarded-Proto  header field
+    to  the   backend  request.   If  inbound   client  sets
+    X-Forwarded-Proto,                                   and
+    :option:`--no-strip-incoming-x-forwarded-proto`  option  is  used,
+    they are passed to the backend.
+
+.. option:: --no-strip-incoming-x-forwarded-proto
+
+    Don't strip X-Forwarded-Proto  header field from inbound
+    client requests.
+
 .. option:: --add-forwarded=<LIST>
 
     Append RFC  7239 Forwarded header field  with parameters
@@ -1248,7 +1339,7 @@ API
 
     Set the maximum size of request body for API request.
 
-    Default: ``16K``
+    Default: ``32M``
 
 
 DNS
@@ -1330,6 +1421,16 @@ Process
     Run this program as <USER>.   This option is intended to
     be used to drop root privileges.
 
+.. option:: --single-process
+
+    Run this program in a  single process mode for debugging
+    purpose.  Without this option,  nghttpx creates at least
+    2  processes:  master  and worker  processes.   If  this
+    option is  used, master  and worker  are unified  into a
+    single process.  nghttpx still spawns additional process
+    if neverbleed is used.  In  the single process mode, the
+    signal handling feature is disabled.
+
 
 Scripting
 ~~~~~~~~~
@@ -1344,7 +1445,9 @@ Misc
 
 .. option:: --conf=<PATH>
 
-    Load configuration from <PATH>.
+    Load  configuration  from   <PATH>.   Please  note  that
+    nghttpx always  tries to read the  default configuration
+    file if :option:`--conf` is not given.
 
     Default: ``/etc/nghttpx/nghttpx.conf``
 
@@ -1424,7 +1527,7 @@ Error log
   <datetime> <master-pid> <current-pid> <thread-id> <level> (<filename>:<line>) <msg>
 
   <datetime>
-    It is a conbination of date and time when the log is written.  It
+    It is a combination of date and time when the log is written.  It
     is in ISO 8601 format.
 
   <master-pid>
@@ -1546,6 +1649,22 @@ be customized using :option:`--fetch-ocsp-response-file` option.
 If OCSP query is failed, previous OCSP response, if any, is continued
 to be used.
 
+:option:`--fetch-ocsp-response-file` option provides wide range of
+possibility to manage OCSP response.  It can take an arbitrary script
+or executable.  The requirement is that it supports the command-line
+interface of ``fetch-ocsp-response`` script, and it must return a
+valid DER encoded OCSP response on success.  It must return exit code
+0 on success, and 75 for temporary error, and the other error code for
+generic failure.  For large cluster of servers, it is not efficient
+for each server to perform OCSP query using ``fetch-ocsp-response``.
+Instead, you can retrieve OCSP response in some way, and store it in a
+disk or a shared database.  Then specify a program in
+:option:`--fetch-ocsp-response-file` to fetch it from those stores.
+This could provide a way to share the OCSP response between fleet of
+servers, and also any OCSP query strategy can be applied which may be
+beyond the ability of nghttpx itself or ``fetch-ocsp-response``
+script.
+
 TLS SESSION RESUMPTION
 ----------------------
 
@@ -1559,7 +1678,7 @@ By default, session ID is shared by all worker threads.
 
 If :option:`--tls-session-cache-memcached` is given, nghttpx will
 insert serialized session data to memcached with
-``nghttpx:tls-session-cache:`` + lowercased hex string of session ID
+``nghttpx:tls-session-cache:`` + lowercase hex string of session ID
 as a memcached entry key, with expiry time 12 hours.  Session timeout
 is set to 12 hours.
 
@@ -1641,6 +1760,14 @@ MRUBY SCRIPTING
   The current mruby extension API is experimental and not frozen.  The
   API is subject to change in the future release.
 
+.. warning::
+
+  Almost all string value returned from method, or attribute is a
+  fresh new mruby string, which involves memory allocation, and
+  copies.  Therefore, it is strongly recommended to store a return
+  value in a local variable, and use it, instead of calling method or
+  accessing attribute repeatedly.
+
 nghttpx allows users to extend its capability using mruby scripts.
 nghttpx has 2 hook points to execute mruby script: request phase and
 response phase.  The request phase hook is invoked after all request
@@ -1687,7 +1814,7 @@ respectively.
     .. rb:attr_reader:: ctx
 
         Return Ruby hash object.  It persists until request finishes.
-        So values set in request phase hoo can be retrieved in
+        So values set in request phase hook can be retrieved in
         response phase hook.
 
     .. rb:attr_reader:: phase
@@ -1719,6 +1846,56 @@ respectively.
 
         Return the TLS SNI value which client sent in this connection.
 
+    .. rb:attr_reader:: tls_client_fingerprint_sha256
+
+        Return the SHA-256 fingerprint of a client certificate.
+
+    .. rb:attr_reader:: tls_client_fingerprint_sha1
+
+        Return the SHA-1 fingerprint of a client certificate.
+
+    .. rb:attr_reader:: tls_client_issuer_name
+
+        Return the issuer name of a client certificate.
+
+    .. rb:attr_reader:: tls_client_subject_name
+
+        Return the subject name of a client certificate.
+
+    .. rb:attr_reader:: tls_client_serial
+
+        Return the serial number of a client certificate.
+
+    .. rb:attr_reader:: tls_client_not_before
+
+        Return the start date of a client certificate in seconds since
+        the epoch.
+
+    .. rb:attr_reader:: tls_client_not_after
+
+        Return the end date of a client certificate in seconds since
+        the epoch.
+
+    .. rb:attr_reader:: tls_cipher
+
+        Return a TLS cipher negotiated in this connection.
+
+    .. rb:attr_reader:: tls_protocol
+
+        Return a TLS protocol version negotiated in this connection.
+
+    .. rb:attr_reader:: tls_session_id
+
+        Return a session ID for this connection in hex string.
+
+    .. rb:attr_reader:: tls_session_reused
+
+        Return true if, and only if a SSL/TLS session is reused.
+
+    .. rb:attr_reader:: alpn
+
+        Return ALPN identifier negotiated in this connection.
+
 .. rb:class:: Request
 
     Object to represent request from client.  The modification to
@@ -1963,7 +2140,7 @@ The replacement is done instantly without breaking existing
 connections or requests.  It also avoids any process creation as is
 the case with hot swapping with signals.
 
-The one limitation is that only numeric IP address is allowd in
+The one limitation is that only numeric IP address is allowed in
 :option:`backend <--backend>` in request body unless "dns" parameter
 is used while non numeric hostname is allowed in command-line or
 configuration file is read using :option:`--conf`.
index e03b7a1..e5aa7ee 100644 (file)
@@ -116,7 +116,10 @@ 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()`.
+validations, use `nghttp2_option_set_no_http_messaging()`.  Please
+note that disabling this feature does not change the fundamental
+client and server model of HTTP.  That is, even if the validation is
+disabled, only client can send requests.
 
 For HTTP request, including those carried by PUSH_PROMISE, HTTP
 message starts with one HEADERS frame containing request headers.  It
@@ -149,13 +152,11 @@ header fields must not appear: "Connection", "Keep-Alive",
 Each header field name and value must obey the field-name and
 field-value production rules described in `RFC 7230, section
 3.2. <https://tools.ietf.org/html/rfc7230#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.
+Additionally, all field name must be lower cased.  The invalid header
+fields are treated as stream error, and that stream is reset.  If
+application wants to treat these headers in their own way, use
+`nghttp2_on_invalid_header_callback
+<https://nghttp2.org/documentation/types.html#c.nghttp2_on_invalid_header_callback>`_.
 
 For "http" or "https" URIs, ":path" pseudo header fields must start
 with "/".  The only exception is OPTIONS request, in that case, "*" is
index c5e58b2..87b16c7 100644 (file)
@@ -2,7 +2,7 @@ Building Android binary
 =======================
 
 In this article, we briefly describe how to build Android binary using
-`Android NDK <http://developer.android.com/tools/sdk/ndk/index.html>`_
+`Android NDK <https://developer.android.com/ndk/index.html>`_
 cross-compiler on Debian Linux.
 
 The easiest way to build android binary is use Dockerfile.android.
index c2d301d..ffb2911 100644 (file)
@@ -26,8 +26,7 @@ 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.9.
+between versions, we currently use clang-format-5.0.
 
 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.
@@ -35,7 +34,7 @@ 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.9 in debian), either add it to PATH variable or
+clang-format-diff-5.0 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.
index c89dcfe..133f900 100644 (file)
@@ -3,16 +3,14 @@
 h2load - HTTP/2 benchmarking tool - HOW-TO
 ==========================================
 
-:doc:`h2load.1` is benchmarking tool for HTTP/2 and HTTP/1.1.  If
-built with spdylay (http://tatsuhiro-t.github.io/spdylay/) library, it
-also supports SPDY protocol.  It supports SSL/TLS and clear text for
-all supported protocols.
+:doc:`h2load.1` is benchmarking tool for HTTP/2 and HTTP/1.1.  It
+supports SSL/TLS and clear text for all supported protocols.
 
 Compiling from source
 ---------------------
 
 h2load is compiled alongside nghttp2 and requires that the
-``--enable-apps`` flag is passed to ``./configure`` and `required
+``--enable-app`` flag is passed to ``./configure`` and `required
 dependencies <https://github.com/nghttp2/nghttp2#requirements>`_ are
 available during compilation. For details on compiling, see `nghttp2:
 Building from Git
@@ -64,23 +62,40 @@ The benchmarking result looks like this:
 See the h2load manual page :ref:`h2load-1-output` section for the
 explanation of the above numbers.
 
+Timing-based load-testing
+-------------------------
+
+As of v1.26.0, h2load supports timing-based load-testing.  This method
+performs load-testing in terms of a given duration instead of a
+pre-defined number of requests. The new option :option:`--duration`
+specifies how long the load-testing takes. For example,
+``--duration=10`` makes h2load perform load-testing against a server
+for 10 seconds. You can also specify a “warming-up” period with
+:option:`--warm-up-time`. If :option:`--duration` is used,
+:option:`-n` option is ignored.
+
+The following command performs load-testing for 10 seconds after 5
+seconds warming up period:
+
+.. code-block:: text
+
+    $ h2load -c100 -m100 --duration=10 --warm-up-time=5 https://localhost
+
 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:
+HTTP/2 has 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:
 
 :option:`-w`
    Sets  the stream  level  initial  window size  to
-   (2**<N>)-1.  For SPDY, 2**<N> is used instead.
+   (2**<N>)-1.
 
 :option:`-W`
    Sets the connection level  initial window size to
-   (2**<N>)-1.  For  SPDY, if  <N> is  strictly less
-   than  16,  this  option  is  ignored.   Otherwise
-   2**<N> is used for SPDY.
+   (2**<N>)-1.
 
 Multi-Threading
 ---------------
index 76999f9..2f348e7 100644 (file)
@@ -290,7 +290,7 @@ 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
+Receive server push and enable SSL/TLS
 ++++++++++++++++++++++++++++++++++++++
 
 .. code-block:: cpp
index e7549de..6acb369 100644 (file)
@@ -4,10 +4,10 @@ nghttpx - HTTP/2 proxy - HOW-TO
 ===============================
 
 :doc:`nghttpx.1` 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.
+other protocols (e.g., HTTP/1).  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
 ------------
@@ -15,9 +15,7 @@ Default mode
 If nghttpx is invoked without :option:`--http2-proxy`, it operates in
 default mode.  In this mode, it works as reverse proxy (gateway) for
 both HTTP/2 and HTTP/1 clients to backend servers.  This is also known
-as "HTTP/2 router".  If nghttpx is linked with spdylay library and
-frontend connection is SSL/TLS, the frontend also supports SPDY
-protocol.
+as "HTTP/2 router".
 
 By default, frontend connection is encrypted using SSL/TLS.  So
 server's private key and certificate must be supplied to the command
@@ -25,11 +23,10 @@ line (or through configuration file).  In this case, the frontend
 protocol selection will be done via ALPN or NPN.
 
 To turn off encryption on frontend connection, use ``no-tls`` keyword
-in :option:`--frontend` option.  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 an 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.
+in :option:`--frontend` option.  HTTP/2 and HTTP/1 are available on
+the frontend, and an 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.
 
 nghttpx can listen on multiple frontend addresses.  This is achieved
 by using multiple :option:`--frontend` options.  For each frontend
@@ -45,17 +42,17 @@ that default backend protocol is HTTP/1.1.  To use HTTP/2 in backend,
 you have to specify ``h2`` in ``proto`` keyword in :option:`--backend`
 explicitly.
 
-The backend is supposed to be Web server.  For example, to make
+The backend is supposed to be Web server.  For example, to make
 nghttpx listen to encrypted HTTP/2 requests at port 8443, and a
-backend Web server is configured to listen to HTTP request at port
-8080 in the same host, run nghttpx command-line like this:
+backend Web server is configured to listen to HTTP requests at port
+8080 on the same host, run nghttpx command-line like this:
 
 .. code-block:: text
 
     $ 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:
+Then an HTTP/2 enabled client can access the nghttpx server using HTTP/2.  For
+example, you can send a GET request using nghttp:
 
 .. code-block:: text
 
@@ -66,19 +63,18 @@ HTTP/2 proxy mode
 
 If nghttpx is invoked with :option:`--http2-proxy` (or its shorthand
 :option:`-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 proxy server (e.g., Squid, Apache Traffic
-Server).  HTTP/1 request must include absolute URI in request line.
+protocols in frontend and backend connections are the same as in `default
+mode`_.  The difference is that this mode acts like forward proxy and
+assumes the backend is an HTTP proxy server (e.g., Squid, Apache Traffic
+Server).  HTTP/1 requests must include an 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.
+By default, the frontend connection is encrypted.  So this mode is
+also called secure proxy.
 
-To turn off encryption on frontend connection, use ``no-tls`` keyword
+To turn off encryption on the frontend connection, use ``no-tls`` keyword
 in :option:`--frontend` option.
 
-The backend must be HTTP proxy server.  nghttpx supports multiple
+The backend must be an HTTP proxy server.  nghttpx supports multiple
 backend server addresses.  It translates incoming requests to HTTP
 request to backend server.  The backend server performs real proxy
 work for each request, for example, dispatching requests to the origin
@@ -92,7 +88,7 @@ connection, use :option:`--backend` option, and specify ``h2`` in
 
 For example, to make nghttpx listen to encrypted HTTP/2 requests at
 port 8443, and a backend HTTP proxy server is configured to listen to
-HTTP/1 request at port 8080 in the same host, run nghttpx command-line
+HTTP/1 requests at port 8080 on the same host, run nghttpx command-line
 like this:
 
 .. code-block:: text
@@ -102,8 +98,8 @@ like this:
 At the time of this writing, Firefox 41 and Chromium v46 can use
 nghttpx as HTTP/2 proxy.
 
-To make Firefox or Chromium use nghttpx as HTTP/2 or SPDY proxy, user
-has to create proxy.pac script file like this:
+To make Firefox or Chromium use nghttpx as HTTP/2 proxy, user has to
+create proxy.pac script file like this:
 
 .. code-block:: javascript
 
@@ -297,13 +293,31 @@ When you write this option in command-line, you should enclose
 argument with single or double quotes, since the character ``;`` has a
 special meaning in shell.
 
-To route, request to request path whose prefix is ``/foo`` to backend
-server ``[::1]:8080``, you can write like so:
+To route, request to request path ``/foo`` to backend server
+``[::1]:8080``, you can write like so:
 
 .. code-block:: text
 
    backend=::1,8080;/foo
 
+If the last character of path pattern is ``/``, all request paths
+which start with that pattern match:
+
+.. code-block:: text
+
+   backend=::1,8080;/bar/
+
+The request path ``/bar/buzz`` matches the ``/bar/``.
+
+You can use ``*`` at the end of the path pattern to make it wildcard
+pattern.  ``*`` must match at least one character:
+
+.. code-block:: text
+
+   backend=::1,8080;/sample*
+
+The request path ``/sample1/foo`` matches the ``/sample*`` pattern.
+
 Of course, you can specify both host and request path at the same
 time:
 
@@ -371,7 +385,7 @@ parameter in :option:`--backend` option, like so:
 
 .. code-block:: text
 
-   backend=foo.example.com;;dns
+   backend=foo.example.com,80;;dns
 
 nghttpx will cache resolved addresses for certain period of time.  To
 change this cache period, use :option:`--dns-cache-timeout`.
@@ -387,6 +401,28 @@ like so:
 
    frontend=*,443;proxyproto
 
+Session affinity
+----------------
+
+Two kinds of session affinity are available: client IP, and HTTP
+Cookie.
+
+To enable client IP based affinity, specify ``affinity=ip`` parameter
+in :option:`--backend` option.  If PROXY protocol is enabled, then an
+address obtained from PROXY protocol is taken into consideration.
+
+To enable HTTP Cookie based affinity, specify ``affinity=cookie``
+parameter, and specify a name of cookie in ``affinity-cookie-name``
+parameter.  Optionally, a Path attribute can be specified in
+``affinity-cookie-path`` parameter:
+
+.. code-block:: text
+
+   backend=127.0.0.1,3000;;affinity=cookie;affinity-cookie-name=nghttpxlb;affinity-cookie-path=/
+
+Secure attribute of cookie is set if client connection is protected by
+TLS.
+
 PSK cipher suites
 -----------------
 
index 2262f67..46fc34b 100644 (file)
@@ -124,6 +124,7 @@ remote server. It's defined as::
       bev = bufferevent_openssl_socket_new(
           evbase, -1, ssl, BUFFEREVENT_SSL_CONNECTING,
           BEV_OPT_DEFER_CALLBACKS | BEV_OPT_CLOSE_ON_FREE);
+      bufferevent_enable(bev, EV_READ | EV_WRITE);
       bufferevent_setcb(bev, readcb, writecb, eventcb, session_data);
       rv = bufferevent_socket_connect_hostname(bev, session_data->dnsbase,
                                                AF_UNSPEC, host, port);
index ff08813..5a78fde 100644 (file)
@@ -848,11 +848,12 @@ Types (structs, unions and typedefs)
     The parameter and behaviour are similar to
     :type:`nghttp2_on_header_callback`.  The difference is that this
     callback is only invoked when a invalid header name/value pair is
-    received which is silently ignored if this callback is not set.
-    Only invalid regular header field are passed to this callback.  In
-    other words, invalid pseudo header field is not passed to this
-    callback.  Also header fields which includes upper cased latter are
-    also treated as error without passing them to this callback.
+    received which is treated as stream error if this callback is not
+    set.  Only invalid regular header field are passed to this
+    callback.  In other words, invalid pseudo header field is not
+    passed to this callback.  Also header fields which includes upper
+    cased latter are also treated as error without passing them to this
+    callback.
     
     This callback is only considered if HTTP messaging validation is
     turned on (which is on by default, see
@@ -861,10 +862,13 @@ Types (structs, unions and typedefs)
     With this callback, application inspects the incoming invalid
     field, and it also can reset stream from this callback by returning
     :macro:`NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE`.  By default, the
-    error code is :macro:`NGHTTP2_INTERNAL_ERROR`.  To change the error
+    error code is :macro:`NGHTTP2_PROTOCOL_ERROR`.  To change the error
     code, call `nghttp2_submit_rst_stream()` with the error code of
     choice in addition to returning
     :macro:`NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE`.
+    
+    If 0 is returned, the header field is ignored, and the stream is
+    not reset.
 .. type:: typedef int (*nghttp2_on_invalid_header_callback2)( nghttp2_session *session, const nghttp2_frame *frame, nghttp2_rcbuf *name, nghttp2_rcbuf *value, uint8_t flags, void *user_data)
 
     
@@ -1036,6 +1040,31 @@ Types (structs, unions and typedefs)
     of length *len*.  *len* does not include the sentinel NULL
     character.
     
+    This function is deprecated.  The new application should use
+    :type:`nghttp2_error_callback2`.
+    
+    The format of error message may change between nghttp2 library
+    versions.  The application should not depend on the particular
+    format.
+    
+    Normally, application should return 0 from this callback.  If fatal
+    error occurred while doing something in this callback, application
+    should return :macro:`NGHTTP2_ERR_CALLBACK_FAILURE`.  In this case,
+    library will return immediately with return value
+    :macro:`NGHTTP2_ERR_CALLBACK_FAILURE`.  Currently, if nonzero value
+    is returned from this callback, they are treated as
+    :macro:`NGHTTP2_ERR_CALLBACK_FAILURE`, but application should not
+    rely on this details.
+.. type:: typedef int (*nghttp2_error_callback2)(nghttp2_session *session, int lib_error_code, const char *msg, size_t len, void *user_data)
+
+    
+    Callback function invoked when library provides the error code, and
+    message.  This callback is solely for debugging purpose.
+    *lib_error_code* is one of error code defined in
+    :macro:`nghttp2_error`.  The *msg* is typically NULL-terminated
+    string of length *len*, and intended for human consumption.  *len*
+    does not include the sentinel NULL character.
+    
     The format of error message may change between nghttp2 library
     versions.  The application should not depend on the particular
     format.
index 962066a..9795ad7 100644 (file)
@@ -7,11 +7,8 @@ if(ENABLE_EXAMPLES)
     COMPILE_FLAGS "${WARNCXXFLAGS} ${CXX1XCXXFLAGS}")
 
   include_directories(
-    ${CMAKE_SOURCE_DIR}
-    ${CMAKE_SOURCE_DIR}/lib/includes
-    ${CMAKE_BINARY_DIR}/lib/includes
-    ${CMAKE_SOURCE_DIR}/src/includes
-    ${CMAKE_SOURCE_DIR}/third-party
+    ${CMAKE_CURRENT_SOURCE_DIR}
+    "${CMAKE_CURRENT_SOURCE_DIR}/../third-party"
 
     ${LIBEVENT_INCLUDE_DIRS}
     ${OPENSSL_INCLUDE_DIRS}
index 776d39d..2638a45 100644 (file)
@@ -62,11 +62,11 @@ 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 \
+       @OPENSSL_LIBS@ \
        ${BOOST_LDFLAGS} \
        ${BOOST_ASIO_LIB} \
        ${BOOST_THREAD_LIB} \
        ${BOOST_SYSTEM_LIB} \
-       @OPENSSL_LIBS@ \
        @APPLDFLAGS@
 
 asio_sv_SOURCES = asio-sv.cc
index fd30b95..57dbd55 100644 (file)
@@ -1,7 +1,7 @@
-# Makefile.in generated by automake 1.15 from Makefile.am.
+# Makefile.in generated by automake 1.15.1 from Makefile.am.
 # @configure_input@
 
-# Copyright (C) 1994-2014 Free Software Foundation, Inc.
+# Copyright (C) 1994-2017 Free Software Foundation, Inc.
 
 # This Makefile.in is free software; the Free Software Foundation
 # gives unlimited permission to copy and/or distribute it,
@@ -349,8 +349,6 @@ LIBMRUBY_CFLAGS = @LIBMRUBY_CFLAGS@
 LIBMRUBY_LIBS = @LIBMRUBY_LIBS@
 LIBOBJS = @LIBOBJS@
 LIBS = @LIBS@
-LIBSPDYLAY_CFLAGS = @LIBSPDYLAY_CFLAGS@
-LIBSPDYLAY_LIBS = @LIBSPDYLAY_LIBS@
 LIBTOOL = @LIBTOOL@
 LIBXML2_CFLAGS = @LIBXML2_CFLAGS@
 LIBXML2_LIBS = @LIBXML2_LIBS@
@@ -499,11 +497,11 @@ EXTRA_DIST = CMakeLists.txt
 @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@   @OPENSSL_LIBS@ \
 @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
index 953e71a..bb6f181 100644 (file)
@@ -159,10 +159,13 @@ static void diec(const char *func, int error_code) {
  * 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) {
+static ssize_t send_callback(nghttp2_session *session, const uint8_t *data,
+                             size_t length, int flags, void *user_data) {
   struct Connection *connection;
   int rv;
+  (void)session;
+  (void)flags;
+
   connection = (struct Connection *)user_data;
   connection->want_io = IO_NONE;
   ERR_clear_error();
@@ -186,10 +189,13 @@ static ssize_t send_callback(nghttp2_session *session _U_, const uint8_t *data,
  * |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) {
+static ssize_t recv_callback(nghttp2_session *session, uint8_t *buf,
+                             size_t length, int flags, void *user_data) {
   struct Connection *connection;
   int rv;
+  (void)session;
+  (void)flags;
+
   connection = (struct Connection *)user_data;
   connection->want_io = IO_NONE;
   ERR_clear_error();
@@ -210,9 +216,10 @@ static ssize_t recv_callback(nghttp2_session *session _U_, uint8_t *buf,
 }
 
 static int on_frame_send_callback(nghttp2_session *session,
-                                  const nghttp2_frame *frame,
-                                  void *user_data _U_) {
+                                  const nghttp2_frame *frame, void *user_data) {
   size_t i;
+  (void)user_data;
+
   switch (frame->hd.type) {
   case NGHTTP2_HEADERS:
     if (nghttp2_session_get_stream_user_data(session, frame->hd.stream_id)) {
@@ -237,9 +244,10 @@ static int on_frame_send_callback(nghttp2_session *session,
 }
 
 static int on_frame_recv_callback(nghttp2_session *session,
-                                  const nghttp2_frame *frame,
-                                  void *user_data _U_) {
+                                  const nghttp2_frame *frame, void *user_data) {
   size_t i;
+  (void)user_data;
+
   switch (frame->hd.type) {
   case NGHTTP2_HEADERS:
     if (frame->headers.cat == NGHTTP2_HCAT_RESPONSE) {
@@ -274,9 +282,11 @@ static int on_frame_recv_callback(nghttp2_session *session,
  * 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_) {
+                                    uint32_t error_code, void *user_data) {
   struct Request *req;
+  (void)error_code;
+  (void)user_data;
+
   req = nghttp2_session_get_stream_user_data(session, stream_id);
   if (req) {
     int rv;
@@ -293,11 +303,13 @@ static int on_stream_close_callback(nghttp2_session *session, int32_t stream_id,
  * 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_) {
+static 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) {
   struct Request *req;
+  (void)flags;
+  (void)user_data;
+
   req = nghttp2_session_get_stream_user_data(session, stream_id);
   if (req) {
     printf("[INFO] C <---------------------------- S (DATA chunk)\n"
@@ -338,10 +350,13 @@ static void setup_nghttp2_callbacks(nghttp2_session_callbacks *callbacks) {
  * 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,
+static int select_next_proto_cb(SSL *ssl, unsigned char **out,
                                 unsigned char *outlen, const unsigned char *in,
-                                unsigned int inlen, void *arg _U_) {
+                                unsigned int inlen, void *arg) {
   int rv;
+  (void)ssl;
+  (void)arg;
+
   /* nghttp2_select_next_protocol() selects HTTP/2 protocol the
      nghttp2 library supports. */
   rv = nghttp2_select_next_protocol(out, outlen, in, inlen);
index 0143073..eb71362 100644 (file)
@@ -44,7 +44,7 @@ static void deflate(nghttp2_hd_deflater *deflater,
 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 main() {
   int rv;
   nghttp2_hd_deflater *deflater;
   nghttp2_hd_inflater *inflater;
index 8d0b727..bfee21e 100644 (file)
@@ -199,22 +199,27 @@ static void print_headers(FILE *f, nghttp2_nv *nva, size_t nvlen) {
 /* 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) {
+static ssize_t send_callback(nghttp2_session *session, const uint8_t *data,
+                             size_t length, int flags, void *user_data) {
   http2_session_data *session_data = (http2_session_data *)user_data;
   struct bufferevent *bev = session_data->bev;
+  (void)session;
+  (void)flags;
+
   bufferevent_write(bev, data, length);
   return (ssize_t)length;
 }
 
 /* nghttp2_on_header_callback: Called when nghttp2 library emits
    single header name/value pair. */
-static int on_header_callback(nghttp2_session *session _U_,
+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) {
+                              size_t valuelen, uint8_t flags, void *user_data) {
   http2_session_data *session_data = (http2_session_data *)user_data;
+  (void)session;
+  (void)flags;
+
   switch (frame->hd.type) {
   case NGHTTP2_HEADERS:
     if (frame->headers.cat == NGHTTP2_HCAT_RESPONSE &&
@@ -229,10 +234,12 @@ static int on_header_callback(nghttp2_session *session _U_,
 
 /* 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_,
+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;
+  (void)session;
+
   switch (frame->hd.type) {
   case NGHTTP2_HEADERS:
     if (frame->headers.cat == NGHTTP2_HCAT_RESPONSE &&
@@ -247,9 +254,11 @@ static int on_begin_headers_callback(nghttp2_session *session _U_,
 
 /* 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_,
+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;
+  (void)session;
+
   switch (frame->hd.type) {
   case NGHTTP2_HEADERS:
     if (frame->headers.cat == NGHTTP2_HCAT_RESPONSE &&
@@ -266,11 +275,13 @@ static int on_frame_recv_callback(nghttp2_session *session _U_,
    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) {
+static 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) {
   http2_session_data *session_data = (http2_session_data *)user_data;
+  (void)session;
+  (void)flags;
+
   if (session_data->stream_data->stream_id == stream_id) {
     fwrite(data, 1, len, stdout);
   }
@@ -300,9 +311,12 @@ static int on_stream_close_callback(nghttp2_session *session, int32_t stream_id,
 /* 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,
+static int select_next_proto_cb(SSL *ssl, unsigned char **out,
                                 unsigned char *outlen, const unsigned char *in,
-                                unsigned int inlen, void *arg _U_) {
+                                unsigned int inlen, void *arg) {
+  (void)ssl;
+  (void)arg;
+
   if (nghttp2_select_next_protocol(out, outlen, in, inlen) <= 0) {
     errx(1, "Server did not advertise " NGHTTP2_PROTO_VERSION_ID);
   }
@@ -461,8 +475,10 @@ static void readcb(struct bufferevent *bev, void *ptr) {
    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) {
+static void writecb(struct bufferevent *bev, void *ptr) {
   http2_session_data *session_data = (http2_session_data *)ptr;
+  (void)bev;
+
   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) {
@@ -532,6 +548,7 @@ static void initiate_connection(struct event_base *evbase, SSL_CTX *ssl_ctx,
   bev = bufferevent_openssl_socket_new(
       evbase, -1, ssl, BUFFEREVENT_SSL_CONNECTING,
       BEV_OPT_DEFER_CALLBACKS | BEV_OPT_CLOSE_ON_FREE);
+  bufferevent_enable(bev, EV_READ | EV_WRITE);
   bufferevent_setcb(bev, readcb, writecb, eventcb, session_data);
   rv = bufferevent_socket_connect_hostname(bev, session_data->dnsbase,
                                            AF_UNSPEC, host, port);
index 12264ad..403d2dd 100644 (file)
@@ -109,18 +109,23 @@ struct app_context {
 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_) {
+static int next_proto_cb(SSL *ssl, const unsigned char **data,
+                         unsigned int *len, void *arg) {
+  (void)ssl;
+  (void)arg;
+
   *data = next_proto_list;
   *len = (unsigned int)next_proto_list_len;
   return SSL_TLSEXT_ERR_OK;
 }
 
 #if OPENSSL_VERSION_NUMBER >= 0x10002000L
-static int alpn_select_proto_cb(SSL *ssl _U_, const unsigned char **out,
+static int alpn_select_proto_cb(SSL *ssl, const unsigned char **out,
                                 unsigned char *outlen, const unsigned char *in,
-                                unsigned int inlen, void *arg _U_) {
+                                unsigned int inlen, void *arg) {
   int rv;
+  (void)ssl;
+  (void)arg;
 
   rv = nghttp2_select_next_protocol((unsigned char **)out, outlen, in, inlen);
 
@@ -197,8 +202,10 @@ static void add_stream(http2_session_data *session_data,
   }
 }
 
-static void remove_stream(http2_session_data *session_data _U_,
+static void remove_stream(http2_session_data *session_data,
                           http2_stream_data *stream_data) {
+  (void)session_data;
+
   stream_data->prev->next = stream_data->next;
   if (stream_data->next) {
     stream_data->next->prev = stream_data->prev;
@@ -243,6 +250,7 @@ static http2_session_data *create_http2_session_data(app_context *app_ctx,
   session_data->bev = bufferevent_openssl_socket_new(
       app_ctx->evbase, fd, ssl, BUFFEREVENT_SSL_ACCEPTING,
       BEV_OPT_CLOSE_ON_FREE | BEV_OPT_DEFER_CALLBACKS);
+  bufferevent_enable(session_data->bev, EV_READ | EV_WRITE);
   rv = getnameinfo(addr, (socklen_t)addrlen, host, sizeof(host), NULL, 0,
                    NI_NUMERICHOST);
   if (rv != 0) {
@@ -309,10 +317,13 @@ static int session_recv(http2_session_data *session_data) {
   return 0;
 }
 
-static ssize_t send_callback(nghttp2_session *session _U_, const uint8_t *data,
-                             size_t length, int flags _U_, void *user_data) {
+static ssize_t send_callback(nghttp2_session *session, const uint8_t *data,
+                             size_t length, int flags, void *user_data) {
   http2_session_data *session_data = (http2_session_data *)user_data;
   struct bufferevent *bev = session_data->bev;
+  (void)session;
+  (void)flags;
+
   /* Avoid excessive buffering in server side. */
   if (evbuffer_get_length(bufferevent_get_output(session_data->bev)) >=
       OUTPUT_WOULDBLOCK_THRESHOLD) {
@@ -375,13 +386,17 @@ static char *percent_decode(const uint8_t *value, size_t valuelen) {
   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,
+static 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 _U_) {
+                                  void *user_data) {
   int fd = source->fd;
   ssize_t r;
+  (void)session;
+  (void)stream_id;
+  (void)user_data;
+
   while ((r = read(fd, buf, length)) == -1 && errno == EINTR)
     ;
   if (r == -1) {
@@ -454,10 +469,12 @@ static int error_reply(nghttp2_session *session,
 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_) {
+                              size_t valuelen, uint8_t flags, void *user_data) {
   http2_stream_data *stream_data;
   const char PATH[] = ":path";
+  (void)flags;
+  (void)user_data;
+
   switch (frame->hd.type) {
   case NGHTTP2_HEADERS:
     if (frame->headers.cat != NGHTTP2_HCAT_REQUEST) {
@@ -570,9 +587,10 @@ static int on_frame_recv_callback(nghttp2_session *session,
 }
 
 static int on_stream_close_callback(nghttp2_session *session, int32_t stream_id,
-                                    uint32_t error_code _U_, void *user_data) {
+                                    uint32_t error_code, void *user_data) {
   http2_session_data *session_data = (http2_session_data *)user_data;
   http2_stream_data *stream_data;
+  (void)error_code;
 
   stream_data = nghttp2_session_get_stream_user_data(session, stream_id);
   if (!stream_data) {
@@ -625,8 +643,10 @@ static int send_server_connection_header(http2_session_data *session_data) {
 
 /* readcb for bufferevent after client connection header was
    checked. */
-static void readcb(struct bufferevent *bev _U_, void *ptr) {
+static void readcb(struct bufferevent *bev, void *ptr) {
   http2_session_data *session_data = (http2_session_data *)ptr;
+  (void)bev;
+
   if (session_recv(session_data) != 0) {
     delete_http2_session_data(session_data);
     return;
@@ -658,12 +678,13 @@ static void writecb(struct bufferevent *bev, void *ptr) {
 }
 
 /* eventcb for bufferevent */
-static void eventcb(struct bufferevent *bev _U_, short events, void *ptr) {
+static void eventcb(struct bufferevent *bev, short events, void *ptr) {
   http2_session_data *session_data = (http2_session_data *)ptr;
   if (events & BEV_EVENT_CONNECTED) {
     const unsigned char *alpn = NULL;
     unsigned int alpnlen = 0;
     SSL *ssl;
+    (void)bev;
 
     fprintf(stderr, "%s connected\n", session_data->client_addr);
 
@@ -703,10 +724,11 @@ static void eventcb(struct bufferevent *bev _U_, short events, void *ptr) {
 }
 
 /* callback for evconnlistener */
-static void acceptcb(struct evconnlistener *listener _U_, int fd,
+static void acceptcb(struct evconnlistener *listener, int fd,
                      struct sockaddr *addr, int addrlen, void *arg) {
   app_context *app_ctx = (app_context *)arg;
   http2_session_data *session_data;
+  (void)listener;
 
   session_data = create_http2_session_data(app_ctx, fd, addr, addrlen);
 
index 53931da..1c5fe9d 100644 (file)
@@ -24,7 +24,6 @@
 GO_FILES = \
        nghttpx_http1_test.go \
        nghttpx_http2_test.go \
-       nghttpx_spdy_test.go \
        server_tester.go
 
 EXTRA_DIST = \
@@ -43,7 +42,6 @@ EXTRA_DIST = \
 itprep:
        go get -d -v golang.org/x/net/http2
        go get -d -v github.com/tatsuhiro-t/go-nghttp2
-       go get -d -v github.com/tatsuhiro-t/spdy
        go get -d -v golang.org/x/net/websocket
 
 it:
index c06e192..8fe1071 100644 (file)
@@ -1,7 +1,7 @@
-# Makefile.in generated by automake 1.15 from Makefile.am.
+# Makefile.in generated by automake 1.15.1 from Makefile.am.
 # @configure_input@
 
-# Copyright (C) 1994-2014 Free Software Foundation, Inc.
+# Copyright (C) 1994-2017 Free Software Foundation, Inc.
 
 # This Makefile.in is free software; the Free Software Foundation
 # gives unlimited permission to copy and/or distribute it,
@@ -215,8 +215,6 @@ LIBMRUBY_CFLAGS = @LIBMRUBY_CFLAGS@
 LIBMRUBY_LIBS = @LIBMRUBY_LIBS@
 LIBOBJS = @LIBOBJS@
 LIBS = @LIBS@
-LIBSPDYLAY_CFLAGS = @LIBSPDYLAY_CFLAGS@
-LIBSPDYLAY_LIBS = @LIBSPDYLAY_LIBS@
 LIBTOOL = @LIBTOOL@
 LIBXML2_CFLAGS = @LIBXML2_CFLAGS@
 LIBXML2_LIBS = @LIBXML2_LIBS@
@@ -338,7 +336,6 @@ top_srcdir = @top_srcdir@
 GO_FILES = \
        nghttpx_http1_test.go \
        nghttpx_http2_test.go \
-       nghttpx_spdy_test.go \
        server_tester.go
 
 EXTRA_DIST = \
@@ -553,7 +550,6 @@ uninstall-am:
 itprep:
        go get -d -v golang.org/x/net/http2
        go get -d -v github.com/tatsuhiro-t/go-nghttp2
-       go get -d -v github.com/tatsuhiro-t/spdy
        go get -d -v golang.org/x/net/websocket
 
 it:
index cbddc13..a765333 100644 (file)
@@ -9,6 +9,7 @@ import (
        "golang.org/x/net/websocket"
        "io"
        "net/http"
+       "regexp"
        "syscall"
        "testing"
        "time"
@@ -125,6 +126,54 @@ Content-Length: 0
 //     }
 // }
 
+// TestH1H1AffinityCookie tests that affinity cookie is sent back in
+// cleartext http.
+func TestH1H1AffinityCookie(t *testing.T) {
+       st := newServerTester([]string{"--affinity-cookie"}, t, noopHandler)
+       defer st.Close()
+
+       res, err := st.http1(requestParam{
+               name: "TestH1H1AffinityCookie",
+       })
+       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)
+       }
+
+       const pattern = `affinity=[0-9a-f]{8}; Path=/foo/bar`
+       validCookie := regexp.MustCompile(pattern)
+       if got := res.header.Get("Set-Cookie"); !validCookie.MatchString(got) {
+               t.Errorf("Set-Cookie: %v; want pattern %v", got, pattern)
+       }
+}
+
+// TestH1H1AffinityCookieTLS tests that affinity cookie is sent back
+// in https.
+func TestH1H1AffinityCookieTLS(t *testing.T) {
+       st := newServerTesterTLS([]string{"--alpn-h1", "--affinity-cookie"}, t, noopHandler)
+       defer st.Close()
+
+       res, err := st.http1(requestParam{
+               name: "TestH1H1AffinityCookieTLS",
+       })
+       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)
+       }
+
+       const pattern = `affinity=[0-9a-f]{8}; Path=/foo/bar; Secure`
+       validCookie := regexp.MustCompile(pattern)
+       if got := res.header.Get("Set-Cookie"); !validCookie.MatchString(got) {
+               t.Errorf("Set-Cookie: %v; want pattern %v", got, pattern)
+       }
+}
+
 // TestH1H1GracefulShutdown tests graceful shutdown.
 func TestH1H1GracefulShutdown(t *testing.T) {
        st := newServerTester(nil, t, noopHandler)
@@ -162,7 +211,7 @@ func TestH1H1GracefulShutdown(t *testing.T) {
        want := io.EOF
        b := make([]byte, 256)
        if _, err := st.conn.Read(b); err == nil || err != want {
-               t.Errorf("st.conn.Read(): %v; want %v, %v", err, want)
+               t.Errorf("st.conn.Read(): %v; want %v", err, want)
        }
 }
 
index a4c46c8..0698dd2 100644 (file)
@@ -35,6 +35,105 @@ func TestH2H1PlainGET(t *testing.T) {
        }
 }
 
+// TestH2H1AddXfp tests that server appends :scheme to the existing
+// x-forwarded-proto header field.
+func TestH2H1AddXfp(t *testing.T) {
+       st := newServerTester([]string{"--no-strip-incoming-x-forwarded-proto"}, t, func(w http.ResponseWriter, r *http.Request) {
+               xfp := r.Header.Get("X-Forwarded-Proto")
+               if got, want := xfp, "foo, http"; got != want {
+                       t.Errorf("X-Forwarded-Proto = %q; want %q", got, want)
+               }
+       })
+       defer st.Close()
+
+       res, err := st.http2(requestParam{
+               name: "TestH2H1AddXfp",
+               header: []hpack.HeaderField{
+                       pair("x-forwarded-proto", "foo"),
+               },
+       })
+       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)
+       }
+}
+
+// TestH2H1NoAddXfp tests that server does not append :scheme to the
+// existing x-forwarded-proto header field.
+func TestH2H1NoAddXfp(t *testing.T) {
+       st := newServerTester([]string{"--no-add-x-forwarded-proto", "--no-strip-incoming-x-forwarded-proto"}, t, func(w http.ResponseWriter, r *http.Request) {
+               xfp := r.Header.Get("X-Forwarded-Proto")
+               if got, want := xfp, "foo"; got != want {
+                       t.Errorf("X-Forwarded-Proto = %q; want %q", got, want)
+               }
+       })
+       defer st.Close()
+
+       res, err := st.http2(requestParam{
+               name: "TestH2H1NoAddXfp",
+               header: []hpack.HeaderField{
+                       pair("x-forwarded-proto", "foo"),
+               },
+       })
+       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)
+       }
+}
+
+// TestH2H1StripXfp tests that server strips incoming
+// x-forwarded-proto header field.
+func TestH2H1StripXfp(t *testing.T) {
+       st := newServerTester(nil, t, func(w http.ResponseWriter, r *http.Request) {
+               xfp := r.Header.Get("X-Forwarded-Proto")
+               if got, want := xfp, "http"; got != want {
+                       t.Errorf("X-Forwarded-Proto = %q; want %q", got, want)
+               }
+       })
+       defer st.Close()
+
+       res, err := st.http2(requestParam{
+               name: "TestH2H1StripXfp",
+               header: []hpack.HeaderField{
+                       pair("x-forwarded-proto", "foo"),
+               },
+       })
+       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)
+       }
+}
+
+// TestH2H1StripNoAddXfp tests that server strips incoming
+// x-forwarded-proto header field, and does not add another.
+func TestH2H1StripNoAddXfp(t *testing.T) {
+       st := newServerTester([]string{"--no-add-x-forwarded-proto"}, t, func(w http.ResponseWriter, r *http.Request) {
+               if got, found := r.Header["X-Forwarded-Proto"]; found {
+                       t.Errorf("X-Forwarded-Proto = %q; want nothing", got)
+               }
+       })
+       defer st.Close()
+
+       res, err := st.http2(requestParam{
+               name: "TestH2H1StripNoAddXfp",
+               header: []hpack.HeaderField{
+                       pair("x-forwarded-proto", "foo"),
+               },
+       })
+       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)
+       }
+}
+
 // TestH2H1AddXff tests that server generates X-Forwarded-For header
 // field when forwarding request to backend.
 func TestH2H1AddXff(t *testing.T) {
@@ -1014,14 +1113,45 @@ func TestH2H1Upgrade(t *testing.T) {
        }
 }
 
+// TestH2H1ProxyProtocolV1ForwardedForObfuscated tests that Forwarded
+// header field includes obfuscated address even if PROXY protocol
+// version 1 containing TCP4 entry is accepted.
+func TestH2H1ProxyProtocolV1ForwardedForObfuscated(t *testing.T) {
+       pattern := fmt.Sprintf(`^for=_[^;]+$`)
+       validFwd := regexp.MustCompile(pattern)
+       st := newServerTester([]string{"--accept-proxy-protocol", "--add-x-forwarded-for", "--add-forwarded=for", "--forwarded-for=obfuscated"}, t, func(w http.ResponseWriter, r *http.Request) {
+               if got := r.Header.Get("Forwarded"); !validFwd.MatchString(got) {
+                       t.Errorf("Forwarded: %v; want pattern %v", got, pattern)
+               }
+       })
+       defer st.Close()
+
+       st.conn.Write([]byte("PROXY TCP4 192.168.0.2 192.168.0.100 12345 8080\r\n"))
+
+       res, err := st.http2(requestParam{
+               name: "TestH2H1ProxyProtocolV1ForwardedForObfuscated",
+       })
+
+       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)
+       }
+}
+
 // TestH2H1ProxyProtocolV1TCP4 tests PROXY protocol version 1
 // containing TCP4 entry is accepted and X-Forwarded-For contains
 // advertised src address.
 func TestH2H1ProxyProtocolV1TCP4(t *testing.T) {
-       st := newServerTester([]string{"--accept-proxy-protocol", "--add-x-forwarded-for"}, t, func(w http.ResponseWriter, r *http.Request) {
+       st := newServerTester([]string{"--accept-proxy-protocol", "--add-x-forwarded-for", "--add-forwarded=for", "--forwarded-for=ip"}, t, func(w http.ResponseWriter, r *http.Request) {
                if got, want := r.Header.Get("X-Forwarded-For"), "192.168.0.2"; got != want {
                        t.Errorf("X-Forwarded-For: %v; want %v", got, want)
                }
+               if got, want := r.Header.Get("Forwarded"), "for=192.168.0.2"; got != want {
+                       t.Errorf("Forwarded: %v; want %v", got, want)
+               }
        })
        defer st.Close()
 
@@ -1044,10 +1174,13 @@ func TestH2H1ProxyProtocolV1TCP4(t *testing.T) {
 // containing TCP6 entry is accepted and X-Forwarded-For contains
 // advertised src address.
 func TestH2H1ProxyProtocolV1TCP6(t *testing.T) {
-       st := newServerTester([]string{"--accept-proxy-protocol", "--add-x-forwarded-for"}, t, func(w http.ResponseWriter, r *http.Request) {
+       st := newServerTester([]string{"--accept-proxy-protocol", "--add-x-forwarded-for", "--add-forwarded=for", "--forwarded-for=ip"}, t, func(w http.ResponseWriter, r *http.Request) {
                if got, want := r.Header.Get("X-Forwarded-For"), "2001:0db8:85a3:0000:0000:8a2e:0370:7334"; got != want {
                        t.Errorf("X-Forwarded-For: %v; want %v", got, want)
                }
+               if got, want := r.Header.Get("Forwarded"), `for="[2001:0db8:85a3:0000:0000:8a2e:0370:7334]"`; got != want {
+                       t.Errorf("Forwarded: %v; want %v", got, want)
+               }
        })
        defer st.Close()
 
@@ -1069,9 +1202,12 @@ func TestH2H1ProxyProtocolV1TCP6(t *testing.T) {
 // TestH2H1ProxyProtocolV1Unknown tests PROXY protocol version 1
 // containing UNKNOWN entry is accepted.
 func TestH2H1ProxyProtocolV1Unknown(t *testing.T) {
-       st := newServerTester([]string{"--accept-proxy-protocol", "--add-x-forwarded-for"}, t, func(w http.ResponseWriter, r *http.Request) {
+       st := newServerTester([]string{"--accept-proxy-protocol", "--add-x-forwarded-for", "--add-forwarded=for", "--forwarded-for=ip"}, t, func(w http.ResponseWriter, r *http.Request) {
                if got, notWant := r.Header.Get("X-Forwarded-For"), "192.168.0.2"; got == notWant {
-                       t.Errorf("X-Forwarded-For: %v")
+                       t.Errorf("X-Forwarded-For: %v; want something else", got)
+               }
+               if got, notWant := r.Header.Get("Forwarded"), "for=192.168.0.2"; got == notWant {
+                       t.Errorf("Forwarded: %v; want something else", got)
                }
        })
        defer st.Close()
@@ -1449,6 +1585,175 @@ func TestH2H1HTTPSRedirectPort(t *testing.T) {
        }
 }
 
+// TestH2H1Code204 tests that 204 response without content-length, and
+// transfer-encoding is valid.
+func TestH2H1Code204(t *testing.T) {
+       st := newServerTester(nil, t, func(w http.ResponseWriter, r *http.Request) {
+               w.WriteHeader(http.StatusNoContent)
+       })
+       defer st.Close()
+
+       res, err := st.http2(requestParam{
+               name: "TestH2H1Code204",
+       })
+       if err != nil {
+               t.Fatalf("Error st.http2() = %v", err)
+       }
+
+       if got, want := res.status, 204; got != want {
+               t.Errorf("status = %v; want %v", got, want)
+       }
+}
+
+// TestH2H1Code204CL0 tests that 204 response with content-length: 0
+// is allowed.
+func TestH2H1Code204CL0(t *testing.T) {
+       st := newServerTester(nil, t, func(w http.ResponseWriter, r *http.Request) {
+               hj, ok := w.(http.Hijacker)
+               if !ok {
+                       http.Error(w, "Could not hijack the connection", http.StatusInternalServerError)
+                       return
+               }
+               conn, bufrw, err := hj.Hijack()
+               if err != nil {
+                       http.Error(w, err.Error(), http.StatusInternalServerError)
+                       return
+               }
+               defer conn.Close()
+               bufrw.WriteString("HTTP/1.1 204\r\nContent-Length: 0\r\n\r\n")
+               bufrw.Flush()
+       })
+       defer st.Close()
+
+       res, err := st.http2(requestParam{
+               name: "TestH2H1Code204CL0",
+       })
+       if err != nil {
+               t.Fatalf("Error st.http2() = %v", err)
+       }
+
+       if got, want := res.status, 204; got != want {
+               t.Errorf("status = %v; want %v", got, want)
+       }
+
+       if got, found := res.header["Content-Length"]; found {
+               t.Errorf("Content-Length = %v, want nothing", got)
+       }
+}
+
+// TestH2H1Code204CLNonzero tests that 204 response with nonzero
+// content-length is not allowed.
+func TestH2H1Code204CLNonzero(t *testing.T) {
+       st := newServerTester(nil, t, func(w http.ResponseWriter, r *http.Request) {
+               hj, ok := w.(http.Hijacker)
+               if !ok {
+                       http.Error(w, "Could not hijack the connection", http.StatusInternalServerError)
+                       return
+               }
+               conn, bufrw, err := hj.Hijack()
+               if err != nil {
+                       http.Error(w, err.Error(), http.StatusInternalServerError)
+                       return
+               }
+               defer conn.Close()
+               bufrw.WriteString("HTTP/1.1 204\r\nContent-Length: 1\r\n\r\n")
+               bufrw.Flush()
+       })
+       defer st.Close()
+
+       res, err := st.http2(requestParam{
+               name: "TestH2H1Code204CLNonzero",
+       })
+       if err != nil {
+               t.Fatalf("Error st.http2() = %v", err)
+       }
+
+       if got, want := res.status, 502; got != want {
+               t.Errorf("status = %v; want %v", got, want)
+       }
+}
+
+// TestH2H1Code204TE tests that 204 response with transfer-encoding is
+// not allowed.
+func TestH2H1Code204TE(t *testing.T) {
+       st := newServerTester(nil, t, func(w http.ResponseWriter, r *http.Request) {
+               hj, ok := w.(http.Hijacker)
+               if !ok {
+                       http.Error(w, "Could not hijack the connection", http.StatusInternalServerError)
+                       return
+               }
+               conn, bufrw, err := hj.Hijack()
+               if err != nil {
+                       http.Error(w, err.Error(), http.StatusInternalServerError)
+                       return
+               }
+               defer conn.Close()
+               bufrw.WriteString("HTTP/1.1 204\r\nTransfer-Encoding: chunked\r\n\r\n")
+               bufrw.Flush()
+       })
+       defer st.Close()
+
+       res, err := st.http2(requestParam{
+               name: "TestH2H1Code204TE",
+       })
+       if err != nil {
+               t.Fatalf("Error st.http2() = %v", err)
+       }
+
+       if got, want := res.status, 502; got != want {
+               t.Errorf("status = %v; want %v", got, want)
+       }
+}
+
+// TestH2H1AffinityCookie tests that affinity cookie is sent back in
+// cleartext http.
+func TestH2H1AffinityCookie(t *testing.T) {
+       st := newServerTester([]string{"--affinity-cookie"}, t, noopHandler)
+       defer st.Close()
+
+       res, err := st.http2(requestParam{
+               name: "TestH2H1AffinityCookie",
+       })
+       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)
+       }
+
+       const pattern = `affinity=[0-9a-f]{8}; Path=/foo/bar`
+       validCookie := regexp.MustCompile(pattern)
+       if got := res.header.Get("Set-Cookie"); !validCookie.MatchString(got) {
+               t.Errorf("Set-Cookie: %v; want pattern %v", got, pattern)
+       }
+}
+
+// TestH2H1AffinityCookieTLS tests that affinity cookie is sent back
+// in https.
+func TestH2H1AffinityCookieTLS(t *testing.T) {
+       st := newServerTesterTLS([]string{"--affinity-cookie"}, t, noopHandler)
+       defer st.Close()
+
+       res, err := st.http2(requestParam{
+               name:   "TestH2H1AffinityCookieTLS",
+               scheme: "https",
+       })
+       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)
+       }
+
+       const pattern = `affinity=[0-9a-f]{8}; Path=/foo/bar; Secure`
+       validCookie := regexp.MustCompile(pattern)
+       if got := res.header.Get("Set-Cookie"); !validCookie.MatchString(got) {
+               t.Errorf("Set-Cookie: %v; want pattern %v", got, pattern)
+       }
+}
+
 // TestH2H1GracefulShutdown tests graceful shutdown.
 func TestH2H1GracefulShutdown(t *testing.T) {
        st := newServerTester(nil, t, noopHandler)
@@ -1653,6 +1958,105 @@ func TestH2H2TLSXfp(t *testing.T) {
        }
 }
 
+// TestH2H2AddXfp tests that server appends :scheme to the existing
+// x-forwarded-proto header field.
+func TestH2H2AddXfp(t *testing.T) {
+       st := newServerTesterTLS([]string{"--http2-bridge", "--no-strip-incoming-x-forwarded-proto"}, t, func(w http.ResponseWriter, r *http.Request) {
+               xfp := r.Header.Get("X-Forwarded-Proto")
+               if got, want := xfp, "foo, http"; got != want {
+                       t.Errorf("X-Forwarded-Proto = %q; want %q", got, want)
+               }
+       })
+       defer st.Close()
+
+       res, err := st.http2(requestParam{
+               name: "TestH2H2AddXfp",
+               header: []hpack.HeaderField{
+                       pair("x-forwarded-proto", "foo"),
+               },
+       })
+       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)
+       }
+}
+
+// TestH2H2NoAddXfp tests that server does not append :scheme to the
+// existing x-forwarded-proto header field.
+func TestH2H2NoAddXfp(t *testing.T) {
+       st := newServerTesterTLS([]string{"--http2-bridge", "--no-add-x-forwarded-proto", "--no-strip-incoming-x-forwarded-proto"}, t, func(w http.ResponseWriter, r *http.Request) {
+               xfp := r.Header.Get("X-Forwarded-Proto")
+               if got, want := xfp, "foo"; got != want {
+                       t.Errorf("X-Forwarded-Proto = %q; want %q", got, want)
+               }
+       })
+       defer st.Close()
+
+       res, err := st.http2(requestParam{
+               name: "TestH2H2NoAddXfp",
+               header: []hpack.HeaderField{
+                       pair("x-forwarded-proto", "foo"),
+               },
+       })
+       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)
+       }
+}
+
+// TestH2H2StripXfp tests that server strips incoming
+// x-forwarded-proto header field.
+func TestH2H2StripXfp(t *testing.T) {
+       st := newServerTesterTLS([]string{"--http2-bridge"}, t, func(w http.ResponseWriter, r *http.Request) {
+               xfp := r.Header.Get("X-Forwarded-Proto")
+               if got, want := xfp, "http"; got != want {
+                       t.Errorf("X-Forwarded-Proto = %q; want %q", got, want)
+               }
+       })
+       defer st.Close()
+
+       res, err := st.http2(requestParam{
+               name: "TestH2H2StripXfp",
+               header: []hpack.HeaderField{
+                       pair("x-forwarded-proto", "foo"),
+               },
+       })
+       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)
+       }
+}
+
+// TestH2H2StripNoAddXfp tests that server strips incoming
+// x-forwarded-proto header field, and does not add another.
+func TestH2H2StripNoAddXfp(t *testing.T) {
+       st := newServerTesterTLS([]string{"--http2-bridge", "--no-add-x-forwarded-proto"}, t, func(w http.ResponseWriter, r *http.Request) {
+               if got, found := r.Header["X-Forwarded-Proto"]; found {
+                       t.Errorf("X-Forwarded-Proto = %q; want nothing", got)
+               }
+       })
+       defer st.Close()
+
+       res, err := st.http2(requestParam{
+               name: "TestH2H2StripNoAddXfp",
+               header: []hpack.HeaderField{
+                       pair("x-forwarded-proto", "foo"),
+               },
+       })
+       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)
+       }
+}
+
 // TestH2H2AddXff tests that server generates X-Forwarded-For header
 // field when forwarding request to backend.
 func TestH2H2AddXff(t *testing.T) {
@@ -1961,6 +2365,26 @@ func TestH2H2DNS(t *testing.T) {
        }
 }
 
+// TestH2H2Code204 tests that 204 response without content-length, and
+// transfer-encoding is valid.
+func TestH2H2Code204(t *testing.T) {
+       st := newServerTester([]string{"--http2-bridge"}, t, func(w http.ResponseWriter, r *http.Request) {
+               w.WriteHeader(http.StatusNoContent)
+       })
+       defer st.Close()
+
+       res, err := st.http2(requestParam{
+               name: "TestH2H2Code204",
+       })
+       if err != nil {
+               t.Fatalf("Error st.http2() = %v", err)
+       }
+
+       if got, want := res.status, 204; got != want {
+               t.Errorf("status = %v; want %v", got, want)
+       }
+}
+
 // TestH2APIBackendconfig exercise backendconfig API endpoint routine
 // for successful case.
 func TestH2APIBackendconfig(t *testing.T) {
diff --git a/integration-tests/nghttpx_spdy_test.go b/integration-tests/nghttpx_spdy_test.go
deleted file mode 100644 (file)
index dfe35e0..0000000
+++ /dev/null
@@ -1,664 +0,0 @@
-package nghttp2
-
-import (
-       "encoding/json"
-       "github.com/tatsuhiro-t/spdy"
-       "golang.org/x/net/http2/hpack"
-       "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", "--request-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-request-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)
-       }
-}
-
-// TestS3H1InvalidMethod tests that server rejects invalid method with
-// 501.
-func TestS3H1InvalidMethod(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 this request")
-       })
-       defer st.Close()
-
-       res, err := st.spdy(requestParam{
-               name:   "TestS3H1InvalidMethod",
-               method: "get",
-       })
-       if err != nil {
-               t.Fatalf("Error st.spdy() = %v", err)
-       }
-       if got, want := res.status, 501; got != want {
-               t.Errorf("status: %v; want %v", got, want)
-       }
-}
-
-// TestS3H1BadHost tests that server rejects request including bad
-// character in :host header field.
-func TestS3H1BadHost(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 this request")
-       })
-       defer st.Close()
-
-       res, err := st.spdy(requestParam{
-               name:      "TestS3H1BadHost",
-               authority: `foo\bar`,
-       })
-       if err != nil {
-               t.Fatalf("Error st.spdy() = %v", err)
-       }
-       if got, want := res.status, 400; got != want {
-               t.Errorf("status: %v; want %v", got, want)
-       }
-}
-
-// TestS3H1BadScheme tests that server rejects request including bad
-// character in :scheme header field.
-func TestS3H1BadScheme(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 this request")
-       })
-       defer st.Close()
-
-       res, err := st.spdy(requestParam{
-               name:   "TestS3H1BadScheme",
-               scheme: `http*`,
-       })
-       if err != nil {
-               t.Fatalf("Error st.spdy() = %v", err)
-       }
-       if got, want := res.status, 400; got != want {
-               t.Errorf("status: %v; want %v", got, want)
-       }
-}
-
-// TestS3H1ReqPhaseSetHeader tests mruby request phase hook
-// modifies request header fields.
-func TestS3H1ReqPhaseSetHeader(t *testing.T) {
-       st := newServerTesterTLS([]string{"--npn-list=spdy/3.1", "--mruby-file=" + testDir + "/req-set-header.rb"}, t, func(w http.ResponseWriter, r *http.Request) {
-               if got, want := r.Header.Get("User-Agent"), "mruby"; got != want {
-                       t.Errorf("User-Agent = %v; want %v", got, want)
-               }
-       })
-       defer st.Close()
-
-       res, err := st.spdy(requestParam{
-               name: "TestS3H1ReqPhaseSetHeader",
-       })
-       if err != nil {
-               t.Fatalf("Error st.spdy() = %v", err)
-       }
-
-       if got, want := res.status, 200; got != want {
-               t.Errorf("status = %v; want %v", got, want)
-       }
-}
-
-// TestS3H1ReqPhaseReturn tests mruby request phase hook returns
-// custom response.
-func TestS3H1ReqPhaseReturn(t *testing.T) {
-       st := newServerTesterTLS([]string{"--npn-list=spdy/3.1", "--mruby-file=" + testDir + "/req-return.rb"}, t, func(w http.ResponseWriter, r *http.Request) {
-               t.Fatalf("request should not be forwarded")
-       })
-       defer st.Close()
-
-       res, err := st.spdy(requestParam{
-               name: "TestS3H1ReqPhaseReturn",
-       })
-       if err != nil {
-               t.Fatalf("Error st.spdy() = %v", err)
-       }
-
-       if got, want := res.status, 404; got != want {
-               t.Errorf("status = %v; want %v", got, want)
-       }
-
-       hdtests := []struct {
-               k, v string
-       }{
-               {"content-length", "20"},
-               {"from", "mruby"},
-       }
-       for _, tt := range hdtests {
-               if got, want := res.header.Get(tt.k), tt.v; got != want {
-                       t.Errorf("%v = %v; want %v", tt.k, got, want)
-               }
-       }
-
-       if got, want := string(res.body), "Hello World from req"; got != want {
-               t.Errorf("body = %v; want %v", got, want)
-       }
-}
-
-// TestS3H1RespPhaseSetHeader tests mruby response phase hook modifies
-// response header fields.
-func TestS3H1RespPhaseSetHeader(t *testing.T) {
-       st := newServerTesterTLS([]string{"--npn-list=spdy/3.1", "--mruby-file=" + testDir + "/resp-set-header.rb"}, t, noopHandler)
-       defer st.Close()
-
-       res, err := st.spdy(requestParam{
-               name: "TestS3H1RespPhaseSetHeader",
-       })
-       if err != nil {
-               t.Fatalf("Error st.spdy() = %v", err)
-       }
-
-       if got, want := res.status, 200; got != want {
-               t.Errorf("status = %v; want %v", got, want)
-       }
-
-       if got, want := res.header.Get("alpha"), "bravo"; got != want {
-               t.Errorf("alpha = %v; want %v", got, want)
-       }
-}
-
-// TestS3H1RespPhaseReturn tests mruby response phase hook returns
-// custom response.
-func TestS3H1RespPhaseReturn(t *testing.T) {
-       st := newServerTesterTLS([]string{"--npn-list=spdy/3.1", "--mruby-file=" + testDir + "/resp-return.rb"}, t, noopHandler)
-       defer st.Close()
-
-       res, err := st.spdy(requestParam{
-               name: "TestS3H1RespPhaseReturn",
-       })
-       if err != nil {
-               t.Fatalf("Error st.spdy() = %v", err)
-       }
-
-       if got, want := res.status, 404; got != want {
-               t.Errorf("status = %v; want %v", got, want)
-       }
-
-       hdtests := []struct {
-               k, v string
-       }{
-               {"content-length", "21"},
-               {"from", "mruby"},
-       }
-       for _, tt := range hdtests {
-               if got, want := res.header.Get(tt.k), tt.v; got != want {
-                       t.Errorf("%v = %v; want %v", tt.k, got, want)
-               }
-       }
-
-       if got, want := string(res.body), "Hello World from resp"; got != want {
-               t.Errorf("body = %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)
-//     }
-// }
-
-// TestS3H2ReqPhaseReturn tests mruby request phase hook returns
-// custom response.
-func TestS3H2ReqPhaseReturn(t *testing.T) {
-       st := newServerTesterTLS([]string{"--npn-list=spdy/3.1", "--http2-bridge", "--mruby-file=" + testDir + "/req-return.rb"}, t, func(w http.ResponseWriter, r *http.Request) {
-               t.Fatalf("request should not be forwarded")
-       })
-       defer st.Close()
-
-       res, err := st.spdy(requestParam{
-               name: "TestS3H2ReqPhaseReturn",
-       })
-       if err != nil {
-               t.Fatalf("Error st.spdy() = %v", err)
-       }
-
-       if got, want := res.status, 404; got != want {
-               t.Errorf("status = %v; want %v", got, want)
-       }
-
-       hdtests := []struct {
-               k, v string
-       }{
-               {"content-length", "20"},
-               {"from", "mruby"},
-       }
-       for _, tt := range hdtests {
-               if got, want := res.header.Get(tt.k), tt.v; got != want {
-                       t.Errorf("%v = %v; want %v", tt.k, got, want)
-               }
-       }
-
-       if got, want := string(res.body), "Hello World from req"; got != want {
-               t.Errorf("body = %v; want %v", got, want)
-       }
-}
-
-// TestS3H2RespPhaseReturn tests mruby response phase hook returns
-// custom response.
-func TestS3H2RespPhaseReturn(t *testing.T) {
-       st := newServerTesterTLS([]string{"--npn-list=spdy/3.1", "--http2-bridge", "--mruby-file=" + testDir + "/resp-return.rb"}, t, noopHandler)
-       defer st.Close()
-
-       res, err := st.spdy(requestParam{
-               name: "TestS3H2RespPhaseReturn",
-       })
-       if err != nil {
-               t.Fatalf("Error st.spdy() = %v", err)
-       }
-
-       if got, want := res.status, 404; got != want {
-               t.Errorf("status = %v; want %v", got, want)
-       }
-
-       hdtests := []struct {
-               k, v string
-       }{
-               {"content-length", "21"},
-               {"from", "mruby"},
-       }
-       for _, tt := range hdtests {
-               if got, want := res.header.Get(tt.k), tt.v; got != want {
-                       t.Errorf("%v = %v; want %v", tt.k, got, want)
-               }
-       }
-
-       if got, want := string(res.body), "Hello World from resp"; got != want {
-               t.Errorf("body = %v; want %v", got, want)
-       }
-}
-
-// TestS3APIBackendconfig exercise backendconfig API endpoint routine
-// for successful case.
-func TestS3APIBackendconfig(t *testing.T) {
-       st := newServerTesterTLSConnectPort([]string{"--npn-list=spdy/3.1", "-f127.0.0.1,3010;api"}, t, func(w http.ResponseWriter, r *http.Request) {
-               t.Fatalf("request should not be forwarded")
-       }, 3010)
-       defer st.Close()
-
-       res, err := st.spdy(requestParam{
-               name:   "TestS3APIBackendconfig",
-               path:   "/api/v1beta1/backendconfig",
-               method: "PUT",
-               body: []byte(`# comment
-backend=127.0.0.1,3011
-
-`),
-       })
-       if err != nil {
-               t.Fatalf("Error st.spdy() = %v", err)
-       }
-       if got, want := res.status, 200; got != want {
-               t.Errorf("res.status: %v; want %v", got, want)
-       }
-
-       var apiResp APIResponse
-       err = json.Unmarshal(res.body, &apiResp)
-       if err != nil {
-               t.Fatalf("Error unmarshaling API response: %v", err)
-       }
-       if got, want := apiResp.Status, "Success"; got != want {
-               t.Errorf("apiResp.Status: %v; want %v", got, want)
-       }
-       if got, want := apiResp.Code, 200; got != want {
-               t.Errorf("apiResp.Status: %v; want %v", got, want)
-       }
-}
-
-// TestS3APIBackendconfigQuery exercise backendconfig API endpoint
-// routine with query.
-func TestS3APIBackendconfigQuery(t *testing.T) {
-       st := newServerTesterTLSConnectPort([]string{"--npn-list=spdy/3.1", "-f127.0.0.1,3010;api"}, t, func(w http.ResponseWriter, r *http.Request) {
-               t.Fatalf("request should not be forwarded")
-       }, 3010)
-       defer st.Close()
-
-       res, err := st.spdy(requestParam{
-               name:   "TestS3APIBackendconfigQuery",
-               path:   "/api/v1beta1/backendconfig?foo=bar",
-               method: "PUT",
-               body: []byte(`# comment
-backend=127.0.0.1,3011
-
-`),
-       })
-       if err != nil {
-               t.Fatalf("Error st.spdy() = %v", err)
-       }
-       if got, want := res.status, 200; got != want {
-               t.Errorf("res.status: %v; want %v", got, want)
-       }
-
-       var apiResp APIResponse
-       err = json.Unmarshal(res.body, &apiResp)
-       if err != nil {
-               t.Fatalf("Error unmarshaling API response: %v", err)
-       }
-       if got, want := apiResp.Status, "Success"; got != want {
-               t.Errorf("apiResp.Status: %v; want %v", got, want)
-       }
-       if got, want := apiResp.Code, 200; got != want {
-               t.Errorf("apiResp.Status: %v; want %v", got, want)
-       }
-}
-
-// TestS3APIBackendconfigBadMethod exercise backendconfig API endpoint
-// routine with bad method.
-func TestS3APIBackendconfigBadMethod(t *testing.T) {
-       st := newServerTesterTLSConnectPort([]string{"--npn-list=spdy/3.1", "-f127.0.0.1,3010;api"}, t, func(w http.ResponseWriter, r *http.Request) {
-               t.Fatalf("request should not be forwarded")
-       }, 3010)
-       defer st.Close()
-
-       res, err := st.spdy(requestParam{
-               name:   "TestS3APIBackendconfigBadMethod",
-               path:   "/api/v1beta1/backendconfig",
-               method: "GET",
-               body: []byte(`# comment
-backend=127.0.0.1,3011
-
-`),
-       })
-       if err != nil {
-               t.Fatalf("Error st.spdy() = %v", err)
-       }
-       if got, want := res.status, 405; got != want {
-               t.Errorf("res.status: %v; want %v", got, want)
-       }
-
-       var apiResp APIResponse
-       err = json.Unmarshal(res.body, &apiResp)
-       if err != nil {
-               t.Fatalf("Error unmarshaling API response: %v", err)
-       }
-       if got, want := apiResp.Status, "Failure"; got != want {
-               t.Errorf("apiResp.Status: %v; want %v", got, want)
-       }
-       if got, want := apiResp.Code, 405; got != want {
-               t.Errorf("apiResp.Status: %v; want %v", got, want)
-       }
-}
-
-// TestS3APINotFound exercise backendconfig API endpoint routine when
-// API endpoint is not found.
-func TestS3APINotFound(t *testing.T) {
-       st := newServerTesterTLSConnectPort([]string{"--npn-list=spdy/3.1", "-f127.0.0.1,3010;api"}, t, func(w http.ResponseWriter, r *http.Request) {
-               t.Fatalf("request should not be forwarded")
-       }, 3010)
-       defer st.Close()
-
-       res, err := st.spdy(requestParam{
-               name:   "TestS3APINotFound",
-               path:   "/api/notfound",
-               method: "GET",
-               body: []byte(`# comment
-backend=127.0.0.1,3011
-
-`),
-       })
-       if err != nil {
-               t.Fatalf("Error st.spdy() = %v", err)
-       }
-       if got, want := res.status, 404; got != want {
-               t.Errorf("res.status: %v; want %v", got, want)
-       }
-
-       var apiResp APIResponse
-       err = json.Unmarshal(res.body, &apiResp)
-       if err != nil {
-               t.Fatalf("Error unmarshaling API response: %v", err)
-       }
-       if got, want := apiResp.Status, "Failure"; got != want {
-               t.Errorf("apiResp.Status: %v; want %v", got, want)
-       }
-       if got, want := apiResp.Code, 404; got != want {
-               t.Errorf("apiResp.Status: %v; want %v", got, want)
-       }
-}
-
-// TestS3Healthmon tests health monitor endpoint.
-func TestS3Healthmon(t *testing.T) {
-       st := newServerTesterTLSConnectPort([]string{"--npn-list=spdy/3.1", "-f127.0.0.1,3011;healthmon"}, t, func(w http.ResponseWriter, r *http.Request) {
-               t.Fatalf("request should not be forwarded")
-       }, 3011)
-       defer st.Close()
-
-       res, err := st.spdy(requestParam{
-               name: "TestS3Healthmon",
-               path: "/alpha/bravo",
-       })
-       if err != nil {
-               t.Fatalf("Error st.spdy() = %v", err)
-       }
-       if got, want := res.status, 200; got != want {
-               t.Errorf("res.status: %v; want %v", got, want)
-       }
-}
-
-// TestS3ResponseBeforeRequestEnd tests the situation where response
-// ends before request body finishes.
-func TestS3ResponseBeforeRequestEnd(t *testing.T) {
-       st := newServerTesterTLS([]string{"--npn-list=spdy/3.1", "--mruby-file=" + testDir + "/req-return.rb"}, t, func(w http.ResponseWriter, r *http.Request) {
-               t.Fatal("request should not be forwarded")
-       })
-       defer st.Close()
-
-       res, err := st.spdy(requestParam{
-               name:        "TestS3ResponseBeforeRequestEnd",
-               noEndStream: true,
-       })
-       if err != nil {
-               t.Fatalf("Error st.spdy() = %v", err)
-       }
-       if got, want := res.status, 404; got != want {
-               t.Errorf("res.status: %v; want %v", got, want)
-       }
-}
index 03271ad..d145519 100644 (file)
@@ -101,7 +101,7 @@ func newServerTesterInternal(src_args []string, t *testing.T, handler http.Handl
 
        args := []string{}
 
-       var backendTLS, dns, externalDNS, acceptProxyProtocol, redirectIfNotTLS bool
+       var backendTLS, dns, externalDNS, acceptProxyProtocol, redirectIfNotTLS, affinityCookie, alpnH1 bool
 
        for _, k := range src_args {
                switch k {
@@ -116,6 +116,10 @@ func newServerTesterInternal(src_args []string, t *testing.T, handler http.Handl
                        acceptProxyProtocol = true
                case "--redirect-if-not-tls":
                        redirectIfNotTLS = true
+               case "--affinity-cookie":
+                       affinityCookie = true
+               case "--alpn-h1":
+                       alpnH1 = true
                default:
                        args = append(args, k)
                }
@@ -153,8 +157,8 @@ func newServerTesterInternal(src_args []string, t *testing.T, handler http.Handl
                if sep == -1 {
                        t.Fatalf("backendURL.Host %v does not contain separator ':'", backendURL.Host)
                }
-               // We use awesome service xip.io.
-               b += fmt.Sprintf("%v.xip.io,%v;", backendURL.Host[:sep], backendURL.Host[sep+1:])
+               // We use awesome service nip.io.
+               b += fmt.Sprintf("%v.nip.io,%v;", backendURL.Host[:sep], backendURL.Host[sep+1:])
        }
 
        if backendTLS {
@@ -168,6 +172,10 @@ func newServerTesterInternal(src_args []string, t *testing.T, handler http.Handl
                b += ";redirect-if-not-tls"
        }
 
+       if affinityCookie {
+               b += ";affinity=cookie;affinity-cookie-name=affinity;affinity-cookie-path=/foo/bar"
+       }
+
        noTLS := ";no-tls"
        if frontendTLS {
                noTLS = ""
@@ -218,7 +226,11 @@ func newServerTesterInternal(src_args []string, t *testing.T, handler http.Handl
                                tlsConfig = clientConfig
                        }
                        tlsConfig.InsecureSkipVerify = true
-                       tlsConfig.NextProtos = []string{"h2", "spdy/3.1"}
+                       if alpnH1 {
+                               tlsConfig.NextProtos = []string{"http/1.1"}
+                       } else {
+                               tlsConfig.NextProtos = []string{"h2", "spdy/3.1"}
+                       }
                        conn, err = tls.Dial("tcp", authority, tlsConfig)
                } else {
                        conn, err = net.Dial("tcp", authority)
@@ -266,7 +278,7 @@ func (st *serverTester) Close() {
                done := make(chan struct{})
                go func() {
                        st.cmd.Wait()
-                       done <- struct{}{}
+                       close(done)
                }()
 
                st.cmd.Process.Signal(syscall.SIGQUIT)
@@ -769,7 +781,7 @@ type serverResponse struct {
        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
+       connClose         bool                 // Connection: 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
 }
index 7ef37ed..0846d06 100644 (file)
@@ -44,6 +44,10 @@ set_target_properties(nghttp2 PROPERTIES
   VERSION ${LT_VERSION} SOVERSION ${LT_SOVERSION}
   C_VISIBILITY_PRESET hidden
 )
+target_include_directories(nghttp2 INTERFACE
+    "${CMAKE_CURRENT_BINARY_DIR}/includes"
+    "${CMAKE_CURRENT_SOURCE_DIR}/includes"
+    )
 
 if(HAVE_CUNIT)
   # Static library (for unittests because of symbol visibility)
index 95edba4..372d0f1 100644 (file)
@@ -1,7 +1,7 @@
-# Makefile.in generated by automake 1.15 from Makefile.am.
+# Makefile.in generated by automake 1.15.1 from Makefile.am.
 # @configure_input@
 
-# Copyright (C) 1994-2014 Free Software Foundation, Inc.
+# Copyright (C) 1994-2017 Free Software Foundation, Inc.
 
 # This Makefile.in is free software; the Free Software Foundation
 # gives unlimited permission to copy and/or distribute it,
@@ -340,8 +340,6 @@ LIBMRUBY_CFLAGS = @LIBMRUBY_CFLAGS@
 LIBMRUBY_LIBS = @LIBMRUBY_LIBS@
 LIBOBJS = @LIBOBJS@
 LIBS = @LIBS@
-LIBSPDYLAY_CFLAGS = @LIBSPDYLAY_CFLAGS@
-LIBSPDYLAY_LIBS = @LIBSPDYLAY_LIBS@
 LIBTOOL = @LIBTOOL@
 LIBXML2_CFLAGS = @LIBXML2_CFLAGS@
 LIBXML2_LIBS = @LIBXML2_LIBS@
index 2a930d1..0077dc4 100644 (file)
-#\r
-# GNU Makefile for nghttp2 / MSVC.\r
-#\r
-# By G. Vanem <gvanem@yahoo.no> 2013\r
-# Updated 3/2015 by Remo Eichenberger @remoe\r
-# The MIT License apply.\r
-#\r
-\r
-#\r
-# Choose your weapons:\r
-#  Set 'USE_CYTHON=1' to build and install the 'nghttp2.pyd' Python extension.\r
-#\r
-THIS_MAKEFILE := $(lastword $(MAKEFILE_LIST))\r
-\r
-USE_CYTHON := 0\r
-#USE_CYTHON := 1\r
-\r
-_VERSION       := $(shell grep AC_INIT ../configure.ac | cut -d'[' -f3 | sed -e 's/-DEV//g' -e 's/], //g')\r
-_VERSION       := $(subst ., ,$(_VERSION))\r
-VER_MAJOR      := $(word 1,$(_VERSION))\r
-VER_MINOR      := $(word 2,$(_VERSION))\r
-VER_MICRO   := $(word 3,$(_VERSION))\r
-VERSION     := $(VER_MAJOR).$(VER_MINOR).$(VER_MICRO)\r
-VERSION_NUM := (($(VER_MAJOR) << 16) + ($(VER_MINOR) << 8) + $(VER_MICRO))\r
-\r
-GENERATED   := 'Generated by $(realpath Makefile.MSVC)'\r
-\r
-OBJ_DIR := MSVC_obj\r
-#SUFFIX :=-vc90-mt-x86\r
-\r
-#\r
-# Where to copy nghttp2.dll + lib + headers to.\r
-# Note: 'make install' is not in default targets. Do it explicitly.\r
-#\r
-TARGET_DIR  ?= ../_VC_ROOT\r
-VC_ROOT     := $(abspath $(TARGET_DIR))\r
-INSTALL_BIN := $(VC_ROOT)/bin\r
-INSTALL_LIB := $(VC_ROOT)/lib\r
-INSTALL_HDR := $(VC_ROOT)/include\r
-DLL_R  := $(OBJ_DIR)/nghttp2$(SUFFIX).dll\r
-DLL_D  := $(OBJ_DIR)/nghttp2d$(SUFFIX).dll\r
-LIB_R  := $(OBJ_DIR)/nghttp2-static.lib\r
-LIB_D  := $(OBJ_DIR)/nghttp2d-static.lib\r
-IMP_R  := $(OBJ_DIR)/nghttp2.lib\r
-IMP_D  := $(OBJ_DIR)/nghttp2d.lib\r
-\r
-#\r
-# Build for DEBUG-model and RELEASE at the same time.\r
-#\r
-TARGETS := $(LIB_R) $(DLL_R) $(IMP_R) \\r
-          $(LIB_D) $(DLL_D) $(IMP_D)\r
-\r
-EXT_LIBS = \r
-\r
-NGHTTP2_PDB_R := $(OBJ_DIR)/nghttp2.pdb\r
-NGHTTP2_PDB_D := $(OBJ_DIR)/nghttp2d.pdb\r
-\r
-CC       = cl\r
-LD              := link\r
-AR              := lib\r
-#CC       := icl\r
-#LD             := xilink\r
-#AR             := xilib\r
-RC              := rc\r
-CFLAGS   := -I./includes -Dssize_t=long -D_U_=""\r
-\r
-CFLAGS_R := -nologo -MD  -W3 -Z7 -DBUILDING_NGHTTP2\r
-CFLAGS_D := -nologo -MDd -W3 -Z7 -DBUILDING_NGHTTP2 \\r
-           -Ot -D_DEBUG -GF -RTCs -RTCu # -RTCc -GS\r
-\r
-LDFLAGS := -nologo -MAP -debug -incremental:no -opt:ref,icf -MANIFEST # -verbose\r
-\r
-\r
-NGHTTP2_SRC := nghttp2_pq.c \\r
-  nghttp2_map.c \\r
-  nghttp2_queue.c \\r
-  nghttp2_frame.c \\r
-  nghttp2_buf.c \\r
-  nghttp2_stream.c \\r
-  nghttp2_outbound_item.c \\r
-  nghttp2_session.c \\r
-  nghttp2_submit.c \\r
-  nghttp2_helper.c \\r
-  nghttp2_npn.c \\r
-  nghttp2_hd.c \\r
-  nghttp2_hd_huffman.c \\r
-  nghttp2_hd_huffman_data.c \\r
-  nghttp2_version.c \\r
-  nghttp2_priority_spec.c \\r
-  nghttp2_option.c \\r
-  nghttp2_callbacks.c \\r
-  nghttp2_mem.c \\r
-  nghttp2_http.c \\r
-  nghttp2_rcbuf.c\r
-\r
-NGHTTP2_OBJ_R := $(addprefix $(OBJ_DIR)/r_, $(notdir $(NGHTTP2_SRC:.c=.obj)))\r
-NGHTTP2_OBJ_D := $(addprefix $(OBJ_DIR)/d_, $(notdir $(NGHTTP2_SRC:.c=.obj)))\r
-\r
-.PHONY: all intro test_ver install copy_headers_and_libs \\r
-       install_nghttp2_pyd_0 install_nghttp2_pyd_1 \\r
-       build_nghttp2_pyd_0 build_nghttp2_pyd_1 \\r
-       clean_nghttp2_pyd_0 clean_nghttp2_pyd_1\r
-\r
-\r
-all: intro includes/nghttp2/nghttp2ver.h $(OBJ_DIR) $(TARGETS) build_nghttp2_pyd_$(USE_CYTHON)\r
-       @echo 'Welcome to NgHTTP2 (release + debug).'\r
-       @echo 'Do a "make -f Makefile.MSVC install" at own risk!'\r
-\r
-intro:\r
-       @echo 'Building NgHTTP (MSVC) ver. "$(VERSION)".'\r
-\r
-test_ver:\r
-       @echo '$$(VERSION):   "$(VERSION)".'\r
-       @echo '$$(_VERSION):  "$(_VERSION)".'\r
-       @echo '$$(VER_MAJOR): "$(VER_MAJOR)".'\r
-       @echo '$$(VER_MINOR): "$(VER_MINOR)".'\r
-       @echo '$$(VER_MICRO): "$(VER_MICRO)".'\r
-\r
-$(OBJ_DIR):\r
-       - mkdir $(OBJ_DIR)\r
-\r
-install: includes/nghttp2/nghttp2.h includes/nghttp2/nghttp2ver.h \\r
-         $(TARGETS) \\r
-         copy_headers_and_libs install_nghttp2_pyd_$(USE_CYTHON) \r
-\r
-#\r
-# This MUST be done before using the 'install_nghttp2_pyd_1' rule.\r
-#\r
-copy_headers_and_libs:\r
-       - mkdir -p $(INSTALL_HDR)/nghttp2 $(INSTALL_BIN) $(INSTALL_LIB)\r
-       cp --update $(addprefix includes/nghttp2/, nghttp2.h nghttp2ver.h)     $(INSTALL_HDR)/nghttp2\r
-       cp --update $(DLL_R) $(DLL_D) $(NGHTTP2_PDB_R) $(NGHTTP2_PDB_D) $(INSTALL_BIN)\r
-       cp --update $(IMP_R) $(IMP_D) $(LIB_R) $(LIB_D) $(INSTALL_LIB)\r
-       @echo\r
-\r
-$(LIB_R): $(NGHTTP2_OBJ_R)\r
-       $(AR) -nologo -out:$@ $^\r
-       @echo\r
-\r
-$(LIB_D): $(NGHTTP2_OBJ_D)\r
-       $(AR) -nologo -out:$@ $^\r
-       @echo\r
-\r
-\r
-$(IMP_R): $(DLL_R)\r
-\r
-$(DLL_R): $(NGHTTP2_OBJ_R) $(OBJ_DIR)/r_nghttp2.res \r
-       $(LD) $(LDFLAGS) -dll -out:$@ -implib:$(IMP_R) $(NGHTTP2_OBJ_R) -PDB:$(NGHTTP2_PDB_R) $(OBJ_DIR)/r_nghttp2.res $(EXT_LIBS)\r
-       mt -nologo -manifest $@.manifest -outputresource:$@\;2\r
-       @echo\r
-\r
-$(IMP_D): $(DLL_D)\r
-       \r
-$(DLL_D): $(NGHTTP2_OBJ_D) $(OBJ_DIR)/d_nghttp2.res \r
-       $(LD) $(LDFLAGS) -dll -out:$@ -implib:$(IMP_D) $(NGHTTP2_OBJ_D) -PDB:$(NGHTTP2_PDB_D) $(OBJ_DIR)/d_nghttp2.res $(EXT_LIBS)\r
-       mt -nologo -manifest $@.manifest -outputresource:$@\;2\r
-       @echo\r
-\r
-\r
-WIN_OBJDIR:=$(shell cygpath -w $(abspath $(OBJ_DIR)))\r
-WIN_OBJDIR:=$(subst \,/,$(WIN_OBJDIR))\r
-\r
-../python/setup.py: ../python/setup.py.in $(THIS_MAKEFILE)\r
-       cd ../python ; \\r
-       echo '# $(GENERATED). DO NOT EDIT.' > setup.py ; \\r
-       sed -e 's/@top_srcdir@/../'   \\r
-           -e 's%@top_builddir@%$(WIN_OBJDIR)%' \\r
-           -e 's/@PACKAGE_VERSION@/$(VERSION)/' setup.py.in >> setup.py ;\r
-\r
-build_nghttp2_pyd_0: ;\r
-\r
-build_nghttp2_pyd_1: $(addprefix ../python/, setup.py nghttp2.pyx)\r
-       cd ../python ; \\r
-       python setup.py build_ext -i -f bdist_wininst\r
-\r
-install_nghttp2_pyd_0: ;\r
-               \r
-install_nghttp2_pyd_1: $(addprefix ../python/, setup.py nghttp2.pyx)\r
-       cd ../python ; \\r
-       pip install .\r
-\r
-clean_nghttp2_pyd_0: ;\r
-\r
-clean_nghttp2_pyd_1:\r
-       cd ../python ; \\r
-       rm -fR build dist\r
-\r
-$(OBJ_DIR)/r_%.obj: %.c $(THIS_MAKEFILE)\r
-       $(CC) $(CFLAGS_R) $(CFLAGS) -Fo$@ -c $<\r
-       @echo\r
-\r
-$(OBJ_DIR)/d_%.obj: %.c $(THIS_MAKEFILE)\r
-       $(CC) $(CFLAGS_D) $(CFLAGS) -Fo$@ -c $<\r
-       @echo\r
-\r
-$(OBJ_DIR)/r_nghttp2.res: $(OBJ_DIR)/nghttp2.rc $(THIS_MAKEFILE)\r
-       $(RC) -D_RELEASE -Fo $@ $<\r
-       @echo\r
-\r
-$(OBJ_DIR)/d_nghttp2.res: $(OBJ_DIR)/nghttp2.rc $(THIS_MAKEFILE)\r
-       $(RC) -D_DEBUG -Fo $@ $<\r
-       @echo\r
-\r
-includes/nghttp2/nghttp2ver.h: includes/nghttp2/nghttp2ver.h.in $(THIS_MAKEFILE)\r
-       sed < includes/nghttp2/nghttp2ver.h.in     \\r
-            -e 's/@PACKAGE_VERSION@/$(VERSION)/g' \\r
-            -e 's/@PACKAGE_VERSION_NUM@/$(VERSION_NUM)/g' > $@\r
-       touch --reference=includes/nghttp2/nghttp2ver.h.in $@\r
-\r
-\r
-define RES_FILE\r
-  #include <winver.h>\r
-\r
-  VS_VERSION_INFO VERSIONINFO\r
-    FILEVERSION    $(VER_MAJOR), $(VER_MINOR), $(VER_MICRO), 0\r
-    PRODUCTVERSION $(VER_MAJOR), $(VER_MINOR), $(VER_MICRO), 0\r
-    FILEFLAGSMASK  0x3fL\r
-    FILEOS         0x40004L\r
-    FILETYPE       0x2L\r
-    FILESUBTYPE    0x0L\r
-  #ifdef _DEBUG\r
-    #define        VER_STR  "$(VERSION).0 (MSVC debug)"\r
-    #define        DBG      "d"\r
-    FILEFLAGS      0x1L\r
-  #else\r
-    #define        VER_STR  "$(VERSION).0 (MSVC release)"\r
-    #define        DBG      ""\r
-    FILEFLAGS      0x0L\r
-  #endif\r
-  BEGIN\r
-    BLOCK "StringFileInfo"\r
-    BEGIN\r
-      BLOCK "040904b0"\r
-      BEGIN\r
-        VALUE "CompanyName",      "http://tatsuhiro-t.github.io/nghttp2/"\r
-        VALUE "FileDescription",  "nghttp2; HTTP/2 C library"\r
-        VALUE "FileVersion",      VER_STR\r
-        VALUE "InternalName",     "nghttp2" DBG\r
-        VALUE "LegalCopyright",   "The MIT License"\r
-        VALUE "LegalTrademarks",  ""\r
-        VALUE "OriginalFilename", "nghttp2" DBG ".dll"\r
-        VALUE "ProductName",      "NGHTTP2."\r
-        VALUE "ProductVersion",   VER_STR\r
-      END\r
-    END\r
-  BLOCK "VarFileInfo"\r
-  BEGIN\r
-    VALUE "Translation", 0x409, 1200\r
-  END\r
-  END\r
-endef\r
-\r
-export RES_FILE\r
-\r
-$(OBJ_DIR)/nghttp2.rc: Makefile.MSVC\r
-       @echo 'Generating $@...'\r
-       @echo ' /* $(GENERATED). DO NOT EDIT.' > $@\r
-       @echo '  */'                          >> $@\r
-       @echo "$$RES_FILE"                    >> $@\r
-\r
-clean:\r
-       rm -f $(OBJ_DIR)/* includes/nghttp2/nghttp2ver.h\r
-       @echo\r
-\r
-vclean realclean: clean clean_nghttp2_pyd_$(USE_CYTHON)\r
-       - rm -rf $(OBJ_DIR)\r
-       - rm -f .depend.MSVC\r
-\r
-#\r
-# Use gcc to generated the dependencies. No MSVC specific args please!\r
-#\r
-REPLACE_R = 's/\(.*\)\.o: /\n$$(OBJ_DIR)\/r_\1.obj: /'\r
-REPLACE_D = 's/\(.*\)\.o: /\n$$(OBJ_DIR)\/d_\1.obj: /'\r
-\r
-depend: includes/nghttp2/nghttp2ver.h\r
-       @echo '# $(GENERATED). DO NOT EDIT.' > .depend.MSVC\r
-       gcc -MM $(CFLAGS) $(NGHTTP2_SRC)    >> .depend.tmp\r
-       @echo '#'                           >> .depend.MSVC\r
-       @echo '# Release lib objects:'      >> .depend.MSVC\r
-       sed -e $(REPLACE_R) .depend.tmp     >> .depend.MSVC\r
-       @echo '#'                           >> .depend.MSVC\r
-       @echo '# Debug lib objects:'        >> .depend.MSVC\r
-       sed -e $(REPLACE_D) .depend.tmp     >> .depend.MSVC\r
-       rm -f .depend.tmp\r
-\r
--include .depend.MSVC\r
+#
+# GNU Makefile for nghttp2 / MSVC.
+#
+# By G. Vanem <gvanem@yahoo.no> 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.
+#
+THIS_MAKEFILE := $(lastword $(MAKEFILE_LIST))
+
+USE_CYTHON := 0
+#USE_CYTHON := 1
+
+_VERSION       := $(shell grep AC_INIT ../configure.ac | cut -d'[' -f3 | sed -e 's/-DEV//g' -e 's/], //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)'
+
+OBJ_DIR := MSVC_obj
+#SUFFIX :=-vc90-mt-x86
+
+#
+# Where to copy nghttp2.dll + lib + headers to.
+# Note: 'make install' is not in default targets. Do it explicitly.
+#
+TARGET_DIR  ?= ../_VC_ROOT
+VC_ROOT     := $(abspath $(TARGET_DIR))
+INSTALL_BIN := $(VC_ROOT)/bin
+INSTALL_LIB := $(VC_ROOT)/lib
+INSTALL_HDR := $(VC_ROOT)/include
+DLL_R  := $(OBJ_DIR)/nghttp2$(SUFFIX).dll
+DLL_D  := $(OBJ_DIR)/nghttp2d$(SUFFIX).dll
+LIB_R  := $(OBJ_DIR)/nghttp2-static.lib
+LIB_D  := $(OBJ_DIR)/nghttp2d-static.lib
+IMP_R  := $(OBJ_DIR)/nghttp2.lib
+IMP_D  := $(OBJ_DIR)/nghttp2d.lib
+
+#
+# Build for DEBUG-model and RELEASE at the same time.
+#
+TARGETS := $(LIB_R) $(DLL_R) $(IMP_R) \
+          $(LIB_D) $(DLL_D) $(IMP_D)
+
+EXT_LIBS = 
+
+NGHTTP2_PDB_R := $(OBJ_DIR)/nghttp2.pdb
+NGHTTP2_PDB_D := $(OBJ_DIR)/nghttp2d.pdb
+
+CC       = cl
+LD              := link
+AR              := lib
+#CC       := icl
+#LD             := xilink
+#AR             := xilib
+RC              := rc
+CFLAGS   := -I./includes -Dssize_t=long
+
+CFLAGS_R := -nologo -MD  -W3 -Z7 -DBUILDING_NGHTTP2
+CFLAGS_D := -nologo -MDd -W3 -Z7 -DBUILDING_NGHTTP2 \
+           -Ot -D_DEBUG -GF -RTCs -RTCu # -RTCc -GS
+
+LDFLAGS := -nologo -MAP -debug -incremental:no -opt:ref,icf -MANIFEST # -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_rcbuf.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)))
+
+.PHONY: all intro test_ver install copy_headers_and_libs \
+       install_nghttp2_pyd_0 install_nghttp2_pyd_1 \
+       build_nghttp2_pyd_0 build_nghttp2_pyd_1 \
+       clean_nghttp2_pyd_0 clean_nghttp2_pyd_1
+
+
+all: intro includes/nghttp2/nghttp2ver.h $(OBJ_DIR) $(TARGETS) build_nghttp2_pyd_$(USE_CYTHON)
+       @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 \
+         $(TARGETS) \
+         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 -p $(INSTALL_HDR)/nghttp2 $(INSTALL_BIN) $(INSTALL_LIB)
+       cp --update $(addprefix includes/nghttp2/, nghttp2.h nghttp2ver.h)     $(INSTALL_HDR)/nghttp2
+       cp --update $(DLL_R) $(DLL_D) $(NGHTTP2_PDB_R) $(NGHTTP2_PDB_D) $(INSTALL_BIN)
+       cp --update $(IMP_R) $(IMP_D) $(LIB_R) $(LIB_D) $(INSTALL_LIB)
+       @echo
+
+$(LIB_R): $(NGHTTP2_OBJ_R)
+       $(AR) -nologo -out:$@ $^
+       @echo
+
+$(LIB_D): $(NGHTTP2_OBJ_D)
+       $(AR) -nologo -out:$@ $^
+       @echo
+
+
+$(IMP_R): $(DLL_R)
+
+$(DLL_R): $(NGHTTP2_OBJ_R) $(OBJ_DIR)/r_nghttp2.res 
+       $(LD) $(LDFLAGS) -dll -out:$@ -implib:$(IMP_R) $(NGHTTP2_OBJ_R) -PDB:$(NGHTTP2_PDB_R) $(OBJ_DIR)/r_nghttp2.res $(EXT_LIBS)
+       mt -nologo -manifest $@.manifest -outputresource:$@\;2
+       @echo
+
+$(IMP_D): $(DLL_D)
+       
+$(DLL_D): $(NGHTTP2_OBJ_D) $(OBJ_DIR)/d_nghttp2.res 
+       $(LD) $(LDFLAGS) -dll -out:$@ -implib:$(IMP_D) $(NGHTTP2_OBJ_D) -PDB:$(NGHTTP2_PDB_D) $(OBJ_DIR)/d_nghttp2.res $(EXT_LIBS)
+       mt -nologo -manifest $@.manifest -outputresource:$@\;2
+       @echo
+
+
+WIN_OBJDIR:=$(shell cygpath -w $(abspath $(OBJ_DIR)))
+WIN_OBJDIR:=$(subst \,/,$(WIN_OBJDIR))
+
+../python/setup.py: ../python/setup.py.in $(THIS_MAKEFILE)
+       cd ../python ; \
+       echo '# $(GENERATED). DO NOT EDIT.' > setup.py ; \
+       sed -e 's/@top_srcdir@/../'   \
+           -e 's%@top_builddir@%$(WIN_OBJDIR)%' \
+           -e 's/@PACKAGE_VERSION@/$(VERSION)/' setup.py.in >> setup.py ;
+
+build_nghttp2_pyd_0: ;
+
+build_nghttp2_pyd_1: $(addprefix ../python/, setup.py nghttp2.pyx)
+       cd ../python ; \
+       python setup.py build_ext -i -f bdist_wininst
+
+install_nghttp2_pyd_0: ;
+               
+install_nghttp2_pyd_1: $(addprefix ../python/, setup.py nghttp2.pyx)
+       cd ../python ; \
+       pip install .
+
+clean_nghttp2_pyd_0: ;
+
+clean_nghttp2_pyd_1:
+       cd ../python ; \
+       rm -fR build dist
+
+$(OBJ_DIR)/r_%.obj: %.c $(THIS_MAKEFILE)
+       $(CC) $(CFLAGS_R) $(CFLAGS) -Fo$@ -c $<
+       @echo
+
+$(OBJ_DIR)/d_%.obj: %.c $(THIS_MAKEFILE)
+       $(CC) $(CFLAGS_D) $(CFLAGS) -Fo$@ -c $<
+       @echo
+
+$(OBJ_DIR)/r_nghttp2.res: $(OBJ_DIR)/nghttp2.rc $(THIS_MAKEFILE)
+       $(RC) -D_RELEASE -Fo $@ $<
+       @echo
+
+$(OBJ_DIR)/d_nghttp2.res: $(OBJ_DIR)/nghttp2.rc $(THIS_MAKEFILE)
+       $(RC) -D_DEBUG -Fo $@ $<
+       @echo
+
+includes/nghttp2/nghttp2ver.h: includes/nghttp2/nghttp2ver.h.in $(THIS_MAKEFILE)
+       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 <winver.h>
+
+  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
+      END
+    END
+  BLOCK "VarFileInfo"
+  BEGIN
+    VALUE "Translation", 0x409, 1200
+  END
+  END
+endef
+
+export RES_FILE
+
+$(OBJ_DIR)/nghttp2.rc: Makefile.MSVC
+       @echo 'Generating $@...'
+       @echo ' /* $(GENERATED). DO NOT EDIT.' > $@
+       @echo '  */'                          >> $@
+       @echo "$$RES_FILE"                    >> $@
+
+clean:
+       rm -f $(OBJ_DIR)/* includes/nghttp2/nghttp2ver.h
+       @echo
+
+vclean realclean: clean clean_nghttp2_pyd_$(USE_CYTHON)
+       - rm -rf $(OBJ_DIR)
+       - rm -f .depend.MSVC
+
+#
+# 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
index 1ca6bfd..973e337 100644 (file)
@@ -1,7 +1,7 @@
-# Makefile.in generated by automake 1.15 from Makefile.am.
+# Makefile.in generated by automake 1.15.1 from Makefile.am.
 # @configure_input@
 
-# Copyright (C) 1994-2014 Free Software Foundation, Inc.
+# Copyright (C) 1994-2017 Free Software Foundation, Inc.
 
 # This Makefile.in is free software; the Free Software Foundation
 # gives unlimited permission to copy and/or distribute it,
@@ -263,8 +263,6 @@ LIBMRUBY_CFLAGS = @LIBMRUBY_CFLAGS@
 LIBMRUBY_LIBS = @LIBMRUBY_LIBS@
 LIBOBJS = @LIBOBJS@
 LIBS = @LIBS@
-LIBSPDYLAY_CFLAGS = @LIBSPDYLAY_CFLAGS@
-LIBSPDYLAY_LIBS = @LIBSPDYLAY_LIBS@
 LIBTOOL = @LIBTOOL@
 LIBXML2_CFLAGS = @LIBXML2_CFLAGS@
 LIBXML2_LIBS = @LIBXML2_LIBS@
index 1590100..14f8950 100644 (file)
@@ -388,6 +388,11 @@ typedef enum {
    */
   NGHTTP2_ERR_CANCEL = -535,
   /**
+   * When a local endpoint expects to receive SETTINGS frame, it
+   * receives an other type of frame.
+   */
+  NGHTTP2_ERR_SETTINGS_EXPECTED = -536,
+  /**
    * 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
@@ -470,6 +475,15 @@ NGHTTP2_EXTERN void nghttp2_rcbuf_decref(nghttp2_rcbuf *rcbuf);
 NGHTTP2_EXTERN nghttp2_vec nghttp2_rcbuf_get_buf(nghttp2_rcbuf *rcbuf);
 
 /**
+ * @function
+ *
+ * Returns nonzero if the underlying buffer is statically allocated,
+ * and 0 otherwise. This can be useful for language bindings that wish
+ * to avoid creating duplicate strings for these buffers.
+ */
+NGHTTP2_EXTERN int nghttp2_rcbuf_is_static(const nghttp2_rcbuf *rcbuf);
+
+/**
  * @enum
  *
  * The flags for header field name/value pair.
@@ -1741,11 +1755,12 @@ typedef int (*nghttp2_on_header_callback2)(nghttp2_session *session,
  * The parameter and behaviour are similar to
  * :type:`nghttp2_on_header_callback`.  The difference is that this
  * callback is only invoked when a invalid header name/value pair is
- * received which is silently ignored if this callback is not set.
- * Only invalid regular header field are passed to this callback.  In
- * other words, invalid pseudo header field is not passed to this
- * callback.  Also header fields which includes upper cased latter are
- * also treated as error without passing them to this callback.
+ * received which is treated as stream error if this callback is not
+ * set.  Only invalid regular header field are passed to this
+ * callback.  In other words, invalid pseudo header field is not
+ * passed to this callback.  Also header fields which includes upper
+ * cased latter are also treated as error without passing them to this
+ * callback.
  *
  * This callback is only considered if HTTP messaging validation is
  * turned on (which is on by default, see
@@ -1754,10 +1769,13 @@ typedef int (*nghttp2_on_header_callback2)(nghttp2_session *session,
  * With this callback, application inspects the incoming invalid
  * field, and it also can reset stream from this callback by returning
  * :enum:`NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE`.  By default, the
- * error code is :enum:`NGHTTP2_INTERNAL_ERROR`.  To change the error
+ * error code is :enum:`NGHTTP2_PROTOCOL_ERROR`.  To change the error
  * code, call `nghttp2_submit_rst_stream()` with the error code of
  * choice in addition to returning
  * :enum:`NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE`.
+ *
+ * If 0 is returned, the header field is ignored, and the stream is
+ * not reset.
  */
 typedef int (*nghttp2_on_invalid_header_callback)(
     nghttp2_session *session, const nghttp2_frame *frame, const uint8_t *name,
@@ -1974,6 +1992,9 @@ typedef ssize_t (*nghttp2_pack_extension_callback)(nghttp2_session *session,
  * of length |len|.  |len| does not include the sentinel NULL
  * character.
  *
+ * This function is deprecated.  The new application should use
+ * :type:`nghttp2_error_callback2`.
+ *
  * The format of error message may change between nghttp2 library
  * versions.  The application should not depend on the particular
  * format.
@@ -1990,6 +2011,33 @@ typedef ssize_t (*nghttp2_pack_extension_callback)(nghttp2_session *session,
 typedef int (*nghttp2_error_callback)(nghttp2_session *session, const char *msg,
                                       size_t len, void *user_data);
 
+/**
+ * @functypedef
+ *
+ * Callback function invoked when library provides the error code, and
+ * message.  This callback is solely for debugging purpose.
+ * |lib_error_code| is one of error code defined in
+ * :enum:`nghttp2_error`.  The |msg| is typically NULL-terminated
+ * string of length |len|, and intended for human consumption.  |len|
+ * does not include the sentinel NULL character.
+ *
+ * The format of error message may change between nghttp2 library
+ * versions.  The application should not depend on the particular
+ * format.
+ *
+ * Normally, application should return 0 from this callback.  If fatal
+ * error occurred while doing something in this callback, application
+ * should return :enum:`NGHTTP2_ERR_CALLBACK_FAILURE`.  In this case,
+ * library will return immediately with return value
+ * :enum:`NGHTTP2_ERR_CALLBACK_FAILURE`.  Currently, if nonzero value
+ * is returned from this callback, they are treated as
+ * :enum:`NGHTTP2_ERR_CALLBACK_FAILURE`, but application should not
+ * rely on this details.
+ */
+typedef int (*nghttp2_error_callback2)(nghttp2_session *session,
+                                       int lib_error_code, const char *msg,
+                                       size_t len, void *user_data);
+
 struct nghttp2_session_callbacks;
 
 /**
@@ -2254,11 +2302,31 @@ nghttp2_session_callbacks_set_on_extension_chunk_recv_callback(
  *
  * Sets callback function invoked when library tells error message to
  * the application.
+ *
+ * This function is deprecated.  The new application should use
+ * `nghttp2_session_callbacks_set_error_callback2()`.
+ *
+ * If both :type:`nghttp2_error_callback` and
+ * :type:`nghttp2_error_callback2` are set, the latter takes
+ * precedence.
  */
 NGHTTP2_EXTERN void nghttp2_session_callbacks_set_error_callback(
     nghttp2_session_callbacks *cbs, nghttp2_error_callback error_callback);
 
 /**
+ * @function
+ *
+ * Sets callback function invoked when library tells error code, and
+ * message to the application.
+ *
+ * If both :type:`nghttp2_error_callback` and
+ * :type:`nghttp2_error_callback2` are set, the latter takes
+ * precedence.
+ */
+NGHTTP2_EXTERN void nghttp2_session_callbacks_set_error_callback2(
+    nghttp2_session_callbacks *cbs, nghttp2_error_callback2 error_callback2);
+
+/**
  * @functypedef
  *
  * Custom memory allocator to replace malloc().  The |mem_user_data|
@@ -2448,7 +2516,10 @@ nghttp2_option_set_no_recv_client_magic(nghttp2_option *option, int val);
  * <https://tools.ietf.org/html/rfc7540#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.
+ * disable this enforcement.  Please note that disabling this feature
+ * does not change the fundamental client and server model of HTTP.
+ * That is, even if the validation is disabled, only client can send
+ * requests.
  */
 NGHTTP2_EXTERN void nghttp2_option_set_no_http_messaging(nghttp2_option *option,
                                                          int val);
@@ -3013,6 +3084,16 @@ nghttp2_session_set_stream_user_data(nghttp2_session *session,
 /**
  * @function
  *
+ * Sets |user_data| to |session|, overwriting the existing user data
+ * specified in `nghttp2_session_client_new()`, or
+ * `nghttp2_session_server_new()`.
+ */
+NGHTTP2_EXTERN void nghttp2_session_set_user_data(nghttp2_session *session,
+                                                  void *user_data);
+
+/**
+ * @function
+ *
  * Returns the number of frames in the outbound queue.  This does not
  * include the deferred DATA frames.
  */
@@ -3802,9 +3883,8 @@ nghttp2_submit_response(nghttp2_session *session, int32_t stream_id,
  * Submits trailer fields 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
- * pseudo-header fields (header field whose name starts with ":") in
- * |nva|.
+ * |nvlen| elements.  The application must not include pseudo-header
+ * fields (headers whose names 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
@@ -4687,8 +4767,8 @@ nghttp2_hd_deflate_change_table_size(nghttp2_hd_deflater *deflater,
  *
  * 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:
+ * This function returns the number of bytes written to |buf| if it
+ * succeeds, or one of the following negative error codes:
  *
  * :enum:`NGHTTP2_ERR_NOMEM`
  *     Out of memory.
@@ -4719,8 +4799,8 @@ NGHTTP2_EXTERN ssize_t nghttp2_hd_deflate_hd(nghttp2_hd_deflater *deflater,
  *
  * 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:
+ * This function returns the number of bytes written to |vec| if it
+ * succeeds, or one of the following negative error codes:
  *
  * :enum:`NGHTTP2_ERR_NOMEM`
  *     Out of memory.
index 72f8d63..a3271d9 100644 (file)
@@ -29,7 +29,7 @@
  * @macro
  * Version number of the nghttp2 library release
  */
-#define NGHTTP2_VERSION "1.20.0"
+#define NGHTTP2_VERSION "1.31.1"
 
 /**
  * @macro
@@ -37,6 +37,6 @@
  * 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 0x011400
+#define NGHTTP2_VERSION_NUM 0x011f01
 
 #endif /* NGHTTP2VER_H */
index 06ab1e4..9f484a2 100644 (file)
@@ -398,7 +398,7 @@ int nghttp2_bufs_advance(nghttp2_bufs *bufs);
 void nghttp2_bufs_seek_last_present(nghttp2_bufs *bufs);
 
 /*
- * Returns nonzero if bufs->cur->next is not emtpy.
+ * Returns nonzero if bufs->cur->next is not empty.
  */
 int nghttp2_bufs_next_present(nghttp2_bufs *bufs);
 
index b6cf595..3c38214 100644 (file)
@@ -168,3 +168,8 @@ void nghttp2_session_callbacks_set_error_callback(
     nghttp2_session_callbacks *cbs, nghttp2_error_callback error_callback) {
   cbs->error_callback = error_callback;
 }
+
+void nghttp2_session_callbacks_set_error_callback2(
+    nghttp2_session_callbacks *cbs, nghttp2_error_callback2 error_callback2) {
+  cbs->error_callback2 = error_callback2;
+}
index 5967524..b607bbb 100644 (file)
@@ -119,6 +119,7 @@ struct nghttp2_session_callbacks {
   nghttp2_unpack_extension_callback unpack_extension_callback;
   nghttp2_on_extension_chunk_recv_callback on_extension_chunk_recv_callback;
   nghttp2_error_callback error_callback;
+  nghttp2_error_callback2 error_callback2;
 };
 
 #endif /* NGHTTP2_CALLBACKS_H */
index 3514ded..cb27797 100644 (file)
@@ -53,6 +53,8 @@ void nghttp2_set_debug_vprintf_callback(
 #else /* !DEBUGBUILD */
 
 void nghttp2_set_debug_vprintf_callback(
-    nghttp2_debug_vprintf_callback debug_vprintf_callback _U_) {}
+    nghttp2_debug_vprintf_callback debug_vprintf_callback) {
+  (void)debug_vprintf_callback;
+}
 
 #endif /* !DEBUGBUILD */
index 85f2691..fa7cb69 100644 (file)
@@ -87,7 +87,7 @@ void nghttp2_frame_priority_init(nghttp2_priority *frame, int32_t stream_id,
   frame->pri_spec = *pri_spec;
 }
 
-void nghttp2_frame_priority_free(nghttp2_priority *frame _U_) {}
+void nghttp2_frame_priority_free(nghttp2_priority *frame) { (void)frame; }
 
 void nghttp2_frame_rst_stream_init(nghttp2_rst_stream *frame, int32_t stream_id,
                                    uint32_t error_code) {
@@ -96,7 +96,7 @@ void nghttp2_frame_rst_stream_init(nghttp2_rst_stream *frame, int32_t stream_id,
   frame->error_code = error_code;
 }
 
-void nghttp2_frame_rst_stream_free(nghttp2_rst_stream *frame _U_) {}
+void nghttp2_frame_rst_stream_free(nghttp2_rst_stream *frame) { (void)frame; }
 
 void nghttp2_frame_settings_init(nghttp2_settings *frame, uint8_t flags,
                                  nghttp2_settings_entry *iv, size_t niv) {
@@ -137,7 +137,7 @@ void nghttp2_frame_ping_init(nghttp2_ping *frame, uint8_t flags,
   }
 }
 
-void nghttp2_frame_ping_free(nghttp2_ping *frame _U_) {}
+void nghttp2_frame_ping_free(nghttp2_ping *frame) { (void)frame; }
 
 void nghttp2_frame_goaway_init(nghttp2_goaway *frame, int32_t last_stream_id,
                                uint32_t error_code, uint8_t *opaque_data,
@@ -163,7 +163,9 @@ void nghttp2_frame_window_update_init(nghttp2_window_update *frame,
   frame->reserved = 0;
 }
 
-void nghttp2_frame_window_update_free(nghttp2_window_update *frame _U_) {}
+void nghttp2_frame_window_update_free(nghttp2_window_update *frame) {
+  (void)frame;
+}
 
 size_t nghttp2_frame_trail_padlen(nghttp2_frame *frame, size_t padlen) {
   /* We have iframe->padlen == 0, but iframe->frame.hd.flags may have
@@ -183,7 +185,7 @@ void nghttp2_frame_data_init(nghttp2_data *frame, uint8_t flags,
   frame->padlen = 0;
 }
 
-void nghttp2_frame_data_free(nghttp2_data *frame _U_) {}
+void nghttp2_frame_data_free(nghttp2_data *frame) { (void)frame; }
 
 void nghttp2_frame_extension_init(nghttp2_extension *frame, uint8_t type,
                                   uint8_t flags, int32_t stream_id,
@@ -192,7 +194,7 @@ void nghttp2_frame_extension_init(nghttp2_extension *frame, uint8_t type,
   frame->payload = payload;
 }
 
-void nghttp2_frame_extension_free(nghttp2_extension *frame _U_) {}
+void nghttp2_frame_extension_free(nghttp2_extension *frame) { (void)frame; }
 
 void nghttp2_frame_altsvc_init(nghttp2_extension *frame, int32_t stream_id,
                                uint8_t *origin, size_t origin_len,
@@ -213,6 +215,9 @@ void nghttp2_frame_altsvc_free(nghttp2_extension *frame, nghttp2_mem *mem) {
   nghttp2_ext_altsvc *altsvc;
 
   altsvc = frame->payload;
+  if (altsvc == NULL) {
+    return;
+  }
   /* We use the same buffer for altsvc->origin and
      altsvc->field_value. */
   nghttp2_mem_free(mem, altsvc->origin);
@@ -346,9 +351,7 @@ void nghttp2_frame_pack_priority_spec(uint8_t *buf,
 }
 
 void nghttp2_frame_unpack_priority_spec(nghttp2_priority_spec *pri_spec,
-                                        uint8_t flags _U_,
-                                        const uint8_t *payload,
-                                        size_t payloadlen _U_) {
+                                        const uint8_t *payload) {
   int32_t dep_stream_id;
   uint8_t exclusive;
   int32_t weight;
@@ -361,11 +364,9 @@ void nghttp2_frame_unpack_priority_spec(nghttp2_priority_spec *pri_spec,
 }
 
 int nghttp2_frame_unpack_headers_payload(nghttp2_headers *frame,
-                                         const uint8_t *payload,
-                                         size_t payloadlen) {
+                                         const uint8_t *payload) {
   if (frame->hd.flags & NGHTTP2_FLAG_PRIORITY) {
-    nghttp2_frame_unpack_priority_spec(&frame->pri_spec, frame->hd.flags,
-                                       payload, payloadlen);
+    nghttp2_frame_unpack_priority_spec(&frame->pri_spec, payload);
   } else {
     nghttp2_priority_spec_default_init(&frame->pri_spec);
   }
@@ -397,10 +398,8 @@ int nghttp2_frame_pack_priority(nghttp2_bufs *bufs, nghttp2_priority *frame) {
 }
 
 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);
+                                           const uint8_t *payload) {
+  nghttp2_frame_unpack_priority_spec(&frame->pri_spec, payload);
 }
 
 int nghttp2_frame_pack_rst_stream(nghttp2_bufs *bufs,
@@ -424,8 +423,7 @@ int nghttp2_frame_pack_rst_stream(nghttp2_bufs *bufs,
 }
 
 void nghttp2_frame_unpack_rst_stream_payload(nghttp2_rst_stream *frame,
-                                             const uint8_t *payload,
-                                             size_t payloadlen _U_) {
+                                             const uint8_t *payload) {
   frame->error_code = nghttp2_get_uint32(payload);
 }
 
@@ -540,8 +538,7 @@ int nghttp2_frame_pack_push_promise(nghttp2_bufs *bufs,
 }
 
 int nghttp2_frame_unpack_push_promise_payload(nghttp2_push_promise *frame,
-                                              const uint8_t *payload,
-                                              size_t payloadlen _U_) {
+                                              const uint8_t *payload) {
   frame->promised_stream_id =
       nghttp2_get_uint32(payload) & NGHTTP2_STREAM_ID_MASK;
   frame->nva = NULL;
@@ -569,8 +566,7 @@ int nghttp2_frame_pack_ping(nghttp2_bufs *bufs, nghttp2_ping *frame) {
 }
 
 void nghttp2_frame_unpack_ping_payload(nghttp2_ping *frame,
-                                       const uint8_t *payload,
-                                       size_t payloadlen _U_) {
+                                       const uint8_t *payload) {
   memcpy(frame->opaque_data, payload, sizeof(frame->opaque_data));
 }
 
@@ -607,7 +603,6 @@ int nghttp2_frame_pack_goaway(nghttp2_bufs *bufs, nghttp2_goaway *frame) {
 
 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;
@@ -643,8 +638,8 @@ int nghttp2_frame_unpack_goaway_payload2(nghttp2_goaway *frame,
     memcpy(var_gift_payload, payload + 8, var_gift_payloadlen);
   }
 
-  nghttp2_frame_unpack_goaway_payload(frame, payload, payloadlen,
-                                      var_gift_payload, var_gift_payloadlen);
+  nghttp2_frame_unpack_goaway_payload(frame, payload, var_gift_payload,
+                                      var_gift_payloadlen);
 
   return 0;
 }
@@ -670,8 +665,7 @@ int nghttp2_frame_pack_window_update(nghttp2_bufs *bufs,
 }
 
 void nghttp2_frame_unpack_window_update_payload(nghttp2_window_update *frame,
-                                                const uint8_t *payload,
-                                                size_t payloadlen _U_) {
+                                                const uint8_t *payload) {
   frame->window_size_increment =
       nghttp2_get_uint32(payload) & NGHTTP2_WINDOW_SIZE_INCREMENT_MASK;
 }
@@ -681,6 +675,9 @@ int nghttp2_frame_pack_altsvc(nghttp2_bufs *bufs, nghttp2_extension *frame) {
   nghttp2_buf *buf;
   nghttp2_ext_altsvc *altsvc;
 
+  /* This is required with --disable-assert. */
+  (void)rv;
+
   altsvc = frame->payload;
 
   buf = &bufs->head->buf;
index 58d3d13..35ca214 100644 (file)
@@ -70,7 +70,9 @@
 #define NGHTTP2_MAX_PADLEN 256
 
 /* Union of extension frame payload */
-typedef union { nghttp2_ext_altsvc altsvc; } nghttp2_ext_frame_payload;
+typedef union {
+  nghttp2_ext_altsvc altsvc;
+} nghttp2_ext_frame_payload;
 
 void nghttp2_frame_pack_frame_hd(uint8_t *buf, const nghttp2_frame_hd *hd);
 
@@ -104,8 +106,7 @@ void nghttp2_frame_pack_priority_spec(uint8_t *buf,
  * 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);
+                                        const uint8_t *payload);
 
 /*
  * Returns the offset from the HEADERS frame payload where the
@@ -144,8 +145,7 @@ int nghttp2_frame_pack_headers(nghttp2_bufs *bufs, nghttp2_headers *frame,
  * This function always succeeds and returns 0.
  */
 int nghttp2_frame_unpack_headers_payload(nghttp2_headers *frame,
-                                         const uint8_t *payload,
-                                         size_t payloadlen);
+                                         const uint8_t *payload);
 
 /*
  * Packs PRIORITY frame |frame| in wire format and store it in
@@ -162,8 +162,7 @@ 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);
+                                           const uint8_t *payload);
 
 /*
  * Packs RST_STREAM frame |frame| in wire frame format and store it in
@@ -181,8 +180,7 @@ int nghttp2_frame_pack_rst_stream(nghttp2_bufs *bufs,
  * 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);
+                                             const uint8_t *payload);
 
 /*
  * Packs SETTINGS frame |frame| in wire format and store it in
@@ -273,8 +271,7 @@ int nghttp2_frame_pack_push_promise(nghttp2_bufs *bufs,
  *     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);
+                                              const uint8_t *payload);
 
 /*
  * Packs PING frame |frame| in wire format and store it in
@@ -291,8 +288,7 @@ 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);
+                                       const uint8_t *payload);
 
 /*
  * Packs GOAWAY frame |frame| in wire format and store it in |bufs|.
@@ -321,7 +317,6 @@ int nghttp2_frame_pack_goaway(nghttp2_bufs *bufs, nghttp2_goaway *frame);
  */
 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);
 
@@ -356,8 +351,7 @@ int nghttp2_frame_pack_window_update(nghttp2_bufs *bufs,
  * 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);
+                                                const uint8_t *payload);
 
 /*
  * Packs ALTSVC frame |frame| in wire frame format and store it in
index f107162..1eb3be3 100644 (file)
@@ -662,9 +662,9 @@ static int hd_context_init(nghttp2_hd_context *context, nghttp2_mem *mem) {
   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);
+  rv = hd_ringbuf_init(
+      &context->hd_table,
+      context->hd_table_bufsize_max / NGHTTP2_HD_ENTRY_OVERHEAD, mem);
   if (rv != 0) {
     return rv;
   }
@@ -1537,10 +1537,11 @@ ssize_t nghttp2_hd_deflate_hd_vec(nghttp2_hd_deflater *deflater,
   return (ssize_t)buflen;
 }
 
-size_t nghttp2_hd_deflate_bound(nghttp2_hd_deflater *deflater _U_,
+size_t nghttp2_hd_deflate_bound(nghttp2_hd_deflater *deflater,
                                 const nghttp2_nv *nva, size_t nvlen) {
   size_t n = 0;
   size_t i;
+  (void)deflater;
 
   /* Possible Maximum Header Table Size Change.  Encoding (1u << 31) -
      1 using 4 bit prefix requires 6 bytes.  We may emit this at most
index 458edaf..760bfbc 100644 (file)
@@ -211,7 +211,9 @@ typedef struct {
 
 #define HD_MAP_SIZE 128
 
-typedef struct { nghttp2_hd_entry *table[HD_MAP_SIZE]; } nghttp2_hd_map;
+typedef struct {
+  nghttp2_hd_entry *table[HD_MAP_SIZE];
+} nghttp2_hd_map;
 
 struct nghttp2_hd_deflater {
   nghttp2_hd_context ctx;
@@ -313,7 +315,7 @@ void nghttp2_hd_deflate_free(nghttp2_hd_deflater *deflater);
  *
  * 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.
+ * function fails and returns NGHTTP2_ERR_HEADER_COMP.
  *
  * After this function returns, it is safe to delete the |nva|.
  *
index b00c907..3b282c7 100644 (file)
@@ -322,6 +322,9 @@ const char *nghttp2_strerror(int error_code) {
     return "Internal error";
   case NGHTTP2_ERR_CANCEL:
     return "Cancel";
+  case NGHTTP2_ERR_SETTINGS_EXPECTED:
+    return "When a local endpoint expects to receive SETTINGS frame, it "
+           "receives an other type of frame";
   case NGHTTP2_ERR_NOMEM:
     return "Out of memory";
   case NGHTTP2_ERR_CALLBACK_FAILURE:
index 877f5e3..8240f8d 100644 (file)
@@ -521,8 +521,10 @@ int nghttp2_http_on_response_headers(nghttp2_stream *stream) {
   return 0;
 }
 
-int nghttp2_http_on_trailer_headers(nghttp2_stream *stream _U_,
+int nghttp2_http_on_trailer_headers(nghttp2_stream *stream,
                                     nghttp2_frame *frame) {
+  (void)stream;
+
   if ((frame->hd.flags & NGHTTP2_FLAG_END_STREAM) == 0) {
     return -1;
   }
index e0b7c29..6a449cf 100644 (file)
  */
 #include "nghttp2_mem.h"
 
-static void *default_malloc(size_t size, void *mem_user_data _U_) {
+static void *default_malloc(size_t size, void *mem_user_data) {
+  (void)mem_user_data;
+
   return malloc(size);
 }
 
-static void default_free(void *ptr, void *mem_user_data _U_) { free(ptr); }
+static void default_free(void *ptr, void *mem_user_data) {
+  (void)mem_user_data;
+
+  free(ptr);
+}
+
+static void *default_calloc(size_t nmemb, size_t size, void *mem_user_data) {
+  (void)mem_user_data;
 
-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_) {
+static void *default_realloc(void *ptr, size_t size, void *mem_user_data) {
+  (void)mem_user_data;
+
   return realloc(ptr, size);
 }
 
index 8bda776..89a8a92 100644 (file)
@@ -112,7 +112,7 @@ struct nghttp2_outbound_item {
   nghttp2_ext_frame_payload ext_frame_payload;
   nghttp2_aux_data aux_data;
   /* The priority used in priority comparion.  Smaller is served
-     ealier.  For PING, SETTINGS and non-DATA frames (excluding
+     earlier.  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
index 6b0ecfb..71cf96a 100644 (file)
 
 /* Implementation of priority queue */
 
-typedef struct { size_t index; } nghttp2_pq_entry;
+typedef struct {
+  size_t index;
+} nghttp2_pq_entry;
 
 typedef struct {
   /* The pointer to the pointer to the item stored */
   nghttp2_pq_entry **q;
   /* Memory allocator */
   nghttp2_mem *mem;
-  /* The number of items sotred */
+  /* The number of items stored */
   size_t length;
   /* The maximum number of items this pq can store. This is
      automatically extended when length is reached to this value. */
@@ -71,7 +73,7 @@ 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
+ * This function returns 0 if it succeeds, or one of the following
  * negative error codes:
  *
  * NGHTTP2_ERR_NOMEM
index d872b07..c7eb753 100644 (file)
@@ -36,7 +36,9 @@ typedef struct nghttp2_queue_cell {
   struct nghttp2_queue_cell *next;
 } nghttp2_queue_cell;
 
-typedef struct { nghttp2_queue_cell *front, *back; } nghttp2_queue;
+typedef struct {
+  nghttp2_queue_cell *front, *back;
+} nghttp2_queue;
 
 void nghttp2_queue_init(nghttp2_queue *queue);
 void nghttp2_queue_free(nghttp2_queue *queue);
index 24f561a..7e7814d 100644 (file)
@@ -96,3 +96,7 @@ nghttp2_vec nghttp2_rcbuf_get_buf(nghttp2_rcbuf *rcbuf) {
   nghttp2_vec res = {rcbuf->base, rcbuf->len};
   return res;
 }
+
+int nghttp2_rcbuf_is_static(const nghttp2_rcbuf *rcbuf) {
+  return rcbuf->ref == -1;
+}
index cc69e11..c58f059 100644 (file)
@@ -148,14 +148,16 @@ static int check_ext_type_set(const uint8_t *ext_types, uint8_t type) {
 }
 
 static int session_call_error_callback(nghttp2_session *session,
-                                       const char *fmt, ...) {
+                                       int lib_error_code, const char *fmt,
+                                       ...) {
   size_t bufsize;
   va_list ap;
   char *buf;
   int rv;
   nghttp2_mem *mem;
 
-  if (!session->callbacks.error_callback) {
+  if (!session->callbacks.error_callback &&
+      !session->callbacks.error_callback2) {
     return 0;
   }
 
@@ -189,8 +191,13 @@ static int session_call_error_callback(nghttp2_session *session,
     return 0;
   }
 
-  rv = session->callbacks.error_callback(session, buf, (size_t)rv,
-                                         session->user_data);
+  if (session->callbacks.error_callback2) {
+    rv = session->callbacks.error_callback2(session, lib_error_code, buf,
+                                            (size_t)rv, session->user_data);
+  } else {
+    rv = session->callbacks.error_callback(session, buf, (size_t)rv,
+                                           session->user_data);
+  }
 
   nghttp2_mem_free(mem, buf);
 
@@ -541,9 +548,8 @@ static int session_new(nghttp2_session **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) {
+    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 {
@@ -682,6 +688,7 @@ static int inflight_settings_new(nghttp2_inflight_settings **settings_ptr,
   if (niv > 0) {
     (*settings_ptr)->iv = nghttp2_frame_iv_copy(iv, niv, mem);
     if (!(*settings_ptr)->iv) {
+      nghttp2_mem_free(mem, *settings_ptr);
       return NGHTTP2_ERR_NOMEM;
     }
   } else {
@@ -1523,13 +1530,14 @@ static int session_predicate_response_headers_send(nghttp2_session *session,
   if (nghttp2_session_is_my_stream_id(session, stream->stream_id)) {
     return NGHTTP2_ERR_INVALID_STREAM_ID;
   }
-  if (stream->state == NGHTTP2_STREAM_OPENING) {
+  switch (stream->state) {
+  case NGHTTP2_STREAM_OPENING:
     return 0;
-  }
-  if (stream->state == NGHTTP2_STREAM_CLOSING) {
+  case NGHTTP2_STREAM_CLOSING:
     return NGHTTP2_ERR_STREAM_CLOSING;
+  default:
+    return NGHTTP2_ERR_INVALID_STREAM_STATE;
   }
-  return NGHTTP2_ERR_INVALID_STREAM_STATE;
 }
 
 /*
@@ -1572,9 +1580,6 @@ session_predicate_push_response_headers_send(nghttp2_session *session,
   if (stream->state != NGHTTP2_STREAM_RESERVED) {
     return NGHTTP2_ERR_PROTO;
   }
-  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;
   }
@@ -1609,19 +1614,18 @@ static int session_predicate_headers_send(nghttp2_session *session,
     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) {
+
+  switch (stream->state) {
+  case NGHTTP2_STREAM_OPENED:
     return 0;
-  }
-  if (stream->state == NGHTTP2_STREAM_CLOSING) {
+  case NGHTTP2_STREAM_CLOSING:
     return NGHTTP2_ERR_STREAM_CLOSING;
+  default:
+    if (nghttp2_session_is_my_stream_id(session, stream->stream_id)) {
+      return 0;
+    }
+    return NGHTTP2_ERR_INVALID_STREAM_STATE;
   }
-  return NGHTTP2_ERR_INVALID_STREAM_STATE;
 }
 
 /*
@@ -2067,14 +2071,6 @@ static int session_prep_frame(nghttp2_session *session,
       /* We don't call nghttp2_session_adjust_closed_stream() here,
          since we don't keep closed stream in client side */
 
-      estimated_payloadlen = session_estimate_headers_payload(
-          session, frame->headers.nva, frame->headers.nvlen,
-          NGHTTP2_PRIORITY_SPECLEN);
-
-      if (estimated_payloadlen > session->max_send_header_block_length) {
-        return NGHTTP2_ERR_FRAME_SIZE_ERROR;
-      }
-
       rv = session_predicate_request_headers_send(session, item);
       if (rv != 0) {
         return rv;
@@ -2086,14 +2082,6 @@ static int session_prep_frame(nghttp2_session *session,
     } else {
       nghttp2_stream *stream;
 
-      estimated_payloadlen = session_estimate_headers_payload(
-          session, frame->headers.nva, frame->headers.nvlen,
-          NGHTTP2_PRIORITY_SPECLEN);
-
-      if (estimated_payloadlen > session->max_send_header_block_length) {
-        return NGHTTP2_ERR_FRAME_SIZE_ERROR;
-      }
-
       stream = nghttp2_session_get_stream(session, frame->hd.stream_id);
 
       if (stream && stream->state == NGHTTP2_STREAM_RESERVED) {
@@ -2120,6 +2108,14 @@ static int session_prep_frame(nghttp2_session *session,
       }
     }
 
+    estimated_payloadlen = session_estimate_headers_payload(
+        session, frame->headers.nva, frame->headers.nvlen,
+        NGHTTP2_PRIORITY_SPECLEN);
+
+    if (estimated_payloadlen > session->max_send_header_block_length) {
+      return NGHTTP2_ERR_FRAME_SIZE_ERROR;
+    }
+
     rv = nghttp2_frame_pack_headers(&session->aob.framebufs, &frame->headers,
                                     &session->hd_deflater);
 
@@ -2189,18 +2185,11 @@ static int session_prep_frame(nghttp2_session *session,
     nghttp2_stream *stream;
     size_t estimated_payloadlen;
 
-    estimated_payloadlen = session_estimate_headers_payload(
-        session, frame->push_promise.nva, frame->push_promise.nvlen, 0);
-
-    if (estimated_payloadlen > session->max_send_header_block_length) {
-      return NGHTTP2_ERR_FRAME_SIZE_ERROR;
-    }
-
     /* stream could be NULL if associated stream was already
        closed. */
     stream = nghttp2_session_get_stream(session, frame->hd.stream_id);
 
-    /* predicte should fail if stream is NULL. */
+    /* predicate should fail if stream is NULL. */
     rv = session_predicate_push_promise_send(session, stream);
     if (rv != 0) {
       return rv;
@@ -2208,6 +2197,13 @@ static int session_prep_frame(nghttp2_session *session,
 
     assert(stream);
 
+    estimated_payloadlen = session_estimate_headers_payload(
+        session, frame->push_promise.nva, frame->push_promise.nvlen, 0);
+
+    if (estimated_payloadlen > session->max_send_header_block_length) {
+      return NGHTTP2_ERR_FRAME_SIZE_ERROR;
+    }
+
     rv = nghttp2_frame_pack_push_promise(
         &session->aob.framebufs, &frame->push_promise, &session->hd_deflater);
     if (rv != 0) {
@@ -2229,8 +2225,9 @@ static int session_prep_frame(nghttp2_session *session,
       assert(session->obq_flood_counter_ > 0);
       --session->obq_flood_counter_;
     }
-
-    if (session_is_closing(session)) {
+    /* PING frame is allowed to be sent unless termination GOAWAY is
+       sent */
+    if (session->goaway_flags & NGHTTP2_GOAWAY_TERM_ON_SEND) {
       return NGHTTP2_ERR_SESSION_CLOSING;
     }
     nghttp2_frame_pack_ping(&session->aob.framebufs, &frame->ping);
@@ -2421,19 +2418,16 @@ static int session_close_stream_on_goaway(nghttp2_session *session,
   nghttp2_stream *stream, *next_stream;
   nghttp2_close_stream_on_goaway_arg arg = {session, NULL, last_stream_id,
                                             incoming};
-  uint32_t error_code;
 
   rv = nghttp2_map_each(&session->streams, find_stream_on_goaway_func, &arg);
   assert(rv == 0);
 
-  error_code =
-      session->server && incoming ? NGHTTP2_REFUSED_STREAM : NGHTTP2_CANCEL;
-
   stream = arg.head;
   while (stream) {
     next_stream = stream->closed_next;
     stream->closed_next = NULL;
-    rv = nghttp2_session_close_stream(session, stream->stream_id, error_code);
+    rv = nghttp2_session_close_stream(session, stream->stream_id,
+                                      NGHTTP2_REFUSED_STREAM);
 
     /* stream may be deleted here */
 
@@ -3331,7 +3325,7 @@ static int session_call_on_invalid_header(nghttp2_session *session,
         session, frame, nv->name->base, nv->name->len, nv->value->base,
         nv->value->len, nv->flags, session->user_data);
   } else {
-    return 0;
+    return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
   }
 
   if (rv == NGHTTP2_ERR_PAUSE || rv == NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE) {
@@ -3394,8 +3388,7 @@ static int session_call_unpack_extension_callback(nghttp2_session *session) {
  * NGHTTP2_ERR_NOMEM
  *   Out of memory.
  */
-static int session_handle_frame_size_error(nghttp2_session *session,
-                                           nghttp2_frame *frame _U_) {
+static int session_handle_frame_size_error(nghttp2_session *session) {
   /* 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);
@@ -3422,6 +3415,27 @@ static uint32_t get_error_code_from_lib_error_code(int lib_error_code) {
   }
 }
 
+/*
+ * Calls on_invalid_frame_recv_callback if it is set to |session|.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGHTTP2_ERR_CALLBACK_FAILURE
+ *   User defined callback function fails.
+ */
+static int session_call_on_invalid_frame_recv_callback(nghttp2_session *session,
+                                                       nghttp2_frame *frame,
+                                                       int lib_error_code) {
+  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_stream2(nghttp2_session *session,
                                           int32_t stream_id,
                                           nghttp2_frame *frame,
@@ -3579,14 +3593,46 @@ static int inflate_header_block(nghttp2_session *session, nghttp2_frame *frame,
       if (subject_stream && session_enforce_http_messaging(session)) {
         rv = nghttp2_http_on_header(session, subject_stream, frame, &nv,
                                     trailer);
+
+        if (rv == NGHTTP2_ERR_IGN_HTTP_HEADER) {
+          /* Don't overwrite rv here */
+          int rv2;
+
+          rv2 = session_call_on_invalid_header(session, frame, &nv);
+          if (rv2 == NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE) {
+            rv = NGHTTP2_ERR_HTTP_HEADER;
+          } else {
+            if (rv2 != 0) {
+              return rv2;
+            }
+
+            /* header is ignored */
+            DEBUGF("recv: HTTP ignored: type=%u, id=%d, header %.*s: %.*s\n",
+                   frame->hd.type, frame->hd.stream_id, (int)nv.name->len,
+                   nv.name->base, (int)nv.value->len, nv.value->base);
+
+            rv2 = session_call_error_callback(
+                session, NGHTTP2_ERR_HTTP_HEADER,
+                "Ignoring received invalid HTTP header field: frame type: "
+                "%u, stream: %d, name: [%.*s], value: [%.*s]",
+                frame->hd.type, frame->hd.stream_id, (int)nv.name->len,
+                nv.name->base, (int)nv.value->len, nv.value->base);
+
+            if (nghttp2_is_fatal(rv2)) {
+              return rv2;
+            }
+          }
+        }
+
         if (rv == NGHTTP2_ERR_HTTP_HEADER) {
           DEBUGF("recv: HTTP error: type=%u, id=%d, header %.*s: %.*s\n",
                  frame->hd.type, frame->hd.stream_id, (int)nv.name->len,
                  nv.name->base, (int)nv.value->len, nv.value->base);
 
           rv = session_call_error_callback(
-              session, "Invalid HTTP header field was received: frame type: "
-                       "%u, stream: %d, name: [%.*s], value: [%.*s]",
+              session, NGHTTP2_ERR_HTTP_HEADER,
+              "Invalid HTTP header field was received: frame type: "
+              "%u, stream: %d, name: [%.*s], value: [%.*s]",
               frame->hd.type, frame->hd.stream_id, (int)nv.name->len,
               nv.name->base, (int)nv.value->len, nv.value->base);
 
@@ -3602,34 +3648,6 @@ static int inflate_header_block(nghttp2_session *session, nghttp2_frame *frame,
           }
           return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
         }
-
-        if (rv == NGHTTP2_ERR_IGN_HTTP_HEADER) {
-          /* Don't overwrite rv here */
-          int rv2;
-
-          rv2 = session_call_on_invalid_header(session, frame, &nv);
-          /* This handles NGHTTP2_ERR_PAUSE and
-             NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE as well */
-          if (rv2 != 0) {
-            return rv2;
-          }
-
-          /* header is ignored */
-          DEBUGF("recv: HTTP ignored: type=%u, id=%d, header %.*s: %.*s\n",
-                 frame->hd.type, frame->hd.stream_id, (int)nv.name->len,
-                 nv.name->base, (int)nv.value->len, nv.value->base);
-
-          rv2 = session_call_error_callback(
-              session,
-              "Ignoring received invalid HTTP header field: frame type: "
-              "%u, stream: %d, name: [%.*s], value: [%.*s]",
-              frame->hd.type, frame->hd.stream_id, (int)nv.name->len,
-              nv.name->base, (int)nv.value->len, nv.value->base);
-
-          if (nghttp2_is_fatal(rv2)) {
-            return rv2;
-          }
-        }
       }
       if (rv == 0) {
         rv = session_call_on_header(session, frame, &nv);
@@ -3768,7 +3786,7 @@ int nghttp2_session_on_request_headers_received(nghttp2_session *session,
         session, frame, NGHTTP2_ERR_PROTO, "request HEADERS: stream_id == 0");
   }
 
-  /* If client recieves idle stream from server, it is invalid
+  /* If client receives 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) {
@@ -3991,8 +4009,7 @@ static int session_process_headers_frame(nghttp2_session *session) {
   nghttp2_frame *frame = &iframe->frame;
   nghttp2_stream *stream;
 
-  rv = nghttp2_frame_unpack_headers_payload(&frame->headers, iframe->sbuf.pos,
-                                            nghttp2_buf_len(&iframe->sbuf));
+  rv = nghttp2_frame_unpack_headers_payload(&frame->headers, iframe->sbuf.pos);
 
   if (rv != 0) {
     return nghttp2_session_terminate_session_with_reason(
@@ -4082,8 +4099,7 @@ 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));
+  nghttp2_frame_unpack_priority_payload(&frame->priority, iframe->sbuf.pos);
 
   return nghttp2_session_on_priority_received(session, frame);
 }
@@ -4124,8 +4140,7 @@ 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));
+  nghttp2_frame_unpack_rst_stream_payload(&frame->rst_stream, iframe->sbuf.pos);
 
   return nghttp2_session_on_rst_stream_received(session, frame);
 }
@@ -4597,8 +4612,8 @@ static int session_process_push_promise_frame(nghttp2_session *session) {
   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));
+  rv = nghttp2_frame_unpack_push_promise_payload(&frame->push_promise,
+                                                 iframe->sbuf.pos);
 
   if (rv != 0) {
     return nghttp2_session_terminate_session_with_reason(
@@ -4632,8 +4647,7 @@ 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));
+  nghttp2_frame_unpack_ping_payload(&frame->ping, iframe->sbuf.pos);
 
   return nghttp2_session_on_ping_received(session, frame);
 }
@@ -4674,9 +4688,9 @@ 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_frame_unpack_goaway_payload(&frame->goaway, iframe->sbuf.pos,
+                                      iframe->lbuf.pos,
+                                      nghttp2_buf_len(&iframe->lbuf));
 
   nghttp2_buf_wrap_init(&iframe->lbuf, NULL, 0);
 
@@ -4759,8 +4773,8 @@ 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));
+  nghttp2_frame_unpack_window_update_payload(&frame->window_update,
+                                             iframe->sbuf.pos);
 
   return nghttp2_session_on_window_update_received(session, frame);
 }
@@ -4776,11 +4790,13 @@ int nghttp2_session_on_altsvc_received(nghttp2_session *session,
 
   if (frame->hd.stream_id == 0) {
     if (altsvc->origin_len == 0) {
-      return 0;
+      return session_call_on_invalid_frame_recv_callback(session, frame,
+                                                         NGHTTP2_ERR_PROTO);
     }
   } else {
     if (altsvc->origin_len > 0) {
-      return 0;
+      return session_call_on_invalid_frame_recv_callback(session, frame,
+                                                         NGHTTP2_ERR_PROTO);
     }
 
     stream = nghttp2_session_get_stream(session, frame->hd.stream_id);
@@ -4793,6 +4809,11 @@ int nghttp2_session_on_altsvc_received(nghttp2_session *session,
     }
   }
 
+  if (altsvc->field_value_len == 0) {
+    return session_call_on_invalid_frame_recv_callback(session, frame,
+                                                       NGHTTP2_ERR_PROTO);
+  }
+
   return session_call_on_frame_received(session, frame);
 }
 
@@ -5289,6 +5310,10 @@ ssize_t nghttp2_session_mem_recv(nghttp2_session *session, const uint8_t *in,
     return rv;
   }
 
+  if (!nghttp2_session_want_read(session)) {
+    return (ssize_t)inlen;
+  }
+
   for (;;) {
     switch (iframe->state) {
     case NGHTTP2_IB_READ_CLIENT_MAGIC:
@@ -5325,9 +5350,10 @@ ssize_t nghttp2_session_mem_recv(nghttp2_session *session, const uint8_t *in,
         iframe->state = NGHTTP2_IB_IGN_ALL;
 
         rv = session_call_error_callback(
-            session, "Remote peer returned unexpected data while we expected "
-                     "SETTINGS frame.  Perhaps, peer does not support HTTP/2 "
-                     "properly.");
+            session, NGHTTP2_ERR_SETTINGS_EXPECTED,
+            "Remote peer returned unexpected data while we expected "
+            "SETTINGS frame.  Perhaps, peer does not support HTTP/2 "
+            "properly.");
 
         if (nghttp2_is_fatal(rv)) {
           return rv;
@@ -5568,7 +5594,7 @@ ssize_t nghttp2_session_mem_recv(nghttp2_session *session, const uint8_t *in,
         if (iframe->payloadleft) {
           nghttp2_settings_entry *min_header_table_size_entry;
 
-          /* We allocate iv with addtional one entry, to store the
+          /* We allocate iv with additional one entry, to store the
              minimum header table size. */
           iframe->max_niv =
               iframe->frame.hd.length / NGHTTP2_FRAME_SETTINGS_ENTRY_LENGTH + 1;
@@ -5951,7 +5977,7 @@ ssize_t nghttp2_session_mem_recv(nghttp2_session *session, const uint8_t *in,
 
         DEBUGF("recv: origin_len=%zu\n", origin_len);
 
-        if (2 + origin_len > iframe->payloadleft) {
+        if (origin_len > iframe->payloadleft) {
           busy = 1;
           iframe->state = NGHTTP2_IB_FRAME_SIZE_ERROR;
           break;
@@ -6037,9 +6063,10 @@ ssize_t nghttp2_session_mem_recv(nghttp2_session *session, const uint8_t *in,
 
           /* 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,
+              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;
@@ -6124,7 +6151,7 @@ ssize_t nghttp2_session_mem_recv(nghttp2_session *session, const uint8_t *in,
     case NGHTTP2_IB_FRAME_SIZE_ERROR:
       DEBUGF("recv: [IB_FRAME_SIZE_ERROR]\n");
 
-      rv = session_handle_frame_size_error(session, &iframe->frame);
+      rv = session_handle_frame_size_error(session);
       if (nghttp2_is_fatal(rv)) {
         return rv;
       }
@@ -6609,21 +6636,12 @@ int nghttp2_session_want_write(nghttp2_session *session) {
    * 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->root.obq) ||
-       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;
+  return session->aob.item || nghttp2_outbound_queue_top(&session->ob_urgent) ||
+         nghttp2_outbound_queue_top(&session->ob_reg) ||
+         (!nghttp2_pq_empty(&session->root.obq) &&
+          session->remote_window_size > 0) ||
+         (nghttp2_outbound_queue_top(&session->ob_syn) &&
+          !session_is_outgoing_concurrent_streams_max(session));
 }
 
 int nghttp2_session_add_ping(nghttp2_session *session, uint8_t flags,
@@ -7138,6 +7156,7 @@ uint32_t nghttp2_session_get_remote_settings(nghttp2_session *session,
   }
 
   assert(0);
+  abort(); /* if NDEBUG is set */
 }
 
 uint32_t nghttp2_session_get_local_settings(nghttp2_session *session,
@@ -7158,6 +7177,7 @@ uint32_t nghttp2_session_get_local_settings(nghttp2_session *session,
   }
 
   assert(0);
+  abort(); /* if NDEBUG is set */
 }
 
 static int nghttp2_session_upgrade_internal(nghttp2_session *session,
@@ -7501,3 +7521,7 @@ size_t
 nghttp2_session_get_hd_deflate_dynamic_table_size(nghttp2_session *session) {
   return nghttp2_hd_deflate_get_dynamic_table_size(&session->hd_deflater);
 }
+
+void nghttp2_session_set_user_data(nghttp2_session *session, void *user_data) {
+  session->user_data = user_data;
+}
index 3e4c144..c7cb27d 100644 (file)
@@ -311,7 +311,7 @@ struct nghttp2_session {
   /* 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;
-  /* The bitwose OR of zero or more of nghttp2_typemask to indicate
+  /* The bitwise OR of zero or more of nghttp2_typemask to indicate
      that the default handling of extension frame is enabled. */
   uint32_t builtin_recv_ext_types;
   /* Unacked local ENABLE_PUSH value.  We use this to refuse
@@ -319,7 +319,7 @@ struct nghttp2_session {
   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
+  /* Flags indicating GOAWAY is sent and/or received. The flags are
      composed by bitwise OR-ing nghttp2_goaway_flag. */
   uint8_t goaway_flags;
   /* This flag is used to reduce excessive queuing of WINDOW_UPDATE to
@@ -722,7 +722,7 @@ int nghttp2_session_on_goaway_received(nghttp2_session *session,
                                        nghttp2_frame *frame);
 
 /*
- * Called when WINDOW_UPDATE is recieved, assuming |frame| is properly
+ * Called when WINDOW_UPDATE is received, assuming |frame| is properly
  * initialized.
  *
  * This function returns 0 if it succeeds, or one of the following
@@ -737,7 +737,7 @@ int nghttp2_session_on_window_update_received(nghttp2_session *session,
                                               nghttp2_frame *frame);
 
 /*
- * Called when ALTSVC is recieved, assuming |frame| is properly
+ * Called when ALTSVC is received, assuming |frame| is properly
  * initialized.
  *
  * This function returns 0 if it succeeds, or one of the following
index b005a63..eccd317 100644 (file)
@@ -366,8 +366,9 @@ static void check_queued(nghttp2_stream *stream) {
         }
       }
       if (queued == 0) {
-        fprintf(stderr, "stream(%p)=%d, stream->queued == 1, and "
-                        "!stream_active(), but no descendants is queued\n",
+        fprintf(stderr,
+                "stream(%p)=%d, stream->queued == 1, and "
+                "!stream_active(), but no descendants is queued\n",
                 stream, stream->stream_id);
         assert(0);
       }
@@ -378,9 +379,10 @@ static void check_queued(nghttp2_stream *stream) {
     }
   } else {
     if (stream_active(stream) || !nghttp2_pq_empty(&stream->obq)) {
-      fprintf(stderr, "stream(%p) = %d, stream->queued == 0, but "
-                      "stream_active(stream) == %d and "
-                      "nghttp2_pq_size(&stream->obq) = %zu\n",
+      fprintf(stderr,
+              "stream(%p) = %d, stream->queued == 0, but "
+              "stream_active(stream) == %d and "
+              "nghttp2_pq_size(&stream->obq) = %zu\n",
               stream, stream->stream_id, stream_active(stream),
               nghttp2_pq_size(&stream->obq));
       assert(0);
@@ -449,8 +451,8 @@ static void validate_tree(nghttp2_stream *stream) {
   check_sum_dep(stream);
   check_dep_prev(stream);
 }
-#else /* !STREAM_DEP_DEBUG */
-static void validate_tree(nghttp2_stream *stream _U_) {}
+#else  /* !STREAM_DEP_DEBUG */
+static void validate_tree(nghttp2_stream *stream) { (void)stream; }
 #endif /* !STREAM_DEP_DEBUG*/
 
 static int stream_update_dep_on_attach_item(nghttp2_stream *stream) {
index 0765692..6c15c82 100644 (file)
@@ -217,7 +217,7 @@ int nghttp2_submit_ping(nghttp2_session *session, uint8_t flags,
   return nghttp2_session_add_ping(session, flags, opaque_data);
 }
 
-int nghttp2_submit_priority(nghttp2_session *session, uint8_t flags _U_,
+int nghttp2_submit_priority(nghttp2_session *session, uint8_t flags,
                             int32_t stream_id,
                             const nghttp2_priority_spec *pri_spec) {
   int rv;
@@ -225,6 +225,7 @@ int nghttp2_submit_priority(nghttp2_session *session, uint8_t flags _U_,
   nghttp2_frame *frame;
   nghttp2_priority_spec copy_pri_spec;
   nghttp2_mem *mem;
+  (void)flags;
 
   mem = &session->mem;
 
@@ -264,8 +265,10 @@ int nghttp2_submit_priority(nghttp2_session *session, uint8_t flags _U_,
   return 0;
 }
 
-int nghttp2_submit_rst_stream(nghttp2_session *session, uint8_t flags _U_,
+int nghttp2_submit_rst_stream(nghttp2_session *session, uint8_t flags,
                               int32_t stream_id, uint32_t error_code) {
+  (void)flags;
+
   if (stream_id == 0) {
     return NGHTTP2_ERR_INVALID_ARGUMENT;
   }
@@ -273,9 +276,11 @@ int nghttp2_submit_rst_stream(nghttp2_session *session, uint8_t flags _U_,
   return nghttp2_session_add_rst_stream(session, stream_id, error_code);
 }
 
-int nghttp2_submit_goaway(nghttp2_session *session, uint8_t flags _U_,
+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) {
+  (void)flags;
+
   if (session->goaway_flags & NGHTTP2_GOAWAY_TERM_ON_SEND) {
     return 0;
   }
@@ -296,12 +301,13 @@ int nghttp2_submit_shutdown_notice(nghttp2_session *session) {
                                     NGHTTP2_GOAWAY_AUX_SHUTDOWN_NOTICE);
 }
 
-int nghttp2_submit_settings(nghttp2_session *session, uint8_t flags _U_,
+int nghttp2_submit_settings(nghttp2_session *session, uint8_t flags,
                             const nghttp2_settings_entry *iv, size_t niv) {
+  (void)flags;
   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 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) {
@@ -312,6 +318,7 @@ int32_t nghttp2_submit_push_promise(nghttp2_session *session, uint8_t flags _U_,
   int32_t promised_stream_id;
   int rv;
   nghttp2_mem *mem;
+  (void)flags;
 
   mem = &session->mem;
 
@@ -365,11 +372,13 @@ int32_t nghttp2_submit_push_promise(nghttp2_session *session, uint8_t flags _U_,
   return promised_stream_id;
 }
 
-int nghttp2_submit_window_update(nghttp2_session *session, uint8_t flags _U_,
+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;
+  (void)flags;
+
   if (window_size_increment == 0) {
     return 0;
   }
@@ -410,11 +419,12 @@ int nghttp2_submit_window_update(nghttp2_session *session, uint8_t flags _U_,
 }
 
 int nghttp2_session_set_local_window_size(nghttp2_session *session,
-                                          uint8_t flags _U_, int32_t stream_id,
+                                          uint8_t flags, int32_t stream_id,
                                           int32_t window_size) {
   int32_t window_size_increment;
   nghttp2_stream *stream;
   int rv;
+  (void)flags;
 
   if (window_size < 0) {
     return NGHTTP2_ERR_INVALID_ARGUMENT;
@@ -476,7 +486,7 @@ int nghttp2_session_set_local_window_size(nghttp2_session *session,
   return 0;
 }
 
-int nghttp2_submit_altsvc(nghttp2_session *session, uint8_t flags _U_,
+int nghttp2_submit_altsvc(nghttp2_session *session, uint8_t flags,
                           int32_t stream_id, const uint8_t *origin,
                           size_t origin_len, const uint8_t *field_value,
                           size_t field_value_len) {
@@ -488,6 +498,7 @@ int nghttp2_submit_altsvc(nghttp2_session *session, uint8_t flags _U_,
   nghttp2_frame *frame;
   nghttp2_ext_altsvc *altsvc;
   int rv;
+  (void)flags;
 
   mem = &session->mem;
 
index 4edfa7a..8938ab3 100644 (file)
@@ -1,40 +1,40 @@
-#include <winver.h>\r
-\r
-VS_VERSION_INFO VERSIONINFO\r
-\r
-FILEVERSION    @PROJECT_VERSION_MAJOR@, @PROJECT_VERSION_MINOR@, @PROJECT_VERSION_PATCH@, 0\r
-PRODUCTVERSION @PROJECT_VERSION_MAJOR@, @PROJECT_VERSION_MINOR@, @PROJECT_VERSION_PATCH@, 0\r
-FILEFLAGSMASK  0x3fL\r
-FILEOS         0x40004L\r
-FILETYPE       0x2L\r
-FILESUBTYPE    0x0L\r
-#ifdef _DEBUG\r
-       #define        VER_STR  "@PROJECT_VERSION@.0 (MSVC debug)"\r
-       #define        DBG      "d"\r
-       FILEFLAGS      0x1L\r
-#else\r
-       #define        VER_STR  "@PROJECT_VERSION@.0 (MSVC release)"\r
-       #define        DBG      ""\r
-       FILEFLAGS      0x0L\r
-#endif\r
-BEGIN\r
-BLOCK "StringFileInfo"\r
-BEGIN\r
-  BLOCK "040904b0"\r
-  BEGIN\r
-       VALUE "CompanyName",      "https://nghttp2.org/"\r
-       VALUE "FileDescription",  "nghttp2; HTTP/2 C library"\r
-       VALUE "FileVersion",      VER_STR\r
-       VALUE "InternalName",     "nghttp2" DBG\r
-       VALUE "LegalCopyright",   "The MIT License"\r
-       VALUE "LegalTrademarks",  ""\r
-       VALUE "OriginalFilename", "nghttp2" DBG ".dll"\r
-       VALUE "ProductName",      "NGHTTP2."\r
-       VALUE "ProductVersion",   VER_STR\r
-  END\r
-END\r
-BLOCK "VarFileInfo"\r
-BEGIN\r
-VALUE "Translation", 0x409, 1200\r
-END\r
-END\r
+#include <winver.h>
+
+VS_VERSION_INFO VERSIONINFO
+
+FILEVERSION    @PROJECT_VERSION_MAJOR@, @PROJECT_VERSION_MINOR@, @PROJECT_VERSION_PATCH@, 0
+PRODUCTVERSION @PROJECT_VERSION_MAJOR@, @PROJECT_VERSION_MINOR@, @PROJECT_VERSION_PATCH@, 0
+FILEFLAGSMASK  0x3fL
+FILEOS         0x40004L
+FILETYPE       0x2L
+FILESUBTYPE    0x0L
+#ifdef _DEBUG
+       #define        VER_STR  "@PROJECT_VERSION@.0 (MSVC debug)"
+       #define        DBG      "d"
+       FILEFLAGS      0x1L
+#else
+       #define        VER_STR  "@PROJECT_VERSION@.0 (MSVC release)"
+       #define        DBG      ""
+       FILEFLAGS      0x0L
+#endif
+BEGIN
+BLOCK "StringFileInfo"
+BEGIN
+  BLOCK "040904b0"
+  BEGIN
+       VALUE "CompanyName",      "https://nghttp2.org/"
+       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
+  END
+END
+BLOCK "VarFileInfo"
+BEGIN
+VALUE "Translation", 0x409, 1200
+END
+END
index 8aefa80..95a1dd0 100644 (file)
@@ -1,7 +1,7 @@
-# Makefile.in generated by automake 1.15 from Makefile.am.
+# Makefile.in generated by automake 1.15.1 from Makefile.am.
 # @configure_input@
 
-# Copyright (C) 1994-2014 Free Software Foundation, Inc.
+# Copyright (C) 1994-2017 Free Software Foundation, Inc.
 
 # This Makefile.in is free software; the Free Software Foundation
 # gives unlimited permission to copy and/or distribute it,
@@ -214,8 +214,6 @@ LIBMRUBY_CFLAGS = @LIBMRUBY_CFLAGS@
 LIBMRUBY_LIBS = @LIBMRUBY_LIBS@
 LIBOBJS = @LIBOBJS@
 LIBS = @LIBS@
-LIBSPDYLAY_CFLAGS = @LIBSPDYLAY_CFLAGS@
-LIBSPDYLAY_LIBS = @LIBSPDYLAY_LIBS@
 LIBTOOL = @LIBTOOL@
 LIBXML2_CFLAGS = @LIBXML2_CFLAGS@
 LIBXML2_LIBS = @LIBXML2_LIBS@
@@ -448,8 +446,8 @@ distclean-generic:
 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@clean-local:
 @ENABLE_PYTHON_BINDINGS_FALSE@uninstall-local:
 clean: clean-am
 
index bba2c47..d6f5803 100644 (file)
@@ -1,7 +1,7 @@
-# Makefile.in generated by automake 1.15 from Makefile.am.
+# Makefile.in generated by automake 1.15.1 from Makefile.am.
 # @configure_input@
 
-# Copyright (C) 1994-2014 Free Software Foundation, Inc.
+# Copyright (C) 1994-2017 Free Software Foundation, Inc.
 
 # This Makefile.in is free software; the Free Software Foundation
 # gives unlimited permission to copy and/or distribute it,
@@ -245,8 +245,6 @@ LIBMRUBY_CFLAGS = @LIBMRUBY_CFLAGS@
 LIBMRUBY_LIBS = @LIBMRUBY_LIBS@
 LIBOBJS = @LIBOBJS@
 LIBS = @LIBS@
-LIBSPDYLAY_CFLAGS = @LIBSPDYLAY_CFLAGS@
-LIBSPDYLAY_LIBS = @LIBSPDYLAY_LIBS@
 LIBTOOL = @LIBTOOL@
 LIBXML2_CFLAGS = @LIBXML2_CFLAGS@
 LIBXML2_LIBS = @LIBXML2_LIBS@
index 0648f6f..f33f4bf 100644 (file)
@@ -8,11 +8,8 @@ set_source_files_properties(${cxx_sources} PROPERTIES
   COMPILE_FLAGS "${WARNCXXFLAGS} ${CXX1XCXXFLAGS}")
 
 include_directories(
-  "${CMAKE_SOURCE_DIR}/lib/includes"
-  "${CMAKE_BINARY_DIR}/lib/includes"
-  "${CMAKE_SOURCE_DIR}/lib"
-  "${CMAKE_SOURCE_DIR}/src/includes"
-  "${CMAKE_SOURCE_DIR}/third-party"
+  "${CMAKE_CURRENT_SOURCE_DIR}/includes"
+  "${CMAKE_CURRENT_SOURCE_DIR}/../third-party"
 
   ${JEMALLOC_INCLUDE_DIRS}
   ${SPDYLAY_INCLUDE_DIRS}
@@ -48,7 +45,7 @@ if(ENABLE_APP)
   set(NGHTTP_SOURCES
     ${HELPER_OBJECTS}
     nghttp.cc
-    ssl.cc
+    tls.cc
   )
   if(HAVE_LIBXML2)
     list(APPEND NGHTTP_SOURCES HtmlParser.cc)
@@ -58,7 +55,7 @@ if(ENABLE_APP)
   set(NGHTTPD_SOURCES
     ${HELPER_OBJECTS}
     nghttpd.cc
-    ssl.cc
+    tls.cc
     HttpServer.cc
   )
 
@@ -67,7 +64,7 @@ if(ENABLE_APP)
     util.cc
     http2.cc h2load.cc
     timegm.c
-    ssl.cc
+    tls.cc
     h2load_http2_session.cc
     h2load_http1_session.cc
   )
@@ -82,7 +79,7 @@ if(ENABLE_APP)
   set(NGHTTPX_SRCS
     util.cc http2.cc timegm.c
     app_helper.cc
-    ssl.cc
+    tls.cc
     shrpx_config.cc
     shrpx_accept_handler.cc
     shrpx_connection_handler.cc
@@ -98,7 +95,7 @@ if(ENABLE_APP)
     shrpx_log.cc
     shrpx_http.cc
     shrpx_io_control.cc
-    shrpx_ssl.cc
+    shrpx_tls.cc
     shrpx_worker.cc
     shrpx_log_config.cc
     shrpx_connect_blocker.cc
@@ -152,7 +149,7 @@ if(ENABLE_APP)
   if(HAVE_CUNIT)
     set(NGHTTPX_UNITTEST_SOURCES
       shrpx-unittest.cc
-      shrpx_ssl_test.cc
+      shrpx_tls_test.cc
       shrpx_downstream_test.cc
       shrpx_config_test.cc
       shrpx_worker_test.cc
@@ -216,7 +213,7 @@ endif()
 if(ENABLE_ASIO_LIB)
   set(NGHTTP2_ASIO_SOURCES
     util.cc http2.cc
-    ssl.cc
+    tls.cc
     timegm.c
     asio_common.cc
     asio_io_service_pool.cc
@@ -252,6 +249,11 @@ if(ENABLE_ASIO_LIB)
     ${OPENSSL_INCLUDE_DIRS}
     ${Boost_INCLUDE_DIRS}
   )
+  target_include_directories(nghttp2_asio INTERFACE
+    "${CMAKE_CURRENT_BINARY_DIR}/../lib/includes"
+    "${CMAKE_CURRENT_SOURCE_DIR}/../lib/includes"
+    "${CMAKE_CURRENT_SOURCE_DIR}/includes"
+  )
   target_link_libraries(nghttp2_asio
     nghttp2
     ${OPENSSL_LIBRARIES}
index 7dc1676..b3e35ef 100644 (file)
@@ -60,7 +60,7 @@
 #include "app_helper.h"
 #include "http2.h"
 #include "util.h"
-#include "ssl.h"
+#include "tls.h"
 #include "template.h"
 
 #ifndef O_BINARY
@@ -877,7 +877,7 @@ int Http2Handler::connection_made() {
     }
   }
 
-  if (ssl_ && !nghttp2::ssl::check_http2_requirement(ssl_)) {
+  if (ssl_ && !nghttp2::tls::check_http2_requirement(ssl_)) {
     terminate_session(NGHTTP2_INADEQUATE_SECURITY);
   }
 
@@ -1749,8 +1749,8 @@ void fill_callback(nghttp2_session_callbacks *callbacks, const Config *config) {
     nghttp2_session_callbacks_set_on_invalid_frame_recv_callback(
         callbacks, verbose_on_invalid_frame_recv_callback);
 
-    nghttp2_session_callbacks_set_error_callback(callbacks,
-                                                 verbose_error_callback);
+    nghttp2_session_callbacks_set_error_callback2(callbacks,
+                                                  verbose_error_callback);
   }
 
   nghttp2_session_callbacks_set_on_data_chunk_recv_callback(
@@ -1779,7 +1779,7 @@ struct ClientInfo {
 struct Worker {
   std::unique_ptr<Sessions> sessions;
   ev_async w;
-  // protectes q
+  // protects q
   std::mutex m;
   std::deque<ClientInfo> q;
 };
@@ -2122,14 +2122,14 @@ int HttpServer::run() {
     SSL_CTX_set_mode(ssl_ctx, SSL_MODE_AUTO_RETRY);
     SSL_CTX_set_mode(ssl_ctx, SSL_MODE_RELEASE_BUFFERS);
 
-    if (nghttp2::ssl::ssl_ctx_set_proto_versions(
-            ssl_ctx, nghttp2::ssl::NGHTTP2_TLS_MIN_VERSION,
-            nghttp2::ssl::NGHTTP2_TLS_MAX_VERSION) != 0) {
+    if (nghttp2::tls::ssl_ctx_set_proto_versions(
+            ssl_ctx, nghttp2::tls::NGHTTP2_TLS_MIN_VERSION,
+            nghttp2::tls::NGHTTP2_TLS_MAX_VERSION) != 0) {
       std::cerr << "Could not set TLS versions" << std::endl;
       return -1;
     }
 
-    if (SSL_CTX_set_cipher_list(ssl_ctx, ssl::DEFAULT_CIPHER_LIST) == 0) {
+    if (SSL_CTX_set_cipher_list(ssl_ctx, tls::DEFAULT_CIPHER_LIST) == 0) {
       std::cerr << ERR_error_string(ERR_get_error(), nullptr) << std::endl;
       return -1;
     }
@@ -2156,7 +2156,7 @@ int HttpServer::run() {
     }
     SSL_CTX_set_tmp_ecdh(ssl_ctx, ecdh);
     EC_KEY_free(ecdh);
-// #endif // OPENSSL_VERSION_NUBMER < 0x10002000L
+    // #endif // OPENSSL_VERSION_NUBMER < 0x10002000L
 
 #endif // OPENSSL_NO_EC
 
@@ -2197,8 +2197,9 @@ int HttpServer::run() {
       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,
+      SSL_CTX_set_verify(ssl_ctx,
+                         SSL_VERIFY_PEER | SSL_VERIFY_CLIENT_ONCE |
+                             SSL_VERIFY_FAIL_IF_NO_PEER_CERT,
                          verify_callback);
     }
 
index ea3f8a3..440f6e7 100644 (file)
@@ -40,7 +40,6 @@ AM_CPPFLAGS = \
        -I$(top_srcdir)/lib \
        -I$(top_srcdir)/src/includes \
        -I$(top_srcdir)/third-party \
-       @LIBSPDYLAY_CFLAGS@ \
        @LIBXML2_CFLAGS@ \
        @LIBEV_CFLAGS@ \
        @OPENSSL_CFLAGS@ \
@@ -52,7 +51,6 @@ AM_CPPFLAGS = \
 LDADD = $(top_builddir)/lib/libnghttp2.la \
        $(top_builddir)/third-party/libhttp-parser.la \
        @JEMALLOC_LIBS@ \
-       @LIBSPDYLAY_LIBS@ \
        @LIBXML2_LIBS@ \
        @LIBEV_LIBS@ \
        @OPENSSL_LIBS@ \
@@ -81,10 +79,10 @@ endif # HAVE_LIBXML2
 
 nghttp_SOURCES = ${HELPER_OBJECTS} ${HELPER_HFILES} nghttp.cc nghttp.h \
        ${HTML_PARSER_OBJECTS} ${HTML_PARSER_HFILES} \
-       ssl.cc ssl.h
+       tls.cc tls.h
 
 nghttpd_SOURCES = ${HELPER_OBJECTS} ${HELPER_HFILES} nghttpd.cc \
-       ssl.cc ssl.h \
+       tls.cc tls.h \
        HttpServer.cc HttpServer.h
 
 bin_PROGRAMS += h2load
@@ -92,19 +90,15 @@ 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 \
+       tls.cc tls.h \
        h2load_session.h \
        h2load_http2_session.cc h2load_http2_session.h \
        h2load_http1_session.cc h2load_http1_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 \
+       tls.cc tls.h \
        shrpx_config.cc shrpx_config.h \
        shrpx_error.h \
        shrpx_accept_handler.cc shrpx_accept_handler.h \
@@ -122,7 +116,7 @@ NGHTTPX_SRCS = \
        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_tls.cc shrpx_tls.h \
        shrpx_worker.cc shrpx_worker.h \
        shrpx_log_config.cc shrpx_log_config.h \
        shrpx_connect_blocker.cc shrpx_connect_blocker.h \
@@ -148,10 +142,6 @@ NGHTTPX_SRCS = \
        buffer.h memchunk.h template.h allocator.h \
        xsi_strerror.c xsi_strerror.h
 
-if HAVE_SPDYLAY
-NGHTTPX_SRCS += shrpx_spdy_upstream.cc shrpx_spdy_upstream.h
-endif # HAVE_SPDYLAY
-
 if HAVE_MRUBY
 NGHTTPX_SRCS += \
        shrpx_mruby.cc shrpx_mruby.h \
@@ -183,7 +173,7 @@ endif # HAVE_NEVERBLEED
 if HAVE_CUNIT
 check_PROGRAMS += nghttpx-unittest
 nghttpx_unittest_SOURCES = shrpx-unittest.cc \
-       shrpx_ssl_test.cc shrpx_ssl_test.h \
+       shrpx_tls_test.cc shrpx_tls_test.h \
        shrpx_downstream_test.cc shrpx_downstream_test.h \
        shrpx_config_test.cc shrpx_config_test.h \
        shrpx_worker_test.cc shrpx_worker_test.h \
@@ -240,7 +230,7 @@ lib_LTLIBRARIES = libnghttp2_asio.la
 
 libnghttp2_asio_la_SOURCES = \
        util.cc util.h http2.cc http2.h \
-       ssl.cc ssl.h \
+       tls.cc tls.h \
        ssl_compat.h \
        timegm.c timegm.h \
        asio_common.cc asio_common.h \
index b03ed7d..88c31c4 100644 (file)
@@ -1,7 +1,7 @@
-# Makefile.in generated by automake 1.15 from Makefile.am.
+# Makefile.in generated by automake 1.15.1 from Makefile.am.
 # @configure_input@
 
-# Copyright (C) 1994-2014 Free Software Foundation, Inc.
+# Copyright (C) 1994-2017 Free Software Foundation, Inc.
 
 # This Makefile.in is free software; the Free Software Foundation
 # gives unlimited permission to copy and/or distribute it,
@@ -112,32 +112,30 @@ 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_MRUBY_TRUE@am__append_5 = \
+@ENABLE_APP_TRUE@@HAVE_MRUBY_TRUE@am__append_3 = \
 @ENABLE_APP_TRUE@@HAVE_MRUBY_TRUE@     shrpx_mruby.cc shrpx_mruby.h \
 @ENABLE_APP_TRUE@@HAVE_MRUBY_TRUE@     shrpx_mruby_module.cc shrpx_mruby_module.h \
 @ENABLE_APP_TRUE@@HAVE_MRUBY_TRUE@     shrpx_mruby_module_env.cc shrpx_mruby_module_env.h \
 @ENABLE_APP_TRUE@@HAVE_MRUBY_TRUE@     shrpx_mruby_module_request.cc shrpx_mruby_module_request.h \
 @ENABLE_APP_TRUE@@HAVE_MRUBY_TRUE@     shrpx_mruby_module_response.cc shrpx_mruby_module_response.h
 
-@ENABLE_APP_TRUE@@HAVE_MRUBY_TRUE@am__append_6 = \
+@ENABLE_APP_TRUE@@HAVE_MRUBY_TRUE@am__append_4 = \
 @ENABLE_APP_TRUE@@HAVE_MRUBY_TRUE@     -I${top_srcdir}/third-party/mruby/include @LIBMRUBY_CFLAGS@
 
-@ENABLE_APP_TRUE@@HAVE_MRUBY_TRUE@am__append_7 = -L${top_builddir}/third-party/mruby/build/lib @LIBMRUBY_LIBS@
-@ENABLE_APP_TRUE@@HAVE_NEVERBLEED_TRUE@am__append_8 = -I${top_srcdir}/third-party/neverbleed
-@ENABLE_APP_TRUE@@HAVE_NEVERBLEED_TRUE@am__append_9 = ${top_builddir}/third-party/libneverbleed.la
-@ENABLE_APP_TRUE@@HAVE_CUNIT_TRUE@am__append_10 = nghttpx-unittest
-@ENABLE_APP_TRUE@@HAVE_CUNIT_TRUE@@HAVE_MRUBY_TRUE@am__append_11 = \
+@ENABLE_APP_TRUE@@HAVE_MRUBY_TRUE@am__append_5 = -L${top_builddir}/third-party/mruby/build/lib @LIBMRUBY_LIBS@
+@ENABLE_APP_TRUE@@HAVE_NEVERBLEED_TRUE@am__append_6 = -I${top_srcdir}/third-party/neverbleed
+@ENABLE_APP_TRUE@@HAVE_NEVERBLEED_TRUE@am__append_7 = ${top_builddir}/third-party/libneverbleed.la
+@ENABLE_APP_TRUE@@HAVE_CUNIT_TRUE@am__append_8 = nghttpx-unittest
+@ENABLE_APP_TRUE@@HAVE_CUNIT_TRUE@@HAVE_MRUBY_TRUE@am__append_9 = \
 @ENABLE_APP_TRUE@@HAVE_CUNIT_TRUE@@HAVE_MRUBY_TRUE@    -I${top_srcdir}/third-party/mruby/include @LIBMRUBY_CFLAGS@
 
-@ENABLE_APP_TRUE@@HAVE_CUNIT_TRUE@@HAVE_MRUBY_TRUE@am__append_12 = \
+@ENABLE_APP_TRUE@@HAVE_CUNIT_TRUE@@HAVE_MRUBY_TRUE@am__append_10 = \
 @ENABLE_APP_TRUE@@HAVE_CUNIT_TRUE@@HAVE_MRUBY_TRUE@    -L${top_builddir}/third-party/mruby/build/lib @LIBMRUBY_LIBS@
 
-@ENABLE_APP_TRUE@@HAVE_CUNIT_TRUE@@HAVE_NEVERBLEED_TRUE@am__append_13 = -I${top_srcdir}/third-party/neverbleed
-@ENABLE_APP_TRUE@@HAVE_CUNIT_TRUE@@HAVE_NEVERBLEED_TRUE@am__append_14 = ${top_builddir}/third-party/libneverbleed.la
-@ENABLE_APP_TRUE@@HAVE_CUNIT_TRUE@am__append_15 = nghttpx-unittest
-@ENABLE_HPACK_TOOLS_TRUE@am__append_16 = inflatehd deflatehd
+@ENABLE_APP_TRUE@@HAVE_CUNIT_TRUE@@HAVE_NEVERBLEED_TRUE@am__append_11 = -I${top_srcdir}/third-party/neverbleed
+@ENABLE_APP_TRUE@@HAVE_CUNIT_TRUE@@HAVE_NEVERBLEED_TRUE@am__append_12 = ${top_builddir}/third-party/libneverbleed.la
+@ENABLE_APP_TRUE@@HAVE_CUNIT_TRUE@am__append_13 = nghttpx-unittest
+@ENABLE_HPACK_TOOLS_TRUE@am__append_14 = inflatehd deflatehd
 subdir = src
 ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
 am__aclocal_m4_deps = $(top_srcdir)/m4/ax_boost_asio.m4 \
@@ -166,8 +164,8 @@ 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 \
+       timegm.c timegm.h base64.h app_helper.cc app_helper.h tls.cc \
+       tls.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 \
@@ -182,7 +180,7 @@ am__libnghttpx_a_SOURCES_DIST = util.cc util.h http2.cc http2.h \
        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_io_control.h shrpx_tls.cc shrpx_tls.h shrpx_worker.cc \
        shrpx_worker.h shrpx_log_config.cc shrpx_log_config.h \
        shrpx_connect_blocker.cc shrpx_connect_blocker.h \
        shrpx_live_check.cc shrpx_live_check.h \
@@ -202,22 +200,21 @@ am__libnghttpx_a_SOURCES_DIST = util.cc util.h http2.cc http2.h \
        shrpx_dual_dns_resolver.cc shrpx_dual_dns_resolver.h \
        shrpx_dns_tracker.cc shrpx_dns_tracker.h buffer.h memchunk.h \
        template.h allocator.h xsi_strerror.c xsi_strerror.h \
-       shrpx_spdy_upstream.cc shrpx_spdy_upstream.h shrpx_mruby.cc \
-       shrpx_mruby.h shrpx_mruby_module.cc shrpx_mruby_module.h \
-       shrpx_mruby_module_env.cc shrpx_mruby_module_env.h \
-       shrpx_mruby_module_request.cc shrpx_mruby_module_request.h \
-       shrpx_mruby_module_response.cc shrpx_mruby_module_response.h
-@ENABLE_APP_TRUE@@HAVE_SPDYLAY_TRUE@am__objects_1 = libnghttpx_a-shrpx_spdy_upstream.$(OBJEXT)
-@ENABLE_APP_TRUE@@HAVE_MRUBY_TRUE@am__objects_2 = libnghttpx_a-shrpx_mruby.$(OBJEXT) \
+       shrpx_mruby.cc shrpx_mruby.h shrpx_mruby_module.cc \
+       shrpx_mruby_module.h shrpx_mruby_module_env.cc \
+       shrpx_mruby_module_env.h shrpx_mruby_module_request.cc \
+       shrpx_mruby_module_request.h shrpx_mruby_module_response.cc \
+       shrpx_mruby_module_response.h
+@ENABLE_APP_TRUE@@HAVE_MRUBY_TRUE@am__objects_1 = libnghttpx_a-shrpx_mruby.$(OBJEXT) \
 @ENABLE_APP_TRUE@@HAVE_MRUBY_TRUE@     libnghttpx_a-shrpx_mruby_module.$(OBJEXT) \
 @ENABLE_APP_TRUE@@HAVE_MRUBY_TRUE@     libnghttpx_a-shrpx_mruby_module_env.$(OBJEXT) \
 @ENABLE_APP_TRUE@@HAVE_MRUBY_TRUE@     libnghttpx_a-shrpx_mruby_module_request.$(OBJEXT) \
 @ENABLE_APP_TRUE@@HAVE_MRUBY_TRUE@     libnghttpx_a-shrpx_mruby_module_response.$(OBJEXT)
-@ENABLE_APP_TRUE@am__objects_3 = libnghttpx_a-util.$(OBJEXT) \
+@ENABLE_APP_TRUE@am__objects_2 = libnghttpx_a-util.$(OBJEXT) \
 @ENABLE_APP_TRUE@      libnghttpx_a-http2.$(OBJEXT) \
 @ENABLE_APP_TRUE@      libnghttpx_a-timegm.$(OBJEXT) \
 @ENABLE_APP_TRUE@      libnghttpx_a-app_helper.$(OBJEXT) \
-@ENABLE_APP_TRUE@      libnghttpx_a-ssl.$(OBJEXT) \
+@ENABLE_APP_TRUE@      libnghttpx_a-tls.$(OBJEXT) \
 @ENABLE_APP_TRUE@      libnghttpx_a-shrpx_config.$(OBJEXT) \
 @ENABLE_APP_TRUE@      libnghttpx_a-shrpx_accept_handler.$(OBJEXT) \
 @ENABLE_APP_TRUE@      libnghttpx_a-shrpx_connection_handler.$(OBJEXT) \
@@ -233,7 +230,7 @@ am__libnghttpx_a_SOURCES_DIST = util.cc util.h http2.cc http2.h \
 @ENABLE_APP_TRUE@      libnghttpx_a-shrpx_log.$(OBJEXT) \
 @ENABLE_APP_TRUE@      libnghttpx_a-shrpx_http.$(OBJEXT) \
 @ENABLE_APP_TRUE@      libnghttpx_a-shrpx_io_control.$(OBJEXT) \
-@ENABLE_APP_TRUE@      libnghttpx_a-shrpx_ssl.$(OBJEXT) \
+@ENABLE_APP_TRUE@      libnghttpx_a-shrpx_tls.$(OBJEXT) \
 @ENABLE_APP_TRUE@      libnghttpx_a-shrpx_worker.$(OBJEXT) \
 @ENABLE_APP_TRUE@      libnghttpx_a-shrpx_log_config.$(OBJEXT) \
 @ENABLE_APP_TRUE@      libnghttpx_a-shrpx_connect_blocker.$(OBJEXT) \
@@ -253,8 +250,8 @@ am__libnghttpx_a_SOURCES_DIST = util.cc util.h http2.cc http2.h \
 @ENABLE_APP_TRUE@      libnghttpx_a-shrpx_dual_dns_resolver.$(OBJEXT) \
 @ENABLE_APP_TRUE@      libnghttpx_a-shrpx_dns_tracker.$(OBJEXT) \
 @ENABLE_APP_TRUE@      libnghttpx_a-xsi_strerror.$(OBJEXT) \
-@ENABLE_APP_TRUE@      $(am__objects_1) $(am__objects_2)
-@ENABLE_APP_TRUE@am_libnghttpx_a_OBJECTS = $(am__objects_3)
+@ENABLE_APP_TRUE@      $(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 \
@@ -295,7 +292,7 @@ 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 ssl_compat.h timegm.c timegm.h asio_common.cc \
+       tls.cc tls.h ssl_compat.h timegm.c timegm.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 \
@@ -319,7 +316,7 @@ am__libnghttp2_asio_la_SOURCES_DIST = util.cc util.h http2.cc http2.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-tls.lo \
 @ENABLE_ASIO_LIB_TRUE@ libnghttp2_asio_la-timegm.lo \
 @ENABLE_ASIO_LIB_TRUE@ libnghttp2_asio_la-asio_common.lo \
 @ENABLE_ASIO_LIB_TRUE@ libnghttp2_asio_la-asio_io_service_pool.lo \
@@ -363,31 +360,28 @@ libnghttp2_asio_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CXX \
 @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_4 = comp_helper.$(OBJEXT)
+@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_4)
+@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.h timegm.c timegm.h tls.cc tls.h h2load_session.h \
        h2load_http2_session.cc h2load_http2_session.h \
-       h2load_http1_session.cc h2load_http1_session.h \
-       h2load_spdy_session.cc h2load_spdy_session.h
-@ENABLE_APP_TRUE@@HAVE_SPDYLAY_TRUE@am__objects_5 = h2load_spdy_session.$(OBJEXT)
+       h2load_http1_session.cc h2load_http1_session.h
 @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@      h2load_http1_session.$(OBJEXT) \
-@ENABLE_APP_TRUE@      $(am__objects_5)
+@ENABLE_APP_TRUE@      tls.$(OBJEXT) h2load_http2_session.$(OBJEXT) \
+@ENABLE_APP_TRUE@      h2load_http1_session.$(OBJEXT)
 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_4)
+@ENABLE_HPACK_TOOLS_TRUE@      $(am__objects_3)
 inflatehd_OBJECTS = $(am_inflatehd_OBJECTS)
 inflatehd_LDADD = $(LDADD)
 inflatehd_DEPENDENCIES = $(top_builddir)/lib/libnghttp2.la \
@@ -395,28 +389,28 @@ inflatehd_DEPENDENCIES = $(top_builddir)/lib/libnghttp2.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 network.h nghttp.cc nghttp.h \
-       HtmlParser.cc HtmlParser.h ssl.cc ssl.h
-@ENABLE_APP_TRUE@am__objects_6 = util.$(OBJEXT) http2.$(OBJEXT) \
+       HtmlParser.cc HtmlParser.h tls.cc tls.h
+@ENABLE_APP_TRUE@am__objects_4 = util.$(OBJEXT) http2.$(OBJEXT) \
 @ENABLE_APP_TRUE@      timegm.$(OBJEXT) app_helper.$(OBJEXT) \
 @ENABLE_APP_TRUE@      nghttp2_gzip.$(OBJEXT)
-am__objects_7 =
-@ENABLE_APP_TRUE@@HAVE_LIBXML2_TRUE@am__objects_8 =  \
+am__objects_5 =
+@ENABLE_APP_TRUE@@HAVE_LIBXML2_TRUE@am__objects_6 =  \
 @ENABLE_APP_TRUE@@HAVE_LIBXML2_TRUE@   HtmlParser.$(OBJEXT)
-@ENABLE_APP_TRUE@am__objects_9 = $(am__objects_8)
-@ENABLE_APP_TRUE@am_nghttp_OBJECTS = $(am__objects_6) $(am__objects_7) \
-@ENABLE_APP_TRUE@      nghttp.$(OBJEXT) $(am__objects_9) \
-@ENABLE_APP_TRUE@      $(am__objects_7) ssl.$(OBJEXT)
+@ENABLE_APP_TRUE@am__objects_7 = $(am__objects_6)
+@ENABLE_APP_TRUE@am_nghttp_OBJECTS = $(am__objects_4) $(am__objects_5) \
+@ENABLE_APP_TRUE@      nghttp.$(OBJEXT) $(am__objects_7) \
+@ENABLE_APP_TRUE@      $(am__objects_5) tls.$(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 network.h nghttpd.cc ssl.cc \
-       ssl.h HttpServer.cc HttpServer.h
-@ENABLE_APP_TRUE@am_nghttpd_OBJECTS = $(am__objects_6) \
-@ENABLE_APP_TRUE@      $(am__objects_7) nghttpd.$(OBJEXT) \
-@ENABLE_APP_TRUE@      ssl.$(OBJEXT) HttpServer.$(OBJEXT)
+       nghttp2_config.h nghttp2_gzip.h network.h nghttpd.cc tls.cc \
+       tls.h HttpServer.cc HttpServer.h
+@ENABLE_APP_TRUE@am_nghttpd_OBJECTS = $(am__objects_4) \
+@ENABLE_APP_TRUE@      $(am__objects_5) nghttpd.$(OBJEXT) \
+@ENABLE_APP_TRUE@      tls.$(OBJEXT) HttpServer.$(OBJEXT)
 nghttpd_OBJECTS = $(am_nghttpd_OBJECTS)
 nghttpd_LDADD = $(LDADD)
 nghttpd_DEPENDENCIES = $(top_builddir)/lib/libnghttp2.la \
@@ -428,9 +422,9 @@ 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__DEPENDENCIES_1) \
-@ENABLE_APP_TRUE@      $(am__append_9)
+@ENABLE_APP_TRUE@      $(am__append_7)
 am__nghttpx_unittest_SOURCES_DIST = shrpx-unittest.cc \
-       shrpx_ssl_test.cc shrpx_ssl_test.h shrpx_downstream_test.cc \
+       shrpx_tls_test.cc shrpx_tls_test.h shrpx_downstream_test.cc \
        shrpx_downstream_test.h shrpx_config_test.cc \
        shrpx_config_test.h shrpx_worker_test.cc shrpx_worker_test.h \
        shrpx_http_test.cc shrpx_http_test.h shrpx_router_test.cc \
@@ -440,7 +434,7 @@ am__nghttpx_unittest_SOURCES_DIST = shrpx-unittest.cc \
        memchunk_test.cc memchunk_test.h template_test.cc \
        template_test.h base64_test.cc base64_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_tls_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-shrpx_worker_test.$(OBJEXT) \
@@ -459,7 +453,7 @@ nghttpx_unittest_OBJECTS = $(am_nghttpx_unittest_OBJECTS)
 @ENABLE_APP_TRUE@@HAVE_CUNIT_TRUE@     libnghttpx.a \
 @ENABLE_APP_TRUE@@HAVE_CUNIT_TRUE@     $(am__DEPENDENCIES_2) \
 @ENABLE_APP_TRUE@@HAVE_CUNIT_TRUE@     $(am__DEPENDENCIES_1) \
-@ENABLE_APP_TRUE@@HAVE_CUNIT_TRUE@     $(am__append_14)
+@ENABLE_APP_TRUE@@HAVE_CUNIT_TRUE@     $(am__append_12)
 AM_V_P = $(am__v_P_@AM_V@)
 am__v_P_ = $(am__v_P_@AM_DEFAULT_V@)
 am__v_P_0 = false
@@ -831,8 +825,6 @@ LIBMRUBY_CFLAGS = @LIBMRUBY_CFLAGS@
 LIBMRUBY_LIBS = @LIBMRUBY_LIBS@
 LIBOBJS = @LIBOBJS@
 LIBS = @LIBS@
-LIBSPDYLAY_CFLAGS = @LIBSPDYLAY_CFLAGS@
-LIBSPDYLAY_LIBS = @LIBSPDYLAY_LIBS@
 LIBTOOL = @LIBTOOL@
 LIBXML2_CFLAGS = @LIBXML2_CFLAGS@
 LIBXML2_LIBS = @LIBXML2_LIBS@
@@ -974,7 +966,6 @@ AM_CPPFLAGS = \
        -I$(top_srcdir)/lib \
        -I$(top_srcdir)/src/includes \
        -I$(top_srcdir)/third-party \
-       @LIBSPDYLAY_CFLAGS@ \
        @LIBXML2_CFLAGS@ \
        @LIBEV_CFLAGS@ \
        @OPENSSL_CFLAGS@ \
@@ -986,7 +977,6 @@ AM_CPPFLAGS = \
 LDADD = $(top_builddir)/lib/libnghttp2.la \
        $(top_builddir)/third-party/libhttp-parser.la \
        @JEMALLOC_LIBS@ \
-       @LIBSPDYLAY_LIBS@ \
        @LIBXML2_LIBS@ \
        @LIBEV_LIBS@ \
        @OPENSSL_LIBS@ \
@@ -1007,22 +997,23 @@ LDADD = $(top_builddir)/lib/libnghttp2.la \
 @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@      tls.cc tls.h
 
 @ENABLE_APP_TRUE@nghttpd_SOURCES = ${HELPER_OBJECTS} ${HELPER_HFILES} nghttpd.cc \
-@ENABLE_APP_TRUE@      ssl.cc ssl.h \
+@ENABLE_APP_TRUE@      tls.cc tls.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 \
-@ENABLE_APP_TRUE@      h2load_http1_session.cc \
-@ENABLE_APP_TRUE@      h2load_http1_session.h $(am__append_3)
+@ENABLE_APP_TRUE@h2load_SOURCES = util.cc util.h \
+@ENABLE_APP_TRUE@      http2.cc http2.h h2load.cc h2load.h \
+@ENABLE_APP_TRUE@      timegm.c timegm.h \
+@ENABLE_APP_TRUE@      tls.cc tls.h \
+@ENABLE_APP_TRUE@      h2load_session.h \
+@ENABLE_APP_TRUE@      h2load_http2_session.cc h2load_http2_session.h \
+@ENABLE_APP_TRUE@      h2load_http1_session.cc h2load_http1_session.h
+
 @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@      app_helper.h tls.cc tls.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 \
@@ -1046,7 +1037,7 @@ LDADD = $(top_builddir)/lib/libnghttp2.la \
 @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_tls.cc shrpx_tls.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 shrpx_live_check.cc \
@@ -1075,18 +1066,17 @@ LDADD = $(top_builddir)/lib/libnghttp2.la \
 @ENABLE_APP_TRUE@      shrpx_dual_dns_resolver.h \
 @ENABLE_APP_TRUE@      shrpx_dns_tracker.cc shrpx_dns_tracker.h \
 @ENABLE_APP_TRUE@      buffer.h memchunk.h template.h allocator.h \
-@ENABLE_APP_TRUE@      xsi_strerror.c xsi_strerror.h $(am__append_4) \
-@ENABLE_APP_TRUE@      $(am__append_5)
+@ENABLE_APP_TRUE@      xsi_strerror.c xsi_strerror.h $(am__append_3)
 @ENABLE_APP_TRUE@noinst_LIBRARIES = libnghttpx.a
 @ENABLE_APP_TRUE@libnghttpx_a_SOURCES = ${NGHTTPX_SRCS}
 @ENABLE_APP_TRUE@libnghttpx_a_CPPFLAGS = ${AM_CPPFLAGS} \
-@ENABLE_APP_TRUE@      $(am__append_6) $(am__append_8)
+@ENABLE_APP_TRUE@      $(am__append_4) $(am__append_6)
 @ENABLE_APP_TRUE@nghttpx_SOURCES = shrpx.cc shrpx.h
 @ENABLE_APP_TRUE@nghttpx_CPPFLAGS = ${libnghttpx_a_CPPFLAGS}
-@ENABLE_APP_TRUE@nghttpx_LDADD = libnghttpx.a ${LDADD} $(am__append_7) \
-@ENABLE_APP_TRUE@      $(am__append_9)
+@ENABLE_APP_TRUE@nghttpx_LDADD = libnghttpx.a ${LDADD} $(am__append_5) \
+@ENABLE_APP_TRUE@      $(am__append_7)
 @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_tls_test.cc shrpx_tls_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@     shrpx_worker_test.cc shrpx_worker_test.h \
@@ -1104,13 +1094,13 @@ LDADD = $(top_builddir)/lib/libnghttp2.la \
 @ENABLE_APP_TRUE@@HAVE_CUNIT_TRUE@nghttpx_unittest_CPPFLAGS =  \
 @ENABLE_APP_TRUE@@HAVE_CUNIT_TRUE@     ${AM_CPPFLAGS} \
 @ENABLE_APP_TRUE@@HAVE_CUNIT_TRUE@     -DNGHTTP2_SRC_DIR=\"$(top_srcdir)/src\" \
-@ENABLE_APP_TRUE@@HAVE_CUNIT_TRUE@     $(am__append_11) \
-@ENABLE_APP_TRUE@@HAVE_CUNIT_TRUE@     $(am__append_13)
+@ENABLE_APP_TRUE@@HAVE_CUNIT_TRUE@     $(am__append_9) \
+@ENABLE_APP_TRUE@@HAVE_CUNIT_TRUE@     $(am__append_11)
 @ENABLE_APP_TRUE@@HAVE_CUNIT_TRUE@nghttpx_unittest_LDADD =  \
 @ENABLE_APP_TRUE@@HAVE_CUNIT_TRUE@     libnghttpx.a ${LDADD} \
 @ENABLE_APP_TRUE@@HAVE_CUNIT_TRUE@     @CUNIT_LIBS@ @TESTLDADD@ \
-@ENABLE_APP_TRUE@@HAVE_CUNIT_TRUE@     $(am__append_12) \
-@ENABLE_APP_TRUE@@HAVE_CUNIT_TRUE@     $(am__append_14)
+@ENABLE_APP_TRUE@@HAVE_CUNIT_TRUE@     $(am__append_10) \
+@ENABLE_APP_TRUE@@HAVE_CUNIT_TRUE@     $(am__append_12)
 @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)
@@ -1120,7 +1110,7 @@ LDADD = $(top_builddir)/lib/libnghttp2.la \
 @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@ tls.cc tls.h \
 @ENABLE_ASIO_LIB_TRUE@ ssl_compat.h \
 @ENABLE_ASIO_LIB_TRUE@ timegm.c timegm.h \
 @ENABLE_ASIO_LIB_TRUE@ asio_common.cc asio_common.h \
@@ -1342,7 +1332,6 @@ distclean-compile:
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/h2load.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/h2load_http1_session.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@
@@ -1370,8 +1359,8 @@ distclean-compile:
 @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-timegm.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnghttp2_asio_la-tls.Plo@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnghttp2_asio_la-util.Plo@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnghttpx_a-app_helper.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnghttpx_a-http2.Po@am__quote@
@@ -1411,12 +1400,11 @@ distclean-compile:
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnghttpx_a-shrpx_rate_limit.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnghttpx_a-shrpx_router.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnghttpx_a-shrpx_signal.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnghttpx_a-shrpx_spdy_upstream.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnghttpx_a-shrpx_ssl.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnghttpx_a-shrpx_tls.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnghttpx_a-shrpx_worker.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnghttpx_a-shrpx_worker_process.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnghttpx_a-ssl.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnghttpx_a-timegm.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnghttpx_a-tls.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnghttpx_a-util.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnghttpx_a-xsi_strerror.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttp.Po@am__quote@
@@ -1434,12 +1422,12 @@ distclean-compile:
 @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_http_test.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttpx_unittest-shrpx_router_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-shrpx_tls_test.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttpx_unittest-shrpx_worker_test.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttpx_unittest-template_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)/ssl.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/timegm.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tls.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/util.Po@am__quote@
 
 .c.o:
@@ -1595,19 +1583,19 @@ libnghttpx_a-app_helper.obj: app_helper.cc
 @AMDEP_TRUE@@am__fastdepCXX_FALSE@     DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
 @am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttpx_a_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o libnghttpx_a-app_helper.obj `if test -f 'app_helper.cc'; then $(CYGPATH_W) 'app_helper.cc'; else $(CYGPATH_W) '$(srcdir)/app_helper.cc'; fi`
 
-libnghttpx_a-ssl.o: ssl.cc
-@am__fastdepCXX_TRUE@  $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttpx_a_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT libnghttpx_a-ssl.o -MD -MP -MF $(DEPDIR)/libnghttpx_a-ssl.Tpo -c -o libnghttpx_a-ssl.o `test -f 'ssl.cc' || echo '$(srcdir)/'`ssl.cc
-@am__fastdepCXX_TRUE@  $(AM_V_at)$(am__mv) $(DEPDIR)/libnghttpx_a-ssl.Tpo $(DEPDIR)/libnghttpx_a-ssl.Po
-@AMDEP_TRUE@@am__fastdepCXX_FALSE@     $(AM_V_CXX)source='ssl.cc' object='libnghttpx_a-ssl.o' libtool=no @AMDEPBACKSLASH@
+libnghttpx_a-tls.o: tls.cc
+@am__fastdepCXX_TRUE@  $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttpx_a_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT libnghttpx_a-tls.o -MD -MP -MF $(DEPDIR)/libnghttpx_a-tls.Tpo -c -o libnghttpx_a-tls.o `test -f 'tls.cc' || echo '$(srcdir)/'`tls.cc
+@am__fastdepCXX_TRUE@  $(AM_V_at)$(am__mv) $(DEPDIR)/libnghttpx_a-tls.Tpo $(DEPDIR)/libnghttpx_a-tls.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@     $(AM_V_CXX)source='tls.cc' object='libnghttpx_a-tls.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) $(libnghttpx_a_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o libnghttpx_a-ssl.o `test -f 'ssl.cc' || echo '$(srcdir)/'`ssl.cc
+@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttpx_a_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o libnghttpx_a-tls.o `test -f 'tls.cc' || echo '$(srcdir)/'`tls.cc
 
-libnghttpx_a-ssl.obj: ssl.cc
-@am__fastdepCXX_TRUE@  $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttpx_a_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT libnghttpx_a-ssl.obj -MD -MP -MF $(DEPDIR)/libnghttpx_a-ssl.Tpo -c -o libnghttpx_a-ssl.obj `if test -f 'ssl.cc'; then $(CYGPATH_W) 'ssl.cc'; else $(CYGPATH_W) '$(srcdir)/ssl.cc'; fi`
-@am__fastdepCXX_TRUE@  $(AM_V_at)$(am__mv) $(DEPDIR)/libnghttpx_a-ssl.Tpo $(DEPDIR)/libnghttpx_a-ssl.Po
-@AMDEP_TRUE@@am__fastdepCXX_FALSE@     $(AM_V_CXX)source='ssl.cc' object='libnghttpx_a-ssl.obj' libtool=no @AMDEPBACKSLASH@
+libnghttpx_a-tls.obj: tls.cc
+@am__fastdepCXX_TRUE@  $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttpx_a_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT libnghttpx_a-tls.obj -MD -MP -MF $(DEPDIR)/libnghttpx_a-tls.Tpo -c -o libnghttpx_a-tls.obj `if test -f 'tls.cc'; then $(CYGPATH_W) 'tls.cc'; else $(CYGPATH_W) '$(srcdir)/tls.cc'; fi`
+@am__fastdepCXX_TRUE@  $(AM_V_at)$(am__mv) $(DEPDIR)/libnghttpx_a-tls.Tpo $(DEPDIR)/libnghttpx_a-tls.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@     $(AM_V_CXX)source='tls.cc' object='libnghttpx_a-tls.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) $(libnghttpx_a_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o libnghttpx_a-ssl.obj `if test -f 'ssl.cc'; then $(CYGPATH_W) 'ssl.cc'; else $(CYGPATH_W) '$(srcdir)/ssl.cc'; fi`
+@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttpx_a_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o libnghttpx_a-tls.obj `if test -f 'tls.cc'; then $(CYGPATH_W) 'tls.cc'; else $(CYGPATH_W) '$(srcdir)/tls.cc'; fi`
 
 libnghttpx_a-shrpx_config.o: shrpx_config.cc
 @am__fastdepCXX_TRUE@  $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttpx_a_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT libnghttpx_a-shrpx_config.o -MD -MP -MF $(DEPDIR)/libnghttpx_a-shrpx_config.Tpo -c -o libnghttpx_a-shrpx_config.o `test -f 'shrpx_config.cc' || echo '$(srcdir)/'`shrpx_config.cc
@@ -1819,19 +1807,19 @@ libnghttpx_a-shrpx_io_control.obj: shrpx_io_control.cc
 @AMDEP_TRUE@@am__fastdepCXX_FALSE@     DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
 @am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttpx_a_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o libnghttpx_a-shrpx_io_control.obj `if test -f 'shrpx_io_control.cc'; then $(CYGPATH_W) 'shrpx_io_control.cc'; else $(CYGPATH_W) '$(srcdir)/shrpx_io_control.cc'; fi`
 
-libnghttpx_a-shrpx_ssl.o: shrpx_ssl.cc
-@am__fastdepCXX_TRUE@  $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttpx_a_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT libnghttpx_a-shrpx_ssl.o -MD -MP -MF $(DEPDIR)/libnghttpx_a-shrpx_ssl.Tpo -c -o libnghttpx_a-shrpx_ssl.o `test -f 'shrpx_ssl.cc' || echo '$(srcdir)/'`shrpx_ssl.cc
-@am__fastdepCXX_TRUE@  $(AM_V_at)$(am__mv) $(DEPDIR)/libnghttpx_a-shrpx_ssl.Tpo $(DEPDIR)/libnghttpx_a-shrpx_ssl.Po
-@AMDEP_TRUE@@am__fastdepCXX_FALSE@     $(AM_V_CXX)source='shrpx_ssl.cc' object='libnghttpx_a-shrpx_ssl.o' libtool=no @AMDEPBACKSLASH@
+libnghttpx_a-shrpx_tls.o: shrpx_tls.cc
+@am__fastdepCXX_TRUE@  $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttpx_a_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT libnghttpx_a-shrpx_tls.o -MD -MP -MF $(DEPDIR)/libnghttpx_a-shrpx_tls.Tpo -c -o libnghttpx_a-shrpx_tls.o `test -f 'shrpx_tls.cc' || echo '$(srcdir)/'`shrpx_tls.cc
+@am__fastdepCXX_TRUE@  $(AM_V_at)$(am__mv) $(DEPDIR)/libnghttpx_a-shrpx_tls.Tpo $(DEPDIR)/libnghttpx_a-shrpx_tls.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@     $(AM_V_CXX)source='shrpx_tls.cc' object='libnghttpx_a-shrpx_tls.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) $(libnghttpx_a_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o libnghttpx_a-shrpx_ssl.o `test -f 'shrpx_ssl.cc' || echo '$(srcdir)/'`shrpx_ssl.cc
+@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttpx_a_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o libnghttpx_a-shrpx_tls.o `test -f 'shrpx_tls.cc' || echo '$(srcdir)/'`shrpx_tls.cc
 
-libnghttpx_a-shrpx_ssl.obj: shrpx_ssl.cc
-@am__fastdepCXX_TRUE@  $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttpx_a_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT libnghttpx_a-shrpx_ssl.obj -MD -MP -MF $(DEPDIR)/libnghttpx_a-shrpx_ssl.Tpo -c -o libnghttpx_a-shrpx_ssl.obj `if test -f 'shrpx_ssl.cc'; then $(CYGPATH_W) 'shrpx_ssl.cc'; else $(CYGPATH_W) '$(srcdir)/shrpx_ssl.cc'; fi`
-@am__fastdepCXX_TRUE@  $(AM_V_at)$(am__mv) $(DEPDIR)/libnghttpx_a-shrpx_ssl.Tpo $(DEPDIR)/libnghttpx_a-shrpx_ssl.Po
-@AMDEP_TRUE@@am__fastdepCXX_FALSE@     $(AM_V_CXX)source='shrpx_ssl.cc' object='libnghttpx_a-shrpx_ssl.obj' libtool=no @AMDEPBACKSLASH@
+libnghttpx_a-shrpx_tls.obj: shrpx_tls.cc
+@am__fastdepCXX_TRUE@  $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttpx_a_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT libnghttpx_a-shrpx_tls.obj -MD -MP -MF $(DEPDIR)/libnghttpx_a-shrpx_tls.Tpo -c -o libnghttpx_a-shrpx_tls.obj `if test -f 'shrpx_tls.cc'; then $(CYGPATH_W) 'shrpx_tls.cc'; else $(CYGPATH_W) '$(srcdir)/shrpx_tls.cc'; fi`
+@am__fastdepCXX_TRUE@  $(AM_V_at)$(am__mv) $(DEPDIR)/libnghttpx_a-shrpx_tls.Tpo $(DEPDIR)/libnghttpx_a-shrpx_tls.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@     $(AM_V_CXX)source='shrpx_tls.cc' object='libnghttpx_a-shrpx_tls.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) $(libnghttpx_a_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o libnghttpx_a-shrpx_ssl.obj `if test -f 'shrpx_ssl.cc'; then $(CYGPATH_W) 'shrpx_ssl.cc'; else $(CYGPATH_W) '$(srcdir)/shrpx_ssl.cc'; fi`
+@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttpx_a_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o libnghttpx_a-shrpx_tls.obj `if test -f 'shrpx_tls.cc'; then $(CYGPATH_W) 'shrpx_tls.cc'; else $(CYGPATH_W) '$(srcdir)/shrpx_tls.cc'; fi`
 
 libnghttpx_a-shrpx_worker.o: shrpx_worker.cc
 @am__fastdepCXX_TRUE@  $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttpx_a_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT libnghttpx_a-shrpx_worker.o -MD -MP -MF $(DEPDIR)/libnghttpx_a-shrpx_worker.Tpo -c -o libnghttpx_a-shrpx_worker.o `test -f 'shrpx_worker.cc' || echo '$(srcdir)/'`shrpx_worker.cc
@@ -2085,20 +2073,6 @@ libnghttpx_a-shrpx_dns_tracker.obj: shrpx_dns_tracker.cc
 @AMDEP_TRUE@@am__fastdepCXX_FALSE@     DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
 @am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttpx_a_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o libnghttpx_a-shrpx_dns_tracker.obj `if test -f 'shrpx_dns_tracker.cc'; then $(CYGPATH_W) 'shrpx_dns_tracker.cc'; else $(CYGPATH_W) '$(srcdir)/shrpx_dns_tracker.cc'; fi`
 
-libnghttpx_a-shrpx_spdy_upstream.o: shrpx_spdy_upstream.cc
-@am__fastdepCXX_TRUE@  $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttpx_a_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT libnghttpx_a-shrpx_spdy_upstream.o -MD -MP -MF $(DEPDIR)/libnghttpx_a-shrpx_spdy_upstream.Tpo -c -o libnghttpx_a-shrpx_spdy_upstream.o `test -f 'shrpx_spdy_upstream.cc' || echo '$(srcdir)/'`shrpx_spdy_upstream.cc
-@am__fastdepCXX_TRUE@  $(AM_V_at)$(am__mv) $(DEPDIR)/libnghttpx_a-shrpx_spdy_upstream.Tpo $(DEPDIR)/libnghttpx_a-shrpx_spdy_upstream.Po
-@AMDEP_TRUE@@am__fastdepCXX_FALSE@     $(AM_V_CXX)source='shrpx_spdy_upstream.cc' object='libnghttpx_a-shrpx_spdy_upstream.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) $(libnghttpx_a_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o libnghttpx_a-shrpx_spdy_upstream.o `test -f 'shrpx_spdy_upstream.cc' || echo '$(srcdir)/'`shrpx_spdy_upstream.cc
-
-libnghttpx_a-shrpx_spdy_upstream.obj: shrpx_spdy_upstream.cc
-@am__fastdepCXX_TRUE@  $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttpx_a_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT libnghttpx_a-shrpx_spdy_upstream.obj -MD -MP -MF $(DEPDIR)/libnghttpx_a-shrpx_spdy_upstream.Tpo -c -o libnghttpx_a-shrpx_spdy_upstream.obj `if test -f 'shrpx_spdy_upstream.cc'; then $(CYGPATH_W) 'shrpx_spdy_upstream.cc'; else $(CYGPATH_W) '$(srcdir)/shrpx_spdy_upstream.cc'; fi`
-@am__fastdepCXX_TRUE@  $(AM_V_at)$(am__mv) $(DEPDIR)/libnghttpx_a-shrpx_spdy_upstream.Tpo $(DEPDIR)/libnghttpx_a-shrpx_spdy_upstream.Po
-@AMDEP_TRUE@@am__fastdepCXX_FALSE@     $(AM_V_CXX)source='shrpx_spdy_upstream.cc' object='libnghttpx_a-shrpx_spdy_upstream.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) $(libnghttpx_a_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o libnghttpx_a-shrpx_spdy_upstream.obj `if test -f 'shrpx_spdy_upstream.cc'; then $(CYGPATH_W) 'shrpx_spdy_upstream.cc'; else $(CYGPATH_W) '$(srcdir)/shrpx_spdy_upstream.cc'; fi`
-
 libnghttpx_a-shrpx_mruby.o: shrpx_mruby.cc
 @am__fastdepCXX_TRUE@  $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttpx_a_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT libnghttpx_a-shrpx_mruby.o -MD -MP -MF $(DEPDIR)/libnghttpx_a-shrpx_mruby.Tpo -c -o libnghttpx_a-shrpx_mruby.o `test -f 'shrpx_mruby.cc' || echo '$(srcdir)/'`shrpx_mruby.cc
 @am__fastdepCXX_TRUE@  $(AM_V_at)$(am__mv) $(DEPDIR)/libnghttpx_a-shrpx_mruby.Tpo $(DEPDIR)/libnghttpx_a-shrpx_mruby.Po
@@ -2183,12 +2157,12 @@ libnghttp2_asio_la-http2.lo: http2.cc
 @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@
+libnghttp2_asio_la-tls.lo: tls.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-tls.lo -MD -MP -MF $(DEPDIR)/libnghttp2_asio_la-tls.Tpo -c -o libnghttp2_asio_la-tls.lo `test -f 'tls.cc' || echo '$(srcdir)/'`tls.cc
+@am__fastdepCXX_TRUE@  $(AM_V_at)$(am__mv) $(DEPDIR)/libnghttp2_asio_la-tls.Tpo $(DEPDIR)/libnghttp2_asio_la-tls.Plo
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@     $(AM_V_CXX)source='tls.cc' object='libnghttp2_asio_la-tls.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
+@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-tls.lo `test -f 'tls.cc' || echo '$(srcdir)/'`tls.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
@@ -2386,19 +2360,19 @@ nghttpx_unittest-shrpx-unittest.obj: shrpx-unittest.cc
 @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@
+nghttpx_unittest-shrpx_tls_test.o: shrpx_tls_test.cc
+@am__fastdepCXX_TRUE@  $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(nghttpx_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT nghttpx_unittest-shrpx_tls_test.o -MD -MP -MF $(DEPDIR)/nghttpx_unittest-shrpx_tls_test.Tpo -c -o nghttpx_unittest-shrpx_tls_test.o `test -f 'shrpx_tls_test.cc' || echo '$(srcdir)/'`shrpx_tls_test.cc
+@am__fastdepCXX_TRUE@  $(AM_V_at)$(am__mv) $(DEPDIR)/nghttpx_unittest-shrpx_tls_test.Tpo $(DEPDIR)/nghttpx_unittest-shrpx_tls_test.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@     $(AM_V_CXX)source='shrpx_tls_test.cc' object='nghttpx_unittest-shrpx_tls_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
+@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_tls_test.o `test -f 'shrpx_tls_test.cc' || echo '$(srcdir)/'`shrpx_tls_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@
+nghttpx_unittest-shrpx_tls_test.obj: shrpx_tls_test.cc
+@am__fastdepCXX_TRUE@  $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(nghttpx_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT nghttpx_unittest-shrpx_tls_test.obj -MD -MP -MF $(DEPDIR)/nghttpx_unittest-shrpx_tls_test.Tpo -c -o nghttpx_unittest-shrpx_tls_test.obj `if test -f 'shrpx_tls_test.cc'; then $(CYGPATH_W) 'shrpx_tls_test.cc'; else $(CYGPATH_W) '$(srcdir)/shrpx_tls_test.cc'; fi`
+@am__fastdepCXX_TRUE@  $(AM_V_at)$(am__mv) $(DEPDIR)/nghttpx_unittest-shrpx_tls_test.Tpo $(DEPDIR)/nghttpx_unittest-shrpx_tls_test.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@     $(AM_V_CXX)source='shrpx_tls_test.cc' object='nghttpx_unittest-shrpx_tls_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`
+@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_tls_test.obj `if test -f 'shrpx_tls_test.cc'; then $(CYGPATH_W) 'shrpx_tls_test.cc'; else $(CYGPATH_W) '$(srcdir)/shrpx_tls_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
index 7fa0b8e..32a7426 100644 (file)
@@ -27,7 +27,9 @@
 
 #include "nghttp2_config.h"
 
+#ifndef _WIN32
 #include <sys/uio.h>
+#endif // !_WIN32
 
 #include <cassert>
 
@@ -271,6 +273,6 @@ ByteRef make_byte_ref(BlockAllocator &alloc, size_t size) {
   return {dst, size};
 }
 
-} // namespace aria2
+} // namespace nghttp2
 
 #endif // ALLOCATOR_H
index a736d30..e61c579 100644 (file)
@@ -155,7 +155,7 @@ void print_nv(nghttp2_nv *nva, size_t nvlen) {
     print_nv(nva);
   }
 }
-} // namelen
+} // namespace
 
 void print_timer() {
   auto millis = get_timer();
@@ -327,8 +327,9 @@ void print_frame(print_type ptype, const nghttp2_frame *frame) {
     break;
   case NGHTTP2_GOAWAY:
     print_frame_attr_indent();
-    fprintf(outfile, "(last_stream_id=%d, error_code=%s(0x%02x), "
-                     "opaque_data(%u)=[%s])\n",
+    fprintf(outfile,
+            "(last_stream_id=%d, error_code=%s(0x%02x), "
+            "opaque_data(%u)=[%s])\n",
             frame->goaway.last_stream_id,
             nghttp2_http2_strerror(frame->goaway.error_code),
             frame->goaway.error_code,
@@ -425,8 +426,8 @@ int verbose_on_data_chunk_recv_callback(nghttp2_session *session, uint8_t flags,
   return 0;
 }
 
-int verbose_error_callback(nghttp2_session *session, const char *msg,
-                           size_t len, void *user_data) {
+int verbose_error_callback(nghttp2_session *session, int lib_error_code,
+                           const char *msg, size_t len, void *user_data) {
   print_timer();
   fprintf(outfile, " [ERROR] %.*s\n", (int)len, msg);
   fflush(outfile);
index 263bb99..2e1ef53 100644 (file)
@@ -60,8 +60,8 @@ 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);
 
-int verbose_error_callback(nghttp2_session *session, const char *msg,
-                           size_t len, void *user_data);
+int verbose_error_callback(nghttp2_session *session, int lib_error_code,
+                           const char *msg, size_t len, void *user_data);
 
 // Returns difference between |a| and |b| in milliseconds, assuming
 // |a| is more recent than |b|.
index 5762c4c..e142ded 100644 (file)
@@ -96,29 +96,48 @@ boost::asio::io_service &session::io_service() const {
 
 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 std::string &uri, header_map h,
+                               priority_spec prio) const {
+  return impl_->submit(ec, method, uri, generator_cb(), std::move(h),
+                       std::move(prio));
 }
 
 const request *session::submit(boost::system::error_code &ec,
                                const std::string &method,
                                const std::string &uri, std::string data,
-                               header_map h) const {
+                               header_map h, priority_spec prio) const {
   return impl_->submit(ec, method, uri, string_generator(std::move(data)),
-                       std::move(h));
+                       std::move(h), std::move(prio));
 }
 
 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));
+                               header_map h, priority_spec prio) const {
+  return impl_->submit(ec, method, uri, std::move(cb), std::move(h),
+                       std::move(prio));
 }
 
 void session::read_timeout(const boost::posix_time::time_duration &t) {
   impl_->read_timeout(t);
 }
 
+priority_spec::priority_spec(const int32_t stream_id, const int32_t weight,
+                             const bool exclusive)
+    : valid_(true) {
+  nghttp2_priority_spec_init(&spec_, stream_id, weight, exclusive);
+}
+
+const nghttp2_priority_spec *priority_spec::get() const {
+  if (!valid_) {
+    return nullptr;
+  }
+
+  return &spec_;
+}
+
+const bool priority_spec::valid() const { return valid_; }
+
 } // namespace client
 } // namespace asio_http2
-} // nghttp2
+} // namespace nghttp2
index 8ac4e16..16f91fa 100644 (file)
@@ -47,6 +47,7 @@ session_impl::session_impl(
       deadline_(io_service),
       connect_timeout_(connect_timeout),
       read_timeout_(boost::posix_time::seconds(60)),
+      ping_(io_service),
       session_(nullptr),
       data_pending_(nullptr),
       data_pendinglen_(0),
@@ -102,6 +103,27 @@ void session_impl::handle_deadline() {
       std::bind(&session_impl::handle_deadline, this->shared_from_this()));
 }
 
+void handle_ping2(const boost::system::error_code &ec, int) {}
+
+void session_impl::start_ping() {
+  ping_.expires_from_now(boost::posix_time::seconds(30));
+  ping_.async_wait(std::bind(&session_impl::handle_ping, shared_from_this(),
+                             std::placeholders::_1));
+}
+
+void session_impl::handle_ping(const boost::system::error_code &ec) {
+  if (stopped_ || ec == boost::asio::error::operation_aborted ||
+      !streams_.empty()) {
+    return;
+  }
+
+  nghttp2_submit_ping(session_, NGHTTP2_FLAG_NONE, nullptr);
+
+  signal_write();
+
+  start_ping();
+}
+
 void session_impl::connected(tcp::resolver::iterator endpoint_it) {
   if (!setup_session()) {
     return;
@@ -112,6 +134,8 @@ void session_impl::connected(tcp::resolver::iterator endpoint_it) {
   do_write();
   do_read();
 
+  start_ping();
+
   auto &connect_cb = on_connect();
   if (connect_cb) {
     connect_cb(endpoint_it);
@@ -433,6 +457,9 @@ std::unique_ptr<stream> session_impl::pop_stream(int32_t stream_id) {
   }
   auto strm = std::move((*it).second);
   streams_.erase(it);
+  if (streams_.empty()) {
+    start_ping();
+  }
   return strm;
 }
 
@@ -441,6 +468,7 @@ stream *session_impl::create_push_stream(int32_t stream_id) {
   strm->stream_id(stream_id);
   auto p = streams_.emplace(stream_id, std::move(strm));
   assert(p.second);
+  ping_.cancel();
   return (*p.first).second.get();
 }
 
@@ -451,7 +479,7 @@ std::unique_ptr<stream> session_impl::create_stream() {
 const request *session_impl::submit(boost::system::error_code &ec,
                                     const std::string &method,
                                     const std::string &uri, generator_cb cb,
-                                    header_map h) {
+                                    header_map h, priority_spec prio) {
   ec.clear();
 
   if (stopped_) {
@@ -531,7 +559,7 @@ const request *session_impl::submit(boost::system::error_code &ec,
     prdptr = &prd;
   }
 
-  auto stream_id = nghttp2_submit_request(session_, nullptr, nva.data(),
+  auto stream_id = nghttp2_submit_request(session_, prio.get(), nva.data(),
                                           nva.size(), prdptr, strm.get());
   if (stream_id < 0) {
     ec = make_error_code(static_cast<nghttp2_error>(stream_id));
@@ -544,6 +572,7 @@ const request *session_impl::submit(boost::system::error_code &ec,
 
   auto p = streams_.emplace(stream_id, std::move(strm));
   assert(p.second);
+  ping_.cancel();
   return &(*p.first).second->request();
 }
 
@@ -715,6 +744,7 @@ void session_impl::stop() {
 
   shutdown_socket();
   deadline_.cancel();
+  ping_.cancel();
   stopped_ = true;
 }
 
@@ -726,4 +756,4 @@ void session_impl::read_timeout(const boost::posix_time::time_duration &t) {
 
 } // namespace client
 } // namespace asio_http2
-} // nghttp2
+} // namespace nghttp2
index 9cd4184..694ac20 100644 (file)
@@ -70,7 +70,7 @@ public:
 
   const request *submit(boost::system::error_code &ec,
                         const std::string &method, const std::string &uri,
-                        generator_cb cb, header_map h);
+                        generator_cb cb, header_map h, priority_spec spec);
 
   virtual void start_connect(tcp::resolver::iterator endpoint_it) = 0;
   virtual tcp::socket &socket() = 0;
@@ -109,6 +109,8 @@ private:
   bool setup_session();
   void call_error_cb(const boost::system::error_code &ec);
   void handle_deadline();
+  void start_ping();
+  void handle_ping(const boost::system::error_code &ec);
 
   boost::asio::io_service &io_service_;
   tcp::resolver resolver_;
@@ -122,6 +124,8 @@ private:
   boost::posix_time::time_duration connect_timeout_;
   boost::posix_time::time_duration read_timeout_;
 
+  boost::asio::deadline_timer ping_;
+
   nghttp2_session *session_;
 
   const uint8_t *data_pending_;
index 4ac2ca1..f1c2133 100644 (file)
@@ -45,8 +45,9 @@ session_tls_impl::~session_tls_impl() {}
 void session_tls_impl::start_connect(tcp::resolver::iterator endpoint_it) {
   auto self = std::static_pointer_cast<session_tls_impl>(shared_from_this());
   boost::asio::async_connect(
-      socket(), endpoint_it, [self](const boost::system::error_code &ec,
-                                    tcp::resolver::iterator endpoint_it) {
+      socket(), endpoint_it,
+      [self](const boost::system::error_code &ec,
+             tcp::resolver::iterator endpoint_it) {
         if (self->stopped()) {
           return;
         }
index 7583001..3291885 100644 (file)
@@ -28,7 +28,7 @@
 
 #include <boost/asio/ssl.hpp>
 
-#include "ssl.h"
+#include "tls.h"
 #include "util.h"
 
 namespace nghttp2 {
index c93b361..590e931 100644 (file)
@@ -24,6 +24,7 @@
  */
 #include "asio_common.h"
 
+#include <fcntl.h>
 #include <memory>
 
 #include "util.h"
index 2f19f6d..1206449 100644 (file)
@@ -169,6 +169,6 @@ private:
 
 } // namespace server
 } // namespace asio_http2
-} // namespace nghttp
+} // namespace nghttp2
 
 #endif // ASIO_SERVER_HTTP2_HANDLER_H
index da138b7..83368d4 100644 (file)
@@ -28,7 +28,7 @@
 
 #include "asio_server.h"
 #include "util.h"
-#include "ssl.h"
+#include "tls.h"
 #include "template.h"
 
 namespace nghttp2 {
index 53fc988..aa73cc5 100644 (file)
@@ -28,7 +28,7 @@
 
 #include <boost/asio/ssl.hpp>
 
-#include "ssl.h"
+#include "tls.h"
 #include "util.h"
 
 namespace nghttp2 {
@@ -72,7 +72,7 @@ configure_tls_context_easy(boost::system::error_code &ec,
   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);
+  SSL_CTX_set_cipher_list(ctx, tls::DEFAULT_CIPHER_LIST);
 
 #ifndef OPENSSL_NO_EC
   auto ecdh = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1);
index cbf0e51..5f7789c 100644 (file)
 #include <future>
 #include <random>
 
-#ifdef HAVE_SPDYLAY
-#include <spdylay/spdylay.h>
-#endif // HAVE_SPDYLAY
-
 #include <openssl/err.h>
 
 #include "http-parser/http_parser.h"
 
 #include "h2load_http1_session.h"
 #include "h2load_http2_session.h"
-#ifdef HAVE_SPDYLAY
-#include "h2load_spdy_session.h"
-#endif // HAVE_SPDYLAY
-#include "ssl.h"
+#include "tls.h"
 #include "http2.h"
 #include "util.h"
 #include "template.h"
@@ -79,7 +72,7 @@ bool recorded(const std::chrono::steady_clock::time_point &t) {
 } // namespace
 
 Config::Config()
-    : ciphers(ssl::DEFAULT_CIPHER_LIST),
+    : ciphers(tls::DEFAULT_CIPHER_LIST),
       data_length(-1),
       addrs(nullptr),
       nreqs(1),
@@ -90,6 +83,8 @@ Config::Config()
       connection_window_bits(30),
       rate(0),
       rate_period(1.0),
+      duration(0.0),
+      warm_up_time(0.0),
       conn_active_timeout(0.),
       conn_inactivity_timeout(0.),
       no_tls_proto(PROTO_HTTP2),
@@ -118,6 +113,7 @@ Config::~Config() {
 }
 
 bool Config::is_rate_mode() const { return (this->rate != 0); }
+bool Config::is_timing_based_mode() const { return (this->duration > 0); }
 bool Config::has_base_uri() const { return (!this->base_uri.empty()); }
 Config config;
 
@@ -151,34 +147,13 @@ std::mt19937 gen(rd());
 } // namespace
 
 namespace {
-void sampling_init(Sampling &smp, size_t total, size_t max_samples) {
+void sampling_init(Sampling &smp, size_t max_samples) {
   smp.n = 0;
-
-  if (total <= max_samples) {
-    smp.interval = 0.;
-    smp.point = 0.;
-    return;
-  }
-
-  smp.interval = static_cast<double>(total) / max_samples;
-
-  std::uniform_real_distribution<> dis(0., smp.interval);
-
-  smp.point = dis(gen);
-}
-} // namespace
-
-namespace {
-bool sampling_should_pick(Sampling &smp) {
-  return smp.interval == 0. || smp.n == ceil(smp.point);
+  smp.max_samples = max_samples;
 }
 } // namespace
 
 namespace {
-void sampling_advance_point(Sampling &smp) { smp.point += smp.interval; }
-} // namespace
-
-namespace {
 void writecb(struct ev_loop *loop, ev_io *w, int revents) {
   auto client = static_cast<Client *>(w->data);
   client->restart_timeout();
@@ -190,6 +165,7 @@ void writecb(struct ev_loop *loop, ev_io *w, int revents) {
     rv = client->connect();
     if (rv != 0) {
       client->fail();
+      client->worker->free_client(client);
       delete client;
       return;
     }
@@ -197,6 +173,7 @@ void writecb(struct ev_loop *loop, ev_io *w, int revents) {
   }
   if (rv != 0) {
     client->fail();
+    client->worker->free_client(client);
     delete client;
   }
 }
@@ -210,6 +187,7 @@ void readcb(struct ev_loop *loop, ev_io *w, int revents) {
     if (client->try_again_or_fail() == 0) {
       return;
     }
+    client->worker->free_client(client);
     delete client;
     return;
   }
@@ -241,13 +219,68 @@ void rate_period_timeout_w_cb(struct ev_loop *loop, ev_timer *w, int revents) {
       std::cerr << "client could not connect to host" << std::endl;
       client->fail();
     } else {
-      client.release();
+      if (worker->config->is_timing_based_mode()) {
+        worker->clients.push_back(client.release());
+      } else {
+        client.release();
+      }
     }
     worker->report_rate_progress();
   }
-  if (worker->nconns_made >= worker->nclients) {
-    ev_timer_stop(worker->loop, w);
+  if (!worker->config->is_timing_based_mode()) {
+    if (worker->nconns_made >= worker->nclients) {
+      ev_timer_stop(worker->loop, w);
+    }
+  } else {
+    // To check whether all created clients are pushed correctly
+    assert(worker->nclients == worker->clients.size());
+  }
+}
+} // namespace
+
+namespace {
+// Called when the duration for infinite number of requests are over
+void duration_timeout_cb(struct ev_loop *loop, ev_timer *w, int revents) {
+  auto worker = static_cast<Worker *>(w->data);
+
+  worker->current_phase = Phase::DURATION_OVER;
+
+  std::cout << "Main benchmark duration is over for thread #" << worker->id
+            << ". Stopping all clients." << std::endl;
+  worker->stop_all_clients();
+  std::cout << "Stopped all clients for thread #" << worker->id << std::endl;
+}
+} // namespace
+
+namespace {
+// Called when the warmup duration for infinite number of requests are over
+void warmup_timeout_cb(struct ev_loop *loop, ev_timer *w, int revents) {
+  auto worker = static_cast<Worker *>(w->data);
+
+  std::cout << "Warm-up phase is over for thread #" << worker->id << "."
+            << std::endl;
+  std::cout << "Main benchmark duration is started for thread #" << worker->id
+            << "." << std::endl;
+  assert(worker->stats.req_started == 0);
+  assert(worker->stats.req_done == 0);
+
+  for (auto client : worker->clients) {
+    if (client) {
+      assert(client->req_todo == 0);
+      assert(client->req_left == 1);
+      assert(client->req_inflight == 0);
+      assert(client->req_started == 0);
+      assert(client->req_done == 0);
+
+      client->record_client_start_time();
+      client->clear_connect_times();
+      client->record_connect_start_time();
+    }
   }
+
+  worker->current_phase = Phase::MAIN_DURATION;
+
+  ev_timer_start(worker->loop, &worker->duration_watcher);
 }
 } // namespace
 
@@ -269,8 +302,7 @@ void conn_timeout_cb(EV_P_ ev_timer *w, int revents) {
 
 namespace {
 bool check_stop_client_request_timeout(Client *client, ev_timer *w) {
-  if (client->req_left == 0 ||
-      client->streams.size() >= client->session->max_concurrent_streams()) {
+  if (client->req_left == 0) {
     // no more requests to make, stop timer
     ev_timer_stop(client->worker->loop, w);
     return true;
@@ -284,6 +316,11 @@ namespace {
 void client_request_timeout_cb(struct ev_loop *loop, ev_timer *w, int revents) {
   auto client = static_cast<Client *>(w->data);
 
+  if (client->streams.size() >= (size_t)config.max_concurrent_streams) {
+    ev_timer_stop(client->worker->loop, w);
+    return;
+  }
+
   if (client->submit_request() != 0) {
     ev_timer_stop(client->worker->loop, w);
     client->process_request_failure();
@@ -336,6 +373,11 @@ Client::Client(uint32_t id, Worker *worker, size_t req_todo)
       fd(-1),
       new_connection_requested(false),
       final(false) {
+  if (req_todo == 0) { // this means infinite number of requests are to be made
+    // This ensures that number of requests are unbounded
+    // Just a positive number is fine, we chose the first positive number
+    req_left = 1;
+  }
   ev_io_init(&wev, writecb, 0, EV_WRITE);
   ev_io_init(&rev, readcb, 0, EV_READ);
 
@@ -361,10 +403,7 @@ Client::~Client() {
     SSL_free(ssl);
   }
 
-  if (sampling_should_pick(worker->client_smp)) {
-    sampling_advance_point(worker->client_smp);
-    worker->sample_client_stat(&cstat);
-  }
+  worker->sample_client_stat(&cstat);
   ++worker->client_smp.n;
 }
 
@@ -407,9 +446,17 @@ int Client::make_socket(addrinfo *addr) {
 int Client::connect() {
   int rv;
 
-  record_client_start_time();
-  clear_connect_times();
-  record_connect_start_time();
+  if (!worker->config->is_timing_based_mode() ||
+      worker->current_phase == Phase::MAIN_DURATION) {
+    record_client_start_time();
+    clear_connect_times();
+    record_connect_start_time();
+  } else if (worker->current_phase == Phase::INITIAL_IDLE) {
+    worker->current_phase = Phase::WARM_UP;
+    std::cout << "Warm-up started for thread #" << worker->id << "."
+              << std::endl;
+    ev_timer_start(worker->loop, &worker->warmup_watcher);
+  }
 
   if (worker->config->conn_inactivity_timeout > 0.) {
     ev_timer_again(worker->loop, &conn_inactivity_watcher);
@@ -467,13 +514,17 @@ int Client::try_again_or_fail() {
 
   if (new_connection_requested) {
     new_connection_requested = false;
+
     if (req_left) {
-      // At the moment, we don't have a facility to re-start request
-      // already in in-flight.  Make them fail.
-      worker->stats.req_failed += req_inflight;
-      worker->stats.req_error += req_inflight;
 
-      req_inflight = 0;
+      if (worker->current_phase == Phase::MAIN_DURATION) {
+        // At the moment, we don't have a facility to re-start request
+        // already in in-flight.  Make them fail.
+        worker->stats.req_failed += req_inflight;
+        worker->stats.req_error += req_inflight;
+
+        req_inflight = 0;
+      }
 
       // Keep using current address
       if (connect() == 0) {
@@ -529,11 +580,16 @@ int Client::submit_request() {
     return -1;
   }
 
+  if (worker->current_phase != Phase::MAIN_DURATION) {
+    return 0;
+  }
+
   ++worker->stats.req_started;
-  --req_left;
   ++req_started;
   ++req_inflight;
-
+  if (!worker->config->is_timing_based_mode()) {
+    --req_left;
+  }
   // if an active timeout is set and this is the last request to be submitted
   // on this connection, start the active timeout.
   if (worker->config->conn_active_timeout > 0. && req_left == 0) {
@@ -544,6 +600,10 @@ int Client::submit_request() {
 }
 
 void Client::process_timedout_streams() {
+  if (worker->current_phase != Phase::MAIN_DURATION) {
+    return;
+  }
+
   for (auto &p : streams) {
     auto &req_stat = p.second.req_stat;
     if (!req_stat.completed) {
@@ -557,6 +617,10 @@ void Client::process_timedout_streams() {
 }
 
 void Client::process_abandoned_streams() {
+  if (worker->current_phase != Phase::MAIN_DURATION) {
+    return;
+  }
+
   auto req_abandoned = req_inflight + req_left;
 
   worker->stats.req_failed += req_abandoned;
@@ -567,6 +631,10 @@ void Client::process_abandoned_streams() {
 }
 
 void Client::process_request_failure() {
+  if (worker->current_phase != Phase::MAIN_DURATION) {
+    return;
+  }
+
   worker->stats.req_failed += req_left;
   worker->stats.req_error += req_left;
 
@@ -575,6 +643,8 @@ void Client::process_request_failure() {
   if (req_inflight == 0) {
     terminate_session();
   }
+  std::cout << "Process Request Failure:" << worker->stats.req_failed
+            << std::endl;
 }
 
 namespace {
@@ -625,7 +695,7 @@ 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 << "TLS Protocol: " << ssl::get_tls_protocol(ssl) << "\n"
+    std::cout << "TLS Protocol: " << tls::get_tls_protocol(ssl) << "\n"
               << "Cipher: " << SSL_CIPHER_get_name(cipher) << std::endl;
     print_server_tmp_key(ssl);
   }
@@ -653,6 +723,15 @@ void Client::on_header(int32_t stream_id, const uint8_t *name, size_t namelen,
     return;
   }
   auto &stream = (*itr).second;
+
+  if (worker->current_phase != Phase::MAIN_DURATION) {
+    // If the stream is for warm-up phase, then mark as a success
+    // But we do not update the count for 2xx, 3xx, etc status codes
+    // Same has been done in on_status_code function
+    stream.status_success = 1;
+    return;
+  }
+
   if (stream.status_success == -1 && namelen == 7 &&
       util::streq_l(":status", name, namelen)) {
     int status = 0;
@@ -691,6 +770,11 @@ void Client::on_status_code(int32_t stream_id, uint16_t status) {
   }
   auto &stream = (*itr).second;
 
+  if (worker->current_phase != Phase::MAIN_DURATION) {
+    stream.status_success = 1;
+    return;
+  }
+
   if (status >= 200 && status < 300) {
     ++worker->stats.status[2];
     stream.status_success = 1;
@@ -706,40 +790,39 @@ void Client::on_status_code(int32_t stream_id, uint16_t status) {
 }
 
 void Client::on_stream_close(int32_t stream_id, bool success, bool final) {
-  ++req_done;
-  --req_inflight;
+  if (worker->current_phase == Phase::MAIN_DURATION) {
+    if (req_inflight > 0) {
+      --req_inflight;
+    }
+    auto req_stat = get_req_stat(stream_id);
+    if (!req_stat) {
+      return;
+    }
 
-  auto req_stat = get_req_stat(stream_id);
-  if (!req_stat) {
-    return;
-  }
+    req_stat->stream_close_time = std::chrono::steady_clock::now();
+    if (success) {
+      req_stat->completed = true;
+      ++worker->stats.req_success;
+      ++cstat.req_success;
 
-  req_stat->stream_close_time = std::chrono::steady_clock::now();
-  if (success) {
-    req_stat->completed = true;
-    ++worker->stats.req_success;
-    ++cstat.req_success;
+      if (streams[stream_id].status_success == 1) {
+        ++worker->stats.req_status_success;
+      } else {
+        ++worker->stats.req_failed;
+      }
 
-    if (streams[stream_id].status_success == 1) {
-      ++worker->stats.req_status_success;
+      worker->sample_req_stat(req_stat);
+
+      // Count up in successful cases only
+      ++worker->request_times_smp.n;
     } else {
       ++worker->stats.req_failed;
+      ++worker->stats.req_error;
     }
-
-    if (sampling_should_pick(worker->request_times_smp)) {
-      sampling_advance_point(worker->request_times_smp);
-      worker->sample_req_stat(req_stat);
-    }
-
-    // Count up in successful cases only
-    ++worker->request_times_smp.n;
-  } else {
-    ++worker->stats.req_failed;
-    ++worker->stats.req_error;
+    ++worker->stats.req_done;
+    ++req_done;
   }
 
-  ++worker->stats.req_done;
-
   worker->report_progress();
   streams.erase(stream_id);
   if (req_left == 0 && req_inflight == 0) {
@@ -747,10 +830,14 @@ void Client::on_stream_close(int32_t stream_id, bool success, bool final) {
     return;
   }
 
-  if (!config.timing_script && !final && req_left > 0 &&
-      submit_request() != 0) {
-    process_request_failure();
-    return;
+  if (!final && req_left > 0) {
+    if (config.timing_script) {
+      if (!ev_is_active(&request_timeout_watcher)) {
+        ev_feed_event(worker->loop, &request_timeout_watcher, EV_TIMER);
+      }
+    } else if (submit_request() != 0) {
+      process_request_failure();
+    }
   }
 }
 
@@ -784,14 +871,6 @@ int Client::connection_made() {
       } else if (util::streq(NGHTTP2_H1_1, proto)) {
         session = make_unique<Http1Session>(this);
       }
-#ifdef HAVE_SPDYLAY
-      else {
-        auto spdy_version = spdylay_npn_get_version(next_proto, next_proto_len);
-        if (spdy_version) {
-          session = make_unique<SpdySession>(this, spdy_version);
-        }
-      }
-#endif // HAVE_SPDYLAY
 
       // Just assign next_proto to selected_proto anyway to show the
       // negotiation result.
@@ -836,20 +915,6 @@ int Client::connection_made() {
       session = make_unique<Http1Session>(this);
       selected_proto = NGHTTP2_H1_1.str();
       break;
-#ifdef HAVE_SPDYLAY
-    case Config::PROTO_SPDY2:
-      session = make_unique<SpdySession>(this, SPDYLAY_PROTO_SPDY2);
-      selected_proto = "spdy/2";
-      break;
-    case Config::PROTO_SPDY3:
-      session = make_unique<SpdySession>(this, SPDYLAY_PROTO_SPDY3);
-      selected_proto = "spdy/3";
-      break;
-    case Config::PROTO_SPDY3_1:
-      session = make_unique<SpdySession>(this, SPDYLAY_PROTO_SPDY3_1);
-      selected_proto = "spdy/3.1";
-      break;
-#endif // HAVE_SPDYLAY
     default:
       // unreachable
       assert(0);
@@ -865,7 +930,9 @@ int Client::connection_made() {
   record_connect_time();
 
   if (!config.timing_script) {
-    auto nreq = std::min(req_left, session->max_concurrent_streams());
+    auto nreq = config.is_timing_based_mode()
+                    ? std::max(req_left, session->max_concurrent_streams())
+                    : std::min(req_left, session->max_concurrent_streams());
     for (; nreq > 0; --nreq) {
       if (submit_request() != 0) {
         process_request_failure();
@@ -906,7 +973,9 @@ int Client::on_read(const uint8_t *data, size_t len) {
   if (rv != 0) {
     return -1;
   }
-  worker->stats.bytes_total += len;
+  if (worker->current_phase == Phase::MAIN_DURATION) {
+    worker->stats.bytes_total += len;
+  }
   signal_write();
   return 0;
 }
@@ -1176,37 +1245,78 @@ Worker::Worker(uint32_t id, SSL_CTX *ssl_ctx, size_t req_todo, size_t nclients,
       rate(rate),
       max_samples(max_samples),
       next_client_id(0) {
-  if (!config->is_rate_mode()) {
+  if (!config->is_rate_mode() && !config->is_timing_based_mode()) {
     progress_interval = std::max(static_cast<size_t>(1), req_todo / 10);
   } else {
     progress_interval = std::max(static_cast<size_t>(1), nclients / 10);
   }
 
+  // Below timeout is not needed in case of timing-based benchmarking
   // create timer that will go off every rate_period
   ev_timer_init(&timeout_watcher, rate_period_timeout_w_cb, 0.,
                 config->rate_period);
   timeout_watcher.data = this;
 
-  stats.req_stats.reserve(std::min(req_todo, max_samples));
-  stats.client_stats.reserve(std::min(nclients, max_samples));
+  if (config->is_timing_based_mode()) {
+    stats.req_stats.reserve(std::max(req_todo, max_samples));
+    stats.client_stats.reserve(std::max(nclients, max_samples));
+  } else {
+    stats.req_stats.reserve(std::min(req_todo, max_samples));
+    stats.client_stats.reserve(std::min(nclients, max_samples));
+  }
+
+  sampling_init(request_times_smp, max_samples);
+  sampling_init(client_smp, max_samples);
+
+  ev_timer_init(&duration_watcher, duration_timeout_cb, config->duration, 0.);
+  duration_watcher.data = this;
 
-  sampling_init(request_times_smp, req_todo, max_samples);
-  sampling_init(client_smp, nclients, max_samples);
+  ev_timer_init(&warmup_watcher, warmup_timeout_cb, config->warm_up_time, 0.);
+  warmup_watcher.data = this;
+
+  if (config->is_timing_based_mode()) {
+    current_phase = Phase::INITIAL_IDLE;
+  } else {
+    current_phase = Phase::MAIN_DURATION;
+  }
 }
 
 Worker::~Worker() {
   ev_timer_stop(loop, &timeout_watcher);
+  ev_timer_stop(loop, &duration_watcher);
+  ev_timer_stop(loop, &warmup_watcher);
   ev_loop_destroy(loop);
 }
 
+void Worker::stop_all_clients() {
+  for (auto client : clients) {
+    if (client && client->session) {
+      client->terminate_session();
+    }
+  }
+}
+
+void Worker::free_client(Client *deleted_client) {
+  for (auto &client : clients) {
+    if (client == deleted_client) {
+      client->req_todo = client->req_done;
+      stats.req_todo += client->req_todo;
+      auto index = &client - &clients[0];
+      clients[index] = NULL;
+      return;
+    }
+  }
+}
+
 void Worker::run() {
-  if (!config->is_rate_mode()) {
+  if (!config->is_rate_mode() && !config->is_timing_based_mode()) {
     for (size_t i = 0; i < nclients; ++i) {
       auto req_todo = nreqs_per_client;
       if (nreqs_rem > 0) {
         ++req_todo;
         --nreqs_rem;
       }
+
       auto client = make_unique<Client>(next_client_id++, this, req_todo);
       if (client->connect() != 0) {
         std::cerr << "client could not connect to host" << std::endl;
@@ -1215,27 +1325,45 @@ void Worker::run() {
         client.release();
       }
     }
-  } else {
+  } else if (config->is_rate_mode()) {
     ev_timer_again(loop, &timeout_watcher);
 
     // call callback so that we don't waste the first rate_period
     rate_period_timeout_w_cb(loop, &timeout_watcher, 0);
+  } else {
+    // call the callback to start for one single time
+    rate_period_timeout_w_cb(loop, &timeout_watcher, 0);
   }
   ev_run(loop, 0);
 }
 
+namespace {
+template <typename Stats, typename Stat>
+void sample(Sampling &smp, Stats &stats, Stat *s) {
+  ++smp.n;
+  if (stats.size() < smp.max_samples) {
+    stats.push_back(*s);
+    return;
+  }
+  auto d = std::uniform_int_distribution<unsigned long>(0, smp.n - 1);
+  auto i = d(gen);
+  if (i < smp.max_samples) {
+    stats[i] = *s;
+  }
+}
+} // namespace
+
 void Worker::sample_req_stat(RequestStat *req_stat) {
-  stats.req_stats.push_back(*req_stat);
-  assert(stats.req_stats.size() <= max_samples);
+  sample(request_times_smp, stats.req_stats, req_stat);
 }
 
 void Worker::sample_client_stat(ClientStat *cstat) {
-  stats.client_stats.push_back(*cstat);
-  assert(stats.client_stats.size() <= max_samples);
+  sample(client_smp, stats.client_stats, cstat);
 }
 
 void Worker::report_progress() {
-  if (id != 0 || config->is_rate_mode() || stats.req_done % progress_interval) {
+  if (id != 0 || config->is_rate_mode() || stats.req_done % progress_interval ||
+      config->is_timing_based_mode()) {
     return;
   }
 
@@ -1313,14 +1441,10 @@ process_time_stats(const std::vector<std::unique_ptr<Worker>> &workers) {
   size_t nclient_times = 0;
   for (const auto &w : workers) {
     nrequest_times += w->stats.req_stats.size();
-    if (w->request_times_smp.interval != 0.) {
-      request_times_sampling = true;
-    }
+    request_times_sampling = w->request_times_smp.n > w->stats.req_stats.size();
 
     nclient_times += w->stats.client_stats.size();
-    if (w->client_smp.interval != 0.) {
-      client_times_sampling = true;
-    }
+    client_times_sampling = w->client_smp.n > w->stats.client_stats.size();
   }
 
   std::vector<double> request_times;
@@ -1581,12 +1705,27 @@ std::unique_ptr<Worker> create_worker(uint32_t id, SSL_CTX *ssl_ctx,
                 << util::duration_str(config.rate_period) << " ";
   }
 
-  std::cout << "spawning thread #" << id << ": " << nclients
-            << " total client(s). " << rate_report.str() << nreqs
-            << " total requests" << std::endl;
+  if (config.is_timing_based_mode()) {
+    std::cout << "spawning thread #" << id << ": " << nclients
+              << " total client(s). Timing-based test with "
+              << config.warm_up_time << "s of warm-up time and "
+              << config.duration << "s of main duration for measurements."
+              << std::endl;
+  } else {
+    std::cout << "spawning thread #" << id << ": " << nclients
+              << " total client(s). " << rate_report.str() << nreqs
+              << " total requests" << std::endl;
+  }
 
-  return make_unique<Worker>(id, ssl_ctx, nreqs, nclients, rate, max_samples,
-                             &config);
+  if (config.is_rate_mode()) {
+    return make_unique<Worker>(id, ssl_ctx, nreqs, nclients, rate, max_samples,
+                               &config);
+  } else {
+    // Here rate is same as client because the rate_timeout callback
+    // will be called only once
+    return make_unique<Worker>(id, ssl_ctx, nreqs, nclients, nclients,
+                               max_samples, &config);
+  }
 }
 } // namespace
 
@@ -1620,17 +1759,13 @@ void print_version(std::ostream &out) {
 namespace {
 void print_usage(std::ostream &out) {
   out << R"(Usage: h2load [OPTIONS]... [URI]...
-benchmarking tool for HTTP/2 and SPDY server)"
+benchmarking tool for HTTP/2 server)"
       << std::endl;
 }
 } // namespace
 
 namespace {
-constexpr char DEFAULT_NPN_LIST[] = "h2,h2-16,h2-14"
-#ifdef HAVE_SPDYLAY
-                                    ",spdy/3.1,spdy/3,spdy/2"
-#endif // HAVE_SPDYLAY
-                                    ",http/1.1";
+constexpr char DEFAULT_NPN_LIST[] = "h2,h2-16,h2-14,http/1.1";
 } // namespace
 
 namespace {
@@ -1652,18 +1787,18 @@ Options:
               Number of  requests across all  clients.  If it  is used
               with --timing-script-file option,  this option specifies
               the number of requests  each client performs rather than
-              the number of requests across all clients.
+              the number of requests  across all clients.  This option
+              is ignored if timing-based  benchmarking is enabled (see
+              --duration option).
               Default: )"
       << config.nreqs << R"(
   -c, --clients=<N>
               Number  of concurrent  clients.   With  -r option,  this
               specifies the maximum number of connections to be made.
-              Default: )"
-      << config.nclients << R"(
+              Default: )" << config.nclients << R"(
   -t, --threads=<N>
               Number of native threads.
-              Default: )"
-      << config.nthreads << R"(
+              Default: )" << config.nthreads << R"(
   -i, --input-file=<PATH>
               Path of a file with multiple URIs are separated by EOLs.
               This option will disable URIs getting from command-line.
@@ -1681,16 +1816,12 @@ Options:
               Default: 1
   -w, --window-bits=<N>
               Sets the stream level initial window size to (2**<N>)-1.
-              For SPDY, 2**<N> is used instead.
               Default: )"
       << config.window_bits << R"(
   -W, --connection-window-bits=<N>
               Sets  the  connection  level   initial  window  size  to
-              (2**<N>)-1.  For SPDY, if <N>  is strictly less than 16,
-              this option  is ignored.   Otherwise 2**<N> is  used for
-              SPDY.
-              Default: )"
-      << config.connection_window_bits << R"(
+              (2**<N>)-1.
+              Default: )" << config.connection_window_bits << R"(
   -H, --header=<HEADER>
               Add/Override a header to the requests.
   --ciphers=<SUITE>
@@ -1700,18 +1831,9 @@ Options:
       << config.ciphers << R"(
   -p, --no-tls-proto=<PROTOID>
               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, )";
-#else  // !HAVE_SPDYLAY
-  out << R"(
-              Available protocols: )";
-#endif // !HAVE_SPDYLAY
-  out << NGHTTP2_CLEARTEXT_PROTO_VERSION_ID << R"( and
-              )"
-      << NGHTTP2_H1_1 << R"(
+              accessing http URI without SSL/TLS.
+              Available protocols: )" << NGHTTP2_CLEARTEXT_PROTO_VERSION_ID
+      << R"( and )" << NGHTTP2_H1_1 << R"(
               Default: )"
       << NGHTTP2_CLEARTEXT_PROTO_VERSION_ID << R"(
   -d, --data=<PATH>
@@ -1737,6 +1859,13 @@ Options:
               length of the period in time.  This option is ignored if
               the rate option is not used.  The default value for this
               option is 1s.
+  -D, --duration=<N>
+              Specifies the main duration for the measurements in case
+              of timing-based benchmarking.
+  --warm-up-time=<DURATION>
+              Specifies the  time  period  before  starting the actual
+              measurements, in  case  of  timing-based benchmarking.
+              Needs to provided along with -D option.
   -T, --connection-active-timeout=<DURATION>
               Specifies  the maximum  time that  h2load is  willing to
               keep a  connection open,  regardless of the  activity on
@@ -1787,8 +1916,7 @@ Options:
               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"(
+              Default: )" << DEFAULT_NPN_LIST << R"(
   --h1        Short        hand         for        --npn-list=http/1.1
               --no-tls-proto=http/1.1,    which   effectively    force
               http/1.1 for both http and https URI.
@@ -1816,16 +1944,15 @@ Options:
   The <DURATION> 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;
+  is omitted, a second is used as unit.)" << std::endl;
 }
 } // namespace
 
 int main(int argc, char **argv) {
-  ssl::libssl_init();
+  tls::libssl_init();
 
 #ifndef NOTHREADS
-  ssl::LibsslGlobalLock lock;
+  tls::LibsslGlobalLock lock;
 #endif // NOTHREADS
 
   std::string datafile;
@@ -1850,6 +1977,7 @@ int main(int argc, char **argv) {
         {"rate", required_argument, nullptr, 'r'},
         {"connection-active-timeout", required_argument, nullptr, 'T'},
         {"connection-inactivity-timeout", required_argument, nullptr, 'N'},
+        {"duration", required_argument, nullptr, 'D'},
         {"timing-script-file", required_argument, &flag, 3},
         {"base-uri", required_argument, nullptr, 'B'},
         {"npn-list", required_argument, &flag, 4},
@@ -1857,10 +1985,12 @@ int main(int argc, char **argv) {
         {"h1", no_argument, &flag, 6},
         {"header-table-size", required_argument, &flag, 7},
         {"encoder-header-table-size", required_argument, &flag, 8},
+        {"warm-up-time", required_argument, &flag, 9},
         {nullptr, 0, nullptr, 0}};
     int option_index = 0;
-    auto c = getopt_long(argc, argv, "hvW:c:d:m:n:p:t:w:H:i:r:T:N:B:",
-                         long_options, &option_index);
+    auto c = getopt_long(argc, argv,
+                         "hvW:c:d:m:n:p:t:w:H:i:r:T:N:D:B:", long_options,
+                         &option_index);
     if (c == -1) {
       break;
     }
@@ -1941,14 +2071,6 @@ int main(int argc, char **argv) {
         config.no_tls_proto = Config::PROTO_HTTP2;
       } else if (util::strieq(NGHTTP2_H1_1, proto)) {
         config.no_tls_proto = Config::PROTO_HTTP1_1;
-#ifdef HAVE_SPDYLAY
-      } else if (util::strieq_l("spdy/2", proto)) {
-        config.no_tls_proto = Config::PROTO_SPDY2;
-      } else if (util::strieq_l("spdy/3", proto)) {
-        config.no_tls_proto = Config::PROTO_SPDY3;
-      } else if (util::strieq_l("spdy/3.1", proto)) {
-        config.no_tls_proto = Config::PROTO_SPDY3_1;
-#endif // HAVE_SPDYLAY
       } else {
         std::cerr << "-p: unsupported protocol " << proto << std::endl;
         exit(EXIT_FAILURE);
@@ -2015,6 +2137,14 @@ int main(int argc, char **argv) {
       config.base_uri = arg.str();
       break;
     }
+    case 'D':
+      config.duration = strtoul(optarg, nullptr, 10);
+      if (config.duration == 0) {
+        std::cerr << "-D: the main duration for timing-based benchmarking "
+                  << "must be positive." << std::endl;
+        exit(EXIT_FAILURE);
+      }
+      break;
     case 'v':
       config.verbose = true;
       break;
@@ -2071,6 +2201,14 @@ int main(int argc, char **argv) {
           exit(EXIT_FAILURE);
         }
         break;
+      case 9:
+        // --warm-up-time
+        config.warm_up_time = util::parse_duration_with_unit(optarg);
+        if (!std::isfinite(config.warm_up_time)) {
+          std::cerr << "--warm-up-time: value error " << optarg << std::endl;
+          exit(EXIT_FAILURE);
+        }
+        break;
       }
       break;
     default:
@@ -2156,8 +2294,9 @@ int main(int argc, char **argv) {
     exit(EXIT_FAILURE);
   }
 
-  if (config.nreqs == 0) {
-    std::cerr << "-n: the number of requests must be strictly greater than 0."
+  if (config.nreqs == 0 && !config.is_timing_based_mode()) {
+    std::cerr << "-n: the number of requests must be strictly greater than 0 "
+                 "if timing-based test is not being run."
               << std::endl;
     exit(EXIT_FAILURE);
   }
@@ -2181,7 +2320,8 @@ int main(int argc, char **argv) {
 
   // With timing script, we don't distribute config.nreqs to each
   // client or thread.
-  if (!config.timing_script && config.nreqs < config.nclients) {
+  if (!config.timing_script && config.nreqs < config.nclients &&
+      !config.is_timing_based_mode()) {
     std::cerr << "-n, -c: the number of requests must be greater than or "
               << "equal to the clients." << std::endl;
     exit(EXIT_FAILURE);
@@ -2189,11 +2329,14 @@ int main(int argc, char **argv) {
 
   if (config.nclients < config.nthreads) {
     std::cerr << "-c, -t: the number of clients must be greater than or equal "
-                 "to the number of threads."
-              << std::endl;
+              << "to the number of threads." << std::endl;
     exit(EXIT_FAILURE);
   }
 
+  if (config.is_timing_based_mode()) {
+    config.nreqs = 0;
+  }
+
   if (config.is_rate_mode()) {
     if (config.rate < config.nthreads) {
       std::cerr << "-r, -t: the connection rate must be greater than or equal "
@@ -2242,9 +2385,9 @@ int main(int argc, char **argv) {
   SSL_CTX_set_mode(ssl_ctx, SSL_MODE_AUTO_RETRY);
   SSL_CTX_set_mode(ssl_ctx, SSL_MODE_RELEASE_BUFFERS);
 
-  if (nghttp2::ssl::ssl_ctx_set_proto_versions(
-          ssl_ctx, nghttp2::ssl::NGHTTP2_TLS_MIN_VERSION,
-          nghttp2::ssl::NGHTTP2_TLS_MAX_VERSION) != 0) {
+  if (nghttp2::tls::ssl_ctx_set_proto_versions(
+          ssl_ctx, nghttp2::tls::NGHTTP2_TLS_MIN_VERSION,
+          nghttp2::tls::NGHTTP2_TLS_MAX_VERSION) != 0) {
     std::cerr << "Could not set TLS versions" << std::endl;
     exit(EXIT_FAILURE);
   }
@@ -2312,7 +2455,6 @@ int main(int argc, char **argv) {
 
   config.h1reqs.reserve(reqlines.size());
   config.nva.reserve(reqlines.size());
-  config.nv.reserve(reqlines.size());
 
   for (auto &req : reqlines) {
     // For HTTP/1.1
@@ -2362,35 +2504,6 @@ int main(int argc, char **argv) {
     }
 
     config.nva.push_back(std::move(nva));
-
-    // For spdylay
-    std::vector<const char *> cva;
-    // 3 for :path, :version, and possible content-length, 1 for
-    // terminal nullptr
-    cva.reserve(2 * (3 + shared_nva.size()) + 1);
-
-    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");
-
-    if (!content_length_str.empty()) {
-      cva.push_back("content-length");
-      cva.push_back(content_length_str.c_str());
-    }
-
-    cva.push_back(nullptr);
-
-    config.nv.push_back(std::move(cva));
   }
 
   // Don't DOS our server!
@@ -2527,7 +2640,7 @@ int main(int argc, char **argv) {
   // 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_todo - stats.req_status_success - stats.req_failed);
   stats.req_failed += req_not_issued;
   stats.req_error += req_not_issued;
 
@@ -2538,10 +2651,17 @@ int main(int argc, char **argv) {
   double rps = 0;
   int64_t bps = 0;
   if (duration.count() > 0) {
-    auto secd = std::chrono::duration_cast<
-        std::chrono::duration<double, std::chrono::seconds::period>>(duration);
-    rps = stats.req_success / secd.count();
-    bps = stats.bytes_total / secd.count();
+    if (config.is_timing_based_mode()) {
+      // we only want to consider the main duration if warm-up is given
+      rps = stats.req_success / config.duration;
+      bps = stats.bytes_total / config.duration;
+    } else {
+      auto secd = std::chrono::duration_cast<
+          std::chrono::duration<double, std::chrono::seconds::period>>(
+          duration);
+      rps = stats.req_success / secd.count();
+      bps = stats.bytes_total / secd.count();
+    }
   }
 
   double header_space_savings = 0.;
@@ -2554,14 +2674,14 @@ int main(int argc, char **argv) {
 finished in )"
             << util::format_duration(duration) << ", " << rps << " req/s, "
             << util::utos_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
-            << " errored, " << stats.req_timedout << R"( timeout
-status codes: )"
-            << stats.status[2] << " 2xx, " << stats.status[3] << " 3xx, "
-            << stats.status[4] << " 4xx, " << stats.status[5] << R"( 5xx
+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 << " errored, "
+            << stats.req_timedout << R"( timeout
+status codes: )" << stats.status[2] << " 2xx, "
+            << stats.status[3] << " 3xx, " << stats.status[4] << " 4xx, "
+            << stats.status[5] << R"( 5xx
 traffic: )" << util::utos_funit(stats.bytes_total)
             << "B (" << stats.bytes_total << ") total, "
             << util::utos_funit(stats.bytes_head) << "B (" << stats.bytes_head
@@ -2569,12 +2689,12 @@ traffic: )" << util::utos_funit(stats.bytes_total)
             << "%), " << util::utos_funit(stats.bytes_body) << "B ("
             << stats.bytes_body << R"() 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) << "%"
+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)
index db50472..4f6739d 100644 (file)
@@ -64,7 +64,6 @@ struct Worker;
 
 struct Config {
   std::vector<std::vector<nghttp2_nv>> nva;
-  std::vector<std::vector<const char *>> nv;
   std::vector<std::string> h1reqs;
   std::vector<ev_tstamp> timings;
   nghttp2::Headers custom_headers;
@@ -85,17 +84,15 @@ struct Config {
   // rate at which connections should be made
   size_t rate;
   ev_tstamp rate_period;
+  // amount of time for main measurements in timing-based test
+  ev_tstamp duration;
+  // amount of time to wait before starting measurements in timing-based test
+  ev_tstamp warm_up_time;
   // amount of time to wait for activity on a given connection
   ev_tstamp conn_active_timeout;
   // amount of time to wait after the last request is made on a connection
   ev_tstamp conn_inactivity_timeout;
-  enum {
-    PROTO_HTTP2,
-    PROTO_SPDY2,
-    PROTO_SPDY3,
-    PROTO_SPDY3_1,
-    PROTO_HTTP1_1
-  } no_tls_proto;
+  enum { PROTO_HTTP2, PROTO_HTTP1_1 } no_tls_proto;
   uint32_t header_table_size;
   uint32_t encoder_header_table_size;
   // file descriptor for upload data
@@ -118,6 +115,7 @@ struct Config {
   ~Config();
 
   bool is_rate_mode() const;
+  bool is_timing_based_mode() const;
   bool has_base_uri() const;
 };
 
@@ -139,7 +137,7 @@ struct ClientStat {
   // time client end (i.e., client somehow processed all requests it
   // is responsible for, and disconnected)
   std::chrono::steady_clock::time_point client_end_time;
-  // The number of requests completed successfull, but not necessarily
+  // The number of requests completed successful, but not necessarily
   // means successful HTTP status code.
   size_t req_success;
 
@@ -180,7 +178,7 @@ struct Stats {
   size_t req_started;
   // The number of requests finished
   size_t req_done;
-  // The number of requests completed successfull, but not necessarily
+  // The number of requests completed successful, but not necessarily
   // means successful HTTP status code.
   size_t req_success;
   // The number of requests marked as success.  HTTP status code is
@@ -215,15 +213,21 @@ struct Stats {
 
 enum ClientState { CLIENT_IDLE, CLIENT_CONNECTED };
 
+// This type tells whether the client is in warmup phase or not or is over
+enum class Phase {
+  INITIAL_IDLE,  // Initial idle state before warm-up phase
+  WARM_UP,       // Warm up phase when no measurements are done
+  MAIN_DURATION, // Main measurement phase; if timing-based
+                 // test is not run, this is the default phase
+  DURATION_OVER  // This phase occurs after the measurements are over
+};
+
 struct Client;
 
-// We use systematic sampling method
+// We use reservoir sampling method
 struct Sampling {
-  // sampling interval
-  double interval;
-  // cumulative value of interval, and the next point is the integer
-  // rounded up from this value.
-  double point;
+  // maximum number of samples
+  size_t max_samples;
   // number of samples seen, including discarded samples.
   size_t n;
 };
@@ -253,6 +257,15 @@ struct Worker {
   ev_timer timeout_watcher;
   // The next client ID this worker assigns
   uint32_t next_client_id;
+  // Keeps track of the current phase (for timing-based experiment) for the
+  // worker
+  Phase current_phase;
+  // We need to keep track of the clients in order to stop them when needed
+  std::vector<Client *> clients;
+  // This is only active when there is not a bounded number of requests
+  // specified
+  ev_timer duration_watcher;
+  ev_timer warmup_watcher;
 
   Worker(uint32_t id, SSL_CTX *ssl_ctx, size_t nreq_todo, size_t nclients,
          size_t rate, size_t max_samples, Config *config);
@@ -263,6 +276,10 @@ struct Worker {
   void sample_client_stat(ClientStat *cstat);
   void report_progress();
   void report_rate_progress();
+  // This function calls the destructors of all the clients.
+  void stop_all_clients();
+  // This function frees a client from the list of clients for this Worker.
+  void free_client(Client *);
 };
 
 struct Stream {
index f7f63f1..28ad456 100644 (file)
@@ -26,6 +26,7 @@
 
 #include <cassert>
 #include <cerrno>
+#include <iostream>
 
 #include "h2load.h"
 #include "util.h"
@@ -52,6 +53,15 @@ int on_header_callback(nghttp2_session *session, const nghttp2_frame *frame,
   }
   client->on_header(frame->hd.stream_id, name, namelen, value, valuelen);
   client->worker->stats.bytes_head_decomp += namelen + valuelen;
+
+  if (client->worker->config->verbose) {
+    std::cout << "[stream_id=" << frame->hd.stream_id << "] ";
+    std::cout.write(reinterpret_cast<const char *>(name), namelen);
+    std::cout << ": ";
+    std::cout.write(reinterpret_cast<const char *>(value), valuelen);
+    std::cout << "\n";
+  }
+
   return 0;
 }
 } // namespace
@@ -180,6 +190,9 @@ ssize_t send_callback(nghttp2_session *session, const uint8_t *data,
 void Http2Session::on_connect() {
   int rv;
 
+  // This is required with --disable-assert.
+  (void)rv;
+
   nghttp2_session_callbacks *callbacks;
 
   nghttp2_session_callbacks_new(&callbacks);
diff --git a/src/h2load_spdy_session.cc b/src/h2load_spdy_session.cc
deleted file mode 100644 (file)
index 370aecc..0000000
+++ /dev/null
@@ -1,289 +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 <cassert>
-#include <cerrno>
-
-#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<Client *>(user_data);
-  if (type != SPDYLAY_SYN_STREAM) {
-    return;
-  }
-  client->on_request(frame->syn_stream.stream_id);
-  auto req_stat = client->get_req_stat(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<Client *>(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);
-    auto namelen = strlen(name);
-    auto valuelen = strlen(value);
-    client->on_header(frame->syn_reply.stream_id,
-                      reinterpret_cast<const uint8_t *>(name), namelen,
-                      reinterpret_cast<const uint8_t *>(value), valuelen);
-    client->worker->stats.bytes_head_decomp += namelen + valuelen;
-  }
-
-  // Strictly speaking, we have to subtract 2 (unused field) if SPDY
-  // version is 2.  But it is already deprecated, and we don't do
-  // extra work for it.
-  client->worker->stats.bytes_head += frame->syn_reply.hd.length - 4;
-
-  if (frame->syn_stream.hd.flags & SPDYLAY_CTRL_FLAG_FIN) {
-    client->record_ttfb();
-  }
-}
-} // 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<Client *>(user_data);
-
-  client->record_ttfb();
-  client->worker->stats.bytes_body += len;
-
-  auto spdy_session = static_cast<SpdySession *>(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<Client *>(user_data);
-  client->on_stream_close(stream_id, status_code == SPDYLAY_OK);
-}
-} // 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<Client *>(user_data);
-  auto &wb = client->wb;
-
-  if (wb.rleft() >= BACKOFF_WRITE_BUFFER_THRES) {
-    return SPDYLAY_ERR_WOULDBLOCK;
-  }
-
-  return wb.append(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<Client *>(user_data);
-  auto config = client->worker->config;
-  auto req_stat = client->get_req_stat(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();
-}
-
-int SpdySession::submit_request() {
-  int rv;
-  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};
-
-  rv = spdylay_submit_request(session_, 0, nv.data(),
-                              config->data_fd == -1 ? nullptr : &prd, nullptr);
-
-  if (rv != 0) {
-    return -1;
-  }
-
-  return 0;
-}
-
-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<size_t>(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);
-  }
-}
-
-size_t SpdySession::max_concurrent_streams() {
-  return (size_t)client_->worker->config->max_concurrent_streams;
-}
-
-} // namespace h2load
diff --git a/src/h2load_spdy_session.h b/src/h2load_spdy_session.h
deleted file mode 100644 (file)
index c7721fa..0000000
+++ /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.
- */
-#ifndef H2LOAD_SPDY_SESSION_H
-#define H2LOAD_SPDY_SESSION_H
-
-#include "h2load_session.h"
-
-#include <spdylay/spdylay.h>
-
-#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 int submit_request();
-  virtual int on_read(const uint8_t *data, size_t len);
-  virtual int on_write();
-  virtual void terminate();
-  virtual size_t max_concurrent_streams();
-  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
index 04dfb7a..6db5974 100644 (file)
@@ -36,6 +36,8 @@ StringRef get_reason_phrase(unsigned int status_code) {
     return StringRef::from_lit("Continue");
   case 101:
     return StringRef::from_lit("Switching Protocols");
+  case 103:
+    return StringRef::from_lit("Early Hints");
   case 200:
     return StringRef::from_lit("OK");
   case 201:
@@ -140,6 +142,8 @@ StringRef stringify_status(BlockAllocator &balloc, unsigned int status_code) {
     return StringRef::from_lit("100");
   case 101:
     return StringRef::from_lit("101");
+  case 103:
+    return StringRef::from_lit("103");
   case 200:
     return StringRef::from_lit("200");
   case 201:
@@ -358,15 +362,21 @@ nghttp2_nv make_nv_nocopy(const StringRef &name, const StringRef &value,
 
 namespace {
 void copy_headers_to_nva_internal(std::vector<nghttp2_nv> &nva,
-                                  const HeaderRefs &headers, uint8_t nv_flags) {
-  for (auto &kv : headers) {
-    if (kv.name.empty() || kv.name[0] == ':') {
+                                  const HeaderRefs &headers, uint8_t nv_flags,
+                                  uint32_t flags) {
+  auto it_forwarded = std::end(headers);
+  auto it_xff = std::end(headers);
+  auto it_xfp = std::end(headers);
+  auto it_via = std::end(headers);
+
+  for (auto it = std::begin(headers); it != std::end(headers); ++it) {
+    auto kv = &(*it);
+    if (kv->name.empty() || kv->name[0] == ':') {
       continue;
     }
-    switch (kv.token) {
+    switch (kv->token) {
     case HD_COOKIE:
     case HD_CONNECTION:
-    case HD_FORWARDED:
     case HD_HOST:
     case HD_HTTP2_SETTINGS:
     case HD_KEEP_ALIVE:
@@ -375,51 +385,157 @@ void copy_headers_to_nva_internal(std::vector<nghttp2_nv> &nva,
     case HD_TE:
     case HD_TRANSFER_ENCODING:
     case HD_UPGRADE:
-    case HD_VIA:
+      continue;
+    case HD_FORWARDED:
+      if (flags & HDOP_STRIP_FORWARDED) {
+        continue;
+      }
+
+      if (it_forwarded == std::end(headers)) {
+        it_forwarded = it;
+        continue;
+      }
+
+      kv = &(*it_forwarded);
+      it_forwarded = it;
+      break;
     case HD_X_FORWARDED_FOR:
+      if (flags & HDOP_STRIP_X_FORWARDED_FOR) {
+        continue;
+      }
+
+      if (it_xff == std::end(headers)) {
+        it_xff = it;
+        continue;
+      }
+
+      kv = &(*it_xff);
+      it_xff = it;
+      break;
     case HD_X_FORWARDED_PROTO:
-      continue;
+      if (flags & HDOP_STRIP_X_FORWARDED_PROTO) {
+        continue;
+      }
+
+      if (it_xfp == std::end(headers)) {
+        it_xfp = it;
+        continue;
+      }
+
+      kv = &(*it_xfp);
+      it_xfp = it;
+      break;
+    case HD_VIA:
+      if (flags & HDOP_STRIP_VIA) {
+        continue;
+      }
+
+      if (it_via == std::end(headers)) {
+        it_via = it;
+        continue;
+      }
+
+      kv = &(*it_via);
+      it_via = it;
+      break;
     }
-    nva.push_back(make_nv_internal(kv.name, kv.value, kv.no_index, nv_flags));
+    nva.push_back(
+        make_nv_internal(kv->name, kv->value, kv->no_index, nv_flags));
   }
 }
 } // namespace
 
 void copy_headers_to_nva(std::vector<nghttp2_nv> &nva,
-                         const HeaderRefs &headers) {
-  copy_headers_to_nva_internal(nva, headers, NGHTTP2_NV_FLAG_NONE);
+                         const HeaderRefs &headers, uint32_t flags) {
+  copy_headers_to_nva_internal(nva, headers, NGHTTP2_NV_FLAG_NONE, flags);
 }
 
 void copy_headers_to_nva_nocopy(std::vector<nghttp2_nv> &nva,
-                                const HeaderRefs &headers) {
-  copy_headers_to_nva_internal(nva, headers, NGHTTP2_NV_FLAG_NO_COPY_NAME |
-                                                 NGHTTP2_NV_FLAG_NO_COPY_VALUE);
+                                const HeaderRefs &headers, uint32_t flags) {
+  copy_headers_to_nva_internal(
+      nva, headers,
+      NGHTTP2_NV_FLAG_NO_COPY_NAME | NGHTTP2_NV_FLAG_NO_COPY_VALUE, flags);
 }
 
 void build_http1_headers_from_headers(DefaultMemchunks *buf,
-                                      const HeaderRefs &headers) {
-  for (auto &kv : headers) {
-    if (kv.name.empty() || kv.name[0] == ':') {
+                                      const HeaderRefs &headers,
+                                      uint32_t flags) {
+  auto it_forwarded = std::end(headers);
+  auto it_xff = std::end(headers);
+  auto it_xfp = std::end(headers);
+  auto it_via = std::end(headers);
+
+  for (auto it = std::begin(headers); it != std::end(headers); ++it) {
+    auto kv = &(*it);
+    if (kv->name.empty() || kv->name[0] == ':') {
       continue;
     }
-    switch (kv.token) {
+    switch (kv->token) {
     case HD_CONNECTION:
     case HD_COOKIE:
-    case HD_FORWARDED:
     case HD_HOST:
     case HD_HTTP2_SETTINGS:
     case HD_KEEP_ALIVE:
     case HD_PROXY_CONNECTION:
     case HD_SERVER:
     case HD_UPGRADE:
-    case HD_VIA:
+      continue;
+    case HD_FORWARDED:
+      if (flags & HDOP_STRIP_FORWARDED) {
+        continue;
+      }
+
+      if (it_forwarded == std::end(headers)) {
+        it_forwarded = it;
+        continue;
+      }
+
+      kv = &(*it_forwarded);
+      it_forwarded = it;
+      break;
     case HD_X_FORWARDED_FOR:
+      if (flags & HDOP_STRIP_X_FORWARDED_FOR) {
+        continue;
+      }
+
+      if (it_xff == std::end(headers)) {
+        it_xff = it;
+        continue;
+      }
+
+      kv = &(*it_xff);
+      it_xff = it;
+      break;
     case HD_X_FORWARDED_PROTO:
-      continue;
+      if (flags & HDOP_STRIP_X_FORWARDED_PROTO) {
+        continue;
+      }
+
+      if (it_xfp == std::end(headers)) {
+        it_xfp = it;
+        continue;
+      }
+
+      kv = &(*it_xfp);
+      it_xfp = it;
+      break;
+    case HD_VIA:
+      if (flags & HDOP_STRIP_VIA) {
+        continue;
+      }
+
+      if (it_via == std::end(headers)) {
+        it_via = it;
+        continue;
+      }
+
+      kv = &(*it_via);
+      it_via = it;
+      break;
     }
-    capitalize(buf, kv.name);
+    capitalize(buf, kv->name);
     buf->append(": ");
-    buf->append(kv.value);
+    buf->append(kv->value);
     buf->append("\r\n");
   }
 }
@@ -1486,7 +1602,7 @@ template <typename InputIt> InputIt eat_file(InputIt first, InputIt last) {
   for (; p != first && *(p - 1) != '/'; --p)
     ;
   if (p == first) {
-    // this should not happend in normal case, where we expect path
+    // this should not happened in normal case, where we expect path
     // starts with '/'
     *first++ = '/';
     return first;
index 9859930..eb6a41d 100644 (file)
@@ -187,24 +187,50 @@ nghttp2_nv make_nv_ls_nocopy(const char (&name)[N], const StringRef &value) {
           NGHTTP2_NV_FLAG_NO_COPY_NAME | NGHTTP2_NV_FLAG_NO_COPY_VALUE};
 }
 
+enum HeaderBuildOp {
+  HDOP_NONE,
+  // Forwarded header fields must be stripped.  If this flag is not
+  // set, all Forwarded header fields other than last one are added.
+  HDOP_STRIP_FORWARDED = 1,
+  // X-Forwarded-For header fields must be stripped.  If this flag is
+  // not set, all X-Forwarded-For header fields other than last one
+  // are added.
+  HDOP_STRIP_X_FORWARDED_FOR = 1 << 1,
+  // X-Forwarded-Proto header fields must be stripped.  If this flag
+  // is not set, all X-Forwarded-Proto header fields other than last
+  // one are added.
+  HDOP_STRIP_X_FORWARDED_PROTO = 1 << 2,
+  // Via header fields must be stripped.  If this flag is not set, all
+  // Via header fields other than last one are added.
+  HDOP_STRIP_VIA = 1 << 3,
+  // Strip above all header fields.
+  HDOP_STRIP_ALL = HDOP_STRIP_FORWARDED | HDOP_STRIP_X_FORWARDED_FOR |
+                   HDOP_STRIP_X_FORWARDED_PROTO | HDOP_STRIP_VIA,
+};
+
 // 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.
+// which require special handling (i.e. via), are not copied.  |flags|
+// is one or more of HeaderBuildOp flags.  They tell function that
+// certain header fields should not be added.
 void copy_headers_to_nva(std::vector<nghttp2_nv> &nva,
-                         const HeaderRefs &headers);
+                         const HeaderRefs &headers, uint32_t flags);
 
 // Just like copy_headers_to_nva(), but this adds
 // NGHTTP2_NV_FLAG_NO_COPY_NAME and NGHTTP2_NV_FLAG_NO_COPY_VALUE.
 void copy_headers_to_nva_nocopy(std::vector<nghttp2_nv> &nva,
-                                const HeaderRefs &headers);
+                                const HeaderRefs &headers, uint32_t flags);
 
 // Appends HTTP/1.1 style header lines to |buf| 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.
+// |flags| is one or more of HeaderBuildOp flags.  They tell function
+// that certain header fields should not be added.
 void build_http1_headers_from_headers(DefaultMemchunks *buf,
-                                      const HeaderRefs &headers);
+                                      const HeaderRefs &headers,
+                                      uint32_t flags);
 
 // Return positive window_size_increment if WINDOW_UPDATE should be
 // sent for the stream |stream_id|. If |stream_id| == 0, this function
@@ -242,7 +268,7 @@ void erase_header(HeaderRef *hd);
 //
 // This function returns the new rewritten URI on success. If the
 // location URI is not subject to the rewrite, this function returns
-// emtpy string.
+// empty string.
 StringRef rewrite_location_uri(BlockAllocator &balloc, const StringRef &uri,
                                const http_parser_url &u,
                                const StringRef &match_host,
index d2be623..04c2611 100644 (file)
@@ -150,11 +150,33 @@ auto headers = HeaderRefs{
     {StringRef::from_lit("zulu"), StringRef::from_lit("12")}};
 } // namespace
 
+namespace {
+auto headers2 = HeaderRefs{
+    {StringRef::from_lit("x-forwarded-for"), StringRef::from_lit("xff1"), false,
+     http2::HD_X_FORWARDED_FOR},
+    {StringRef::from_lit("x-forwarded-for"), StringRef::from_lit("xff2"), false,
+     http2::HD_X_FORWARDED_FOR},
+    {StringRef::from_lit("x-forwarded-proto"), StringRef::from_lit("xfp1"),
+     false, http2::HD_X_FORWARDED_PROTO},
+    {StringRef::from_lit("x-forwarded-proto"), StringRef::from_lit("xfp2"),
+     false, http2::HD_X_FORWARDED_PROTO},
+    {StringRef::from_lit("forwarded"), StringRef::from_lit("fwd1"), false,
+     http2::HD_FORWARDED},
+    {StringRef::from_lit("forwarded"), StringRef::from_lit("fwd2"), false,
+     http2::HD_FORWARDED},
+    {StringRef::from_lit("via"), StringRef::from_lit("via1"), false,
+     http2::HD_VIA},
+    {StringRef::from_lit("via"), StringRef::from_lit("via2"), false,
+     http2::HD_VIA},
+};
+} // namespace
+
 void test_http2_copy_headers_to_nva(void) {
   auto ans = std::vector<int>{0, 1, 4, 5, 6, 7, 12};
   std::vector<nghttp2_nv> nva;
 
-  http2::copy_headers_to_nva_nocopy(nva, headers);
+  http2::copy_headers_to_nva_nocopy(nva, headers,
+                                    http2::HDOP_STRIP_X_FORWARDED_FOR);
   CU_ASSERT(7 == nva.size());
   for (size_t i = 0; i < ans.size(); ++i) {
     check_nv(headers[ans[i]], &nva[i]);
@@ -169,7 +191,7 @@ void test_http2_copy_headers_to_nva(void) {
   }
 
   nva.clear();
-  http2::copy_headers_to_nva(nva, headers);
+  http2::copy_headers_to_nva(nva, headers, http2::HDOP_STRIP_X_FORWARDED_FOR);
   CU_ASSERT(7 == nva.size());
   for (size_t i = 0; i < ans.size(); ++i) {
     check_nv(headers[ans[i]], &nva[i]);
@@ -180,12 +202,27 @@ void test_http2_copy_headers_to_nva(void) {
       CU_ASSERT(NGHTTP2_NV_FLAG_NONE == nva[i].flags);
     }
   }
+
+  nva.clear();
+
+  auto ans2 = std::vector<int>{0, 2, 4, 6};
+  http2::copy_headers_to_nva(nva, headers2, http2::HDOP_NONE);
+  CU_ASSERT(ans2.size() == nva.size());
+  for (size_t i = 0; i < ans2.size(); ++i) {
+    check_nv(headers2[ans2[i]], &nva[i]);
+  }
+
+  nva.clear();
+
+  http2::copy_headers_to_nva(nva, headers2, http2::HDOP_STRIP_ALL);
+  CU_ASSERT(nva.empty());
 }
 
 void test_http2_build_http1_headers_from_headers(void) {
   MemchunkPool pool;
   DefaultMemchunks buf(&pool);
-  http2::build_http1_headers_from_headers(&buf, headers);
+  http2::build_http1_headers_from_headers(&buf, headers,
+                                          http2::HDOP_STRIP_X_FORWARDED_FOR);
   auto hdrs = std::string(buf.head->pos, buf.head->last);
   CU_ASSERT("Alpha: 0\r\n"
             "Bravo: 1\r\n"
@@ -196,6 +233,21 @@ void test_http2_build_http1_headers_from_headers(void) {
             "Te: 8\r\n"
             "Te: 9\r\n"
             "Zulu: 12\r\n" == hdrs);
+
+  buf.reset();
+
+  http2::build_http1_headers_from_headers(&buf, headers2, http2::HDOP_NONE);
+  hdrs = std::string(buf.head->pos, buf.head->last);
+  CU_ASSERT("X-Forwarded-For: xff1\r\n"
+            "X-Forwarded-Proto: xfp1\r\n"
+            "Forwarded: fwd1\r\n"
+            "Via: via1\r\n" == hdrs);
+
+  buf.reset();
+
+  http2::build_http1_headers_from_headers(&buf, headers2,
+                                          http2::HDOP_STRIP_ALL);
+  CU_ASSERT(0 == buf.rleft());
 }
 
 void test_http2_lws(void) {
index 97383c2..0c1905c 100644 (file)
@@ -1,7 +1,7 @@
-# Makefile.in generated by automake 1.15 from Makefile.am.
+# Makefile.in generated by automake 1.15.1 from Makefile.am.
 # @configure_input@
 
-# Copyright (C) 1994-2014 Free Software Foundation, Inc.
+# Copyright (C) 1994-2017 Free Software Foundation, Inc.
 
 # This Makefile.in is free software; the Free Software Foundation
 # gives unlimited permission to copy and/or distribute it,
@@ -265,8 +265,6 @@ LIBMRUBY_CFLAGS = @LIBMRUBY_CFLAGS@
 LIBMRUBY_LIBS = @LIBMRUBY_LIBS@
 LIBOBJS = @LIBOBJS@
 LIBS = @LIBS@
-LIBSPDYLAY_CFLAGS = @LIBSPDYLAY_CFLAGS@
-LIBSPDYLAY_LIBS = @LIBSPDYLAY_LIBS@
 LIBTOOL = @LIBTOOL@
 LIBXML2_CFLAGS = @LIBXML2_CFLAGS@
 LIBXML2_LIBS = @LIBXML2_LIBS@
index c3662e2..c6c1947 100644 (file)
@@ -118,6 +118,28 @@ private:
   std::unique_ptr<request_impl> impl_;
 };
 
+// Wrapper around an nghttp2_priority_spec.
+class priority_spec {
+public:
+  // The default ctor is used only by sentinel values.
+  priority_spec() = default;
+
+  // Create a priority spec with the given priority settings.
+  explicit priority_spec(const int32_t stream_id, const int32_t weight,
+                         const bool exclusive = false);
+
+  // Return a pointer to a valid nghttp2 priority spec, or null.
+  const nghttp2_priority_spec *get() const;
+
+  // Indicates whether or not this spec is valid (i.e. was constructed with
+  // values).
+  const bool valid() const;
+
+private:
+  nghttp2_priority_spec spec_;
+  bool valid_ = false;
+};
+
 class session_impl;
 
 class session {
@@ -177,7 +199,8 @@ public:
   // 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;
+                        header_map h = header_map{},
+                        priority_spec prio = priority_spec()) const;
 
   // Submits request to server using |method| (e.g., "GET"), |uri|
   // (e.g., "http://localhost/") and optionally additional header
@@ -186,7 +209,8 @@ public:
   // 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;
+                        std::string data, header_map h = header_map{},
+                        priority_spec prio = priority_spec()) const;
 
   // Submits request to server using |method| (e.g., "GET"), |uri|
   // (e.g., "http://localhost/") and optionally additional header
@@ -195,7 +219,8 @@ public:
   // 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;
+                        generator_cb cb, header_map h = header_map{},
+                        priority_spec prio = priority_spec()) const;
 
 private:
   std::shared_ptr<session_impl> impl_;
index 9140e05..4f9bf5a 100644 (file)
@@ -48,7 +48,9 @@
 
 namespace nghttp2 {
 
-typedef struct { int dump_header_table; } inflate_config;
+typedef struct {
+  int dump_header_table;
+} inflate_config;
 
 static inflate_config config;
 
index 44daf16..6f76b49 100644 (file)
 #include "nghttp2_config.h"
 
 #include <limits.h>
+#ifdef _WIN32
+/* Structure for scatter/gather I/O.  */
+struct iovec {
+  void *iov_base; /* Pointer to data.  */
+  size_t iov_len; /* Length of data.  */
+};
+#else // !_WIN32
 #include <sys/uio.h>
+#endif // !_WIN32
 
 #include <cassert>
 #include <cstring>
@@ -50,23 +58,21 @@ namespace nghttp2 {
 #endif // !defined(IOV_MAX) || IOV_MAX >= DEFAULT_WR_IOVCNT
 
 template <size_t N> struct Memchunk {
-  Memchunk(std::unique_ptr<Memchunk> next_chunk)
-      : pos(std::begin(buf)),
-        last(pos),
-        knext(std::move(next_chunk)),
-        next(nullptr) {}
+  Memchunk(Memchunk *next_chunk)
+      : pos(std::begin(buf)), last(pos), knext(next_chunk), next(nullptr) {}
   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<uint8_t, N> buf;
   uint8_t *pos, *last;
-  std::unique_ptr<Memchunk> knext;
+  Memchunk *knext;
   Memchunk *next;
   static const size_t size = N;
 };
 
 template <typename T> struct Pool {
   Pool() : pool(nullptr), freelist(nullptr), poolsize(0) {}
+  ~Pool() { clear(); }
   T *get() {
     if (freelist) {
       auto m = freelist;
@@ -76,9 +82,9 @@ template <typename T> struct Pool {
       return m;
     }
 
-    pool = make_unique<T>(std::move(pool));
+    pool = new T{pool};
     poolsize += T::size;
-    return pool.get();
+    return pool;
   }
   void recycle(T *m) {
     m->next = freelist;
@@ -86,11 +92,16 @@ template <typename T> struct Pool {
   }
   void clear() {
     freelist = nullptr;
+    for (auto p = pool; p;) {
+      auto knext = p->knext;
+      delete p;
+      p = knext;
+    }
     pool = nullptr;
     poolsize = 0;
   }
   using value_type = T;
-  std::unique_ptr<T> pool;
+  T *pool;
   T *freelist;
   size_t poolsize;
 };
index 0ae48eb..41854b7 100644 (file)
@@ -42,34 +42,34 @@ void test_pool_recycle(void) {
 
   auto m1 = pool.get();
 
-  CU_ASSERT(m1 == pool.pool.get());
+  CU_ASSERT(m1 == pool.pool);
   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(m2 == pool.pool);
   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());
+  CU_ASSERT(m1 == m2->knext);
+  CU_ASSERT(nullptr == m1->knext);
 
   auto m3 = pool.get();
 
-  CU_ASSERT(m3 == pool.pool.get());
+  CU_ASSERT(m3 == pool.pool);
   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(m3 == pool.pool);
   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(m4 == pool.pool);
   CU_ASSERT(3 * MemchunkPool::value_type::size == pool.poolsize);
   CU_ASSERT(nullptr == pool.freelist);
 
index 3fb765d..02dda09 100644 (file)
 #ifdef HAVE_SYS_SOCKET_H
 #include <sys/socket.h>
 #endif // HAVE_SYS_SOCKET_H
+#ifdef _WIN32
+#include <ws2tcpip.h>
+#else // !_WIN32
 #include <sys/un.h>
+#endif // !_WIN32
 #ifdef HAVE_NETINET_IN_H
 #include <netinet/in.h>
 #endif // HAVE_NETINET_IN_H
@@ -48,7 +52,9 @@ union sockaddr_union {
   sockaddr sa;
   sockaddr_in6 in6;
   sockaddr_in in;
+#ifndef _WIN32
   sockaddr_un un;
+#endif // !_WIN32
 };
 
 struct Address {
index b94e2ba..7c29b3c 100644 (file)
@@ -57,7 +57,7 @@
 #include "HtmlParser.h"
 #include "util.h"
 #include "base64.h"
-#include "ssl.h"
+#include "tls.h"
 #include "template.h"
 
 #ifndef O_BINARY
@@ -89,7 +89,11 @@ enum {
 
 namespace {
 constexpr auto anchors = std::array<Anchor, 5>{{
-    {3, 0, 201}, {5, 0, 101}, {7, 0, 1}, {9, 7, 1}, {11, 3, 1},
+    {3, 0, 201},
+    {5, 0, 101},
+    {7, 0, 1},
+    {9, 7, 1},
+    {11, 3, 1},
 }};
 } // namespace
 
@@ -116,7 +120,8 @@ Config::Config()
       no_dep(false),
       hexdump(false),
       no_push(false),
-      expect_continue(false) {
+      expect_continue(false),
+      verify_peer(true) {
   nghttp2_option_new(&http2_option);
   nghttp2_option_set_peer_max_concurrent_streams(http2_option,
                                                  peer_max_concurrent_streams);
@@ -171,6 +176,8 @@ Request::~Request() { nghttp2_gzip_inflate_del(inflater); }
 
 void Request::init_inflater() {
   int rv;
+  // This is required with --disable-assert.
+  (void)rv;
   rv = nghttp2_gzip_inflate_new(&inflater);
   assert(rv == 0);
 }
@@ -402,16 +409,9 @@ int htp_msg_begincb(http_parser *htp) {
 } // namespace
 
 namespace {
-int htp_statuscb(http_parser *htp, const char *at, size_t length) {
-  auto client = static_cast<HttpClient *>(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<HttpClient *>(htp->data);
+  client->upgrade_response_status_code = htp->status_code;
   client->upgrade_response_complete = true;
   return 0;
 }
@@ -421,7 +421,7 @@ namespace {
 constexpr 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_status;
     nullptr,           // http_data_cb on_header_field;
     nullptr,           // http_data_cb on_header_value;
     nullptr,           // http_cb      on_headers_complete;
@@ -646,6 +646,11 @@ int HttpClient::resolve_host(const std::string &host, uint16_t port) {
   return 0;
 }
 
+namespace {
+// Just returns 1 to continue handshake.
+int verify_cb(int preverify_ok, X509_STORE_CTX *ctx) { return 1; }
+} // namespace
+
 int HttpClient::initiate_connection() {
   int rv;
 
@@ -675,6 +680,17 @@ int HttpClient::initiate_connection() {
       const auto &host_string =
           config.host_override.empty() ? host : config.host_override;
 
+#if (!defined(LIBRESSL_VERSION_NUMBER) &&                                      \
+     OPENSSL_VERSION_NUMBER >= 0x10002000L) ||                                 \
+    defined(OPENSSL_IS_BORINGSSL)
+      auto param = SSL_get0_param(ssl);
+      X509_VERIFY_PARAM_set_hostflags(param, 0);
+      X509_VERIFY_PARAM_set1_host(param, host_string.c_str(),
+                                  host_string.size());
+#endif // (!defined(LIBRESSL_VERSION_NUMBER) && OPENSSL_VERSION_NUMBER >=
+       // 0x10002000L) || defined(OPENSSL_IS_BORINGSSL)
+      SSL_set_verify(ssl, SSL_VERIFY_PEER, verify_cb);
+
       if (!util::numeric_host(host_string.c_str())) {
         SSL_set_tlsext_host_name(ssl, host_string.c_str());
       }
@@ -1295,6 +1311,14 @@ int HttpClient::tls_handshake() {
   readfn = &HttpClient::read_tls;
   writefn = &HttpClient::write_tls;
 
+  if (config.verify_peer) {
+    auto verify_res = SSL_get_verify_result(ssl);
+    if (verify_res != X509_V_OK) {
+      std::cerr << "[WARNING] Certificate verification failed: "
+                << X509_verify_cert_error_string(verify_res) << std::endl;
+    }
+  }
+
   if (connection_made() != 0) {
     return -1;
   }
@@ -2247,15 +2271,20 @@ int communicate(
     SSL_CTX_set_mode(ssl_ctx, SSL_MODE_AUTO_RETRY);
     SSL_CTX_set_mode(ssl_ctx, SSL_MODE_RELEASE_BUFFERS);
 
-    if (nghttp2::ssl::ssl_ctx_set_proto_versions(
-            ssl_ctx, nghttp2::ssl::NGHTTP2_TLS_MIN_VERSION,
-            nghttp2::ssl::NGHTTP2_TLS_MAX_VERSION) != 0) {
+    if (SSL_CTX_set_default_verify_paths(ssl_ctx) != 1) {
+      std::cerr << "[WARNING] Could not load system trusted CA certificates: "
+                << ERR_error_string(ERR_get_error(), nullptr) << std::endl;
+    }
+
+    if (nghttp2::tls::ssl_ctx_set_proto_versions(
+            ssl_ctx, nghttp2::tls::NGHTTP2_TLS_MIN_VERSION,
+            nghttp2::tls::NGHTTP2_TLS_MAX_VERSION) != 0) {
       std::cerr << "[ERROR] Could not set TLS versions" << std::endl;
       result = -1;
       goto fin;
     }
 
-    if (SSL_CTX_set_cipher_list(ssl_ctx, ssl::DEFAULT_CIPHER_LIST) == 0) {
+    if (SSL_CTX_set_cipher_list(ssl_ctx, tls::DEFAULT_CIPHER_LIST) == 0) {
       std::cerr << "[ERROR] " << ERR_error_string(ERR_get_error(), nullptr)
                 << std::endl;
       result = -1;
@@ -2433,8 +2462,8 @@ int run(char **uris, int n) {
     nghttp2_session_callbacks_set_on_invalid_frame_recv_callback(
         callbacks, verbose_on_invalid_frame_recv_callback);
 
-    nghttp2_session_callbacks_set_error_callback(callbacks,
-                                                 verbose_error_callback);
+    nghttp2_session_callbacks_set_error_callback2(callbacks,
+                                                  verbose_error_callback);
   }
 
   nghttp2_session_callbacks_set_on_data_chunk_recv_callback(
@@ -2701,6 +2730,9 @@ Options:
               (up to  a short  timeout)  until the server sends  a 100
               Continue interim response. This option is ignored unless
               combined with the -d option.
+  -y, --no-verify-peer
+              Suppress  warning  on  server  certificate  verification
+              failure.
   --version   Display version information and exit.
   -h, --help  Display this help and exit.
 
@@ -2718,7 +2750,7 @@ Options:
 } // namespace
 
 int main(int argc, char **argv) {
-  ssl::libssl_init();
+  tls::libssl_init();
 
   bool color = false;
   while (1) {
@@ -2742,6 +2774,7 @@ int main(int argc, char **argv) {
         {"header-table-size", required_argument, nullptr, 'c'},
         {"padding", required_argument, nullptr, 'b'},
         {"har", required_argument, nullptr, 'r'},
+        {"no-verify-peer", no_argument, nullptr, 'y'},
         {"cert", required_argument, &flag, 1},
         {"key", required_argument, &flag, 2},
         {"color", no_argument, &flag, 3},
@@ -2757,8 +2790,9 @@ int main(int argc, char **argv) {
         {"encoder-header-table-size", required_argument, &flag, 14},
         {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);
+    int c =
+        getopt_long(argc, argv, "M:Oab:c:d:m:np:r:hH:vst:uw:yW:", long_options,
+                    &option_index);
     if (c == -1) {
       break;
     }
@@ -2888,6 +2922,9 @@ int main(int argc, char **argv) {
       config.min_header_table_size = std::min(config.min_header_table_size, n);
       break;
     }
+    case 'y':
+      config.verify_peer = false;
+      break;
     case '?':
       util::show_candidates(argv[optind - 1], long_options);
       exit(EXIT_FAILURE);
index a5f60a8..3d970f0 100644 (file)
@@ -96,6 +96,7 @@ struct Config {
   bool hexdump;
   bool no_push;
   bool expect_continue;
+  bool verify_peer;
 };
 
 enum class RequestState { INITIAL, ON_REQUEST, ON_RESPONSE, ON_COMPLETE };
index b12f311..0ede3c5 100644 (file)
@@ -72,6 +72,7 @@ int nghttp2_gzip_inflate(nghttp2_gzip *inflater, uint8_t *out,
   switch (rv) {
   case Z_STREAM_END:
     inflater->finished = 1;
+  /* FALL THROUGH */
   case Z_OK:
   case Z_BUF_ERROR:
     return 0;
index 4255caa..7ad3c8d 100644 (file)
@@ -43,14 +43,14 @@ static size_t deflate_data(uint8_t *out, size_t outlen, const uint8_t *in,
   zst.opaque = Z_NULL;
 
   rv = deflateInit(&zst, Z_DEFAULT_COMPRESSION);
-  assert(rv == Z_OK);
+  CU_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);
+  CU_ASSERT(rv == Z_OK);
 
   deflateEnd(&zst);
 
index 6080037..c7b64b5 100644 (file)
@@ -48,7 +48,7 @@
 #include "app_helper.h"
 #include "HttpServer.h"
 #include "util.h"
-#include "ssl.h"
+#include "tls.h"
 
 namespace nghttp2 {
 
@@ -174,8 +174,7 @@ Options:
   --mime-types-file=<PATH>
               Path  to file  that contains  MIME media  types and  the
               extensions that represent them.
-              Default: )"
-      << config.mime_types_file << R"(
+              Default: )" << config.mime_types_file << R"(
   --no-content-length
               Don't send content-length header field.
   --version   Display version information and exit.
@@ -190,10 +189,10 @@ Options:
 } // namespace
 
 int main(int argc, char **argv) {
-  ssl::libssl_init();
+  tls::libssl_init();
 
 #ifndef NOTHREADS
-  ssl::LibsslGlobalLock lock;
+  tls::LibsslGlobalLock lock;
 #endif // NOTHREADS
 
   Config config;
index e53c1ac..7f9bd80 100644 (file)
@@ -30,7 +30,7 @@
 #include <string.h>
 #include <CUnit/Basic.h>
 // include test cases' include files here
-#include "shrpx_ssl_test.h"
+#include "shrpx_tls_test.h"
 #include "shrpx_downstream_test.h"
 #include "shrpx_config_test.h"
 #include "shrpx_worker_test.h"
@@ -43,7 +43,7 @@
 #include "shrpx_http_test.h"
 #include "base64_test.h"
 #include "shrpx_config.h"
-#include "ssl.h"
+#include "tls.h"
 #include "shrpx_router_test.h"
 #include "shrpx_log.h"
 
@@ -55,7 +55,7 @@ int main(int argc, char *argv[]) {
   CU_pSuite pSuite = NULL;
   unsigned int num_tests_failed;
 
-  nghttp2::ssl::libssl_init();
+  nghttp2::tls::libssl_init();
 
   shrpx::create_config();
 
@@ -71,12 +71,12 @@ int main(int argc, char *argv[]) {
   }
 
   // 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_ssl_ctx",
-                   shrpx::test_shrpx_ssl_cert_lookup_tree_add_ssl_ctx) ||
-      !CU_add_test(pSuite, "ssl_tls_hostname_match",
-                   shrpx::test_shrpx_ssl_tls_hostname_match) ||
+  if (!CU_add_test(pSuite, "tls_create_lookup_tree",
+                   shrpx::test_shrpx_tls_create_lookup_tree) ||
+      !CU_add_test(pSuite, "tls_cert_lookup_tree_add_ssl_ctx",
+                   shrpx::test_shrpx_tls_cert_lookup_tree_add_ssl_ctx) ||
+      !CU_add_test(pSuite, "tls_tls_hostname_match",
+                   shrpx::test_shrpx_tls_tls_hostname_match) ||
       !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",
@@ -115,6 +115,10 @@ int main(int argc, char *argv[]) {
                    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, "downstream_supports_non_final_response",
+                   shrpx::test_downstream_supports_non_final_response) ||
+      !CU_add_test(pSuite, "downstream_find_affinity_cookie",
+                   shrpx::test_downstream_find_affinity_cookie) ||
       !CU_add_test(pSuite, "config_parse_header",
                    shrpx::test_shrpx_config_parse_header) ||
       !CU_add_test(pSuite, "config_parse_log_format",
@@ -129,7 +133,11 @@ int main(int argc, char *argv[]) {
                    shrpx::test_shrpx_http_create_forwarded) ||
       !CU_add_test(pSuite, "http_create_via_header_value",
                    shrpx::test_shrpx_http_create_via_header_value) ||
+      !CU_add_test(pSuite, "http_create_affinity_cookie",
+                   shrpx::test_shrpx_http_create_affinity_cookie) ||
       !CU_add_test(pSuite, "router_match", shrpx::test_shrpx_router_match) ||
+      !CU_add_test(pSuite, "router_match_wildcard",
+                   shrpx::test_shrpx_router_match_wildcard) ||
       !CU_add_test(pSuite, "router_match_prefix",
                    shrpx::test_shrpx_router_match_prefix) ||
       !CU_add_test(pSuite, "util_streq", shrpx::test_util_streq) ||
index 6def310..c9010dd 100644 (file)
@@ -76,7 +76,7 @@
 #include <nghttp2/nghttp2.h>
 
 #include "shrpx_config.h"
-#include "shrpx_ssl.h"
+#include "shrpx_tls.h"
 #include "shrpx_log_config.h"
 #include "shrpx_worker.h"
 #include "shrpx_http2_upstream.h"
@@ -88,7 +88,7 @@
 #include "shrpx_log.h"
 #include "util.h"
 #include "app_helper.h"
-#include "ssl.h"
+#include "tls.h"
 #include "template.h"
 #include "allocator.h"
 #include "ssl_compat.h"
@@ -173,10 +173,6 @@ struct InheritedAddr {
 };
 
 namespace {
-std::random_device rd;
-} // namespace
-
-namespace {
 void signal_cb(struct ev_loop *loop, ev_signal *w, int revents);
 } // namespace
 
@@ -577,9 +573,14 @@ void signal_cb(struct ev_loop *loop, ev_signal *w, int revents) {
   case EXEC_BINARY_SIGNAL:
     exec_binary();
     return;
-  case GRACEFUL_SHUTDOWN_SIGNAL:
+  case GRACEFUL_SHUTDOWN_SIGNAL: {
+    auto &listenerconf = get_config()->conn.listener;
+    for (auto &addr : listenerconf.addrs) {
+      close(addr.fd);
+    }
     ipc_send(wp, SHRPX_IPC_GRACEFUL_SHUTDOWN);
     return;
+  }
   case RELOAD_SIGNAL:
     reload_config(wp);
     return;
@@ -630,16 +631,16 @@ int create_unix_domain_server_socket(UpstreamAddr &faddr,
   auto fd = socket(AF_UNIX, SOCK_STREAM | SOCK_NONBLOCK, 0);
   if (fd == -1) {
     auto error = errno;
-    LOG(WARN) << "socket() syscall failed: "
-              << xsi_strerror(error, errbuf.data(), errbuf.size());
+    LOG(FATAL) << "socket() syscall failed: "
+               << xsi_strerror(error, errbuf.data(), errbuf.size());
     return -1;
   }
 #else  // !SOCK_NONBLOCK
   auto fd = socket(AF_UNIX, SOCK_STREAM, 0);
   if (fd == -1) {
     auto error = errno;
-    LOG(WARN) << "socket() syscall failed: "
-              << xsi_strerror(error, errbuf.data(), errbuf.size());
+    LOG(FATAL) << "socket() syscall failed: "
+               << xsi_strerror(error, errbuf.data(), errbuf.size());
     return -1;
   }
   util::make_socket_nonblocking(fd);
@@ -648,8 +649,8 @@ int create_unix_domain_server_socket(UpstreamAddr &faddr,
   if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &val,
                  static_cast<socklen_t>(sizeof(val))) == -1) {
     auto error = errno;
-    LOG(WARN) << "Failed to set SO_REUSEADDR option to listener socket: "
-              << xsi_strerror(error, errbuf.data(), errbuf.size());
+    LOG(FATAL) << "Failed to set SO_REUSEADDR option to listener socket: "
+               << xsi_strerror(error, errbuf.data(), errbuf.size());
     close(fd);
     return -1;
   }
@@ -719,12 +720,17 @@ int create_tcp_server_socket(UpstreamAddr &faddr,
 
   addrinfo *res, *rp;
   rv = getaddrinfo(node, service.c_str(), &hints, &res);
+#ifdef AI_ADDRCONFIG
   if (rv != 0) {
-    if (LOG_ENABLED(INFO)) {
-      LOG(INFO) << "Unable to get IPv" << (faddr.family == AF_INET ? "4" : "6")
-                << " address for " << faddr.host << ", port " << faddr.port
-                << ": " << gai_strerror(rv);
-    }
+    // Retry without AI_ADDRCONFIG
+    hints.ai_flags &= ~AI_ADDRCONFIG;
+    rv = getaddrinfo(node, service.c_str(), &hints, &res);
+  }
+#endif // AI_ADDRCONFIG
+  if (rv != 0) {
+    LOG(FATAL) << "Unable to get IPv" << (faddr.family == AF_INET ? "4" : "6")
+               << " address for " << faddr.host << ", port " << faddr.port
+               << ": " << gai_strerror(rv);
     return -1;
   }
 
@@ -840,8 +846,8 @@ int create_tcp_server_socket(UpstreamAddr &faddr,
   }
 
   if (!rp) {
-    LOG(WARN) << "Listening " << (faddr.family == AF_INET ? "IPv4" : "IPv6")
-              << " socket failed";
+    LOG(FATAL) << "Listening " << (faddr.family == AF_INET ? "IPv4" : "IPv6")
+               << " socket failed";
 
     return -1;
   }
@@ -1204,11 +1210,21 @@ pid_t fork_worker_process(int &main_ipc_fd,
     return -1;
   }
 
-  auto pid = fork();
+  auto config = get_config();
+
+  pid_t pid = 0;
+
+  if (!config->single_process) {
+    pid = fork();
+  }
 
   if (pid == 0) {
     ev_loop_fork(EV_DEFAULT);
 
+    for (auto &addr : config->conn.listener.addrs) {
+      util::make_socket_closeonexec(addr.fd);
+    }
+
     // Remove all WorkerProcesses to stop any registered watcher on
     // default loop.
     worker_process_remove_all();
@@ -1223,22 +1239,37 @@ pid_t fork_worker_process(int &main_ipc_fd,
       LOG(FATAL) << "Unblocking all signals failed: "
                  << xsi_strerror(error, errbuf.data(), errbuf.size());
 
-      nghttp2_Exit(EXIT_FAILURE);
+      if (config->single_process) {
+        exit(EXIT_FAILURE);
+      } else {
+        nghttp2_Exit(EXIT_FAILURE);
+      }
+    }
+
+    if (!config->single_process) {
+      close(ipc_fd[1]);
     }
 
-    close(ipc_fd[1]);
     WorkerProcessConfig wpconf{ipc_fd[0]};
     rv = worker_process_event_loop(&wpconf);
     if (rv != 0) {
       LOG(FATAL) << "Worker process returned error";
 
-      nghttp2_Exit(EXIT_FAILURE);
+      if (config->single_process) {
+        exit(EXIT_FAILURE);
+      } else {
+        nghttp2_Exit(EXIT_FAILURE);
+      }
     }
 
     LOG(NOTICE) << "Worker process shutting down momentarily";
 
     // call exit(...) instead of nghttp2_Exit to get leak sanitizer report
-    nghttp2_Exit(EXIT_SUCCESS);
+    if (config->single_process) {
+      exit(EXIT_SUCCESS);
+    } else {
+      nghttp2_Exit(EXIT_SUCCESS);
+    }
   }
 
   // parent process
@@ -1315,7 +1346,7 @@ int event_loop() {
 
   auto loop = ev_default_loop(config->ev_loop_flags);
 
-  int ipc_fd;
+  int ipc_fd = 0;
 
   auto pid = fork_worker_process(ipc_fd, {});
 
@@ -1358,15 +1389,12 @@ bool conf_exists(const char *path) {
 } // namespace
 
 namespace {
-constexpr auto DEFAULT_NPN_LIST = StringRef::from_lit("h2,h2-16,h2-14,"
-#ifdef HAVE_SPDYLAY
-                                                      "spdy/3.1,"
-#endif // HAVE_SPDYLAY
-                                                      "http/1.1");
+constexpr auto DEFAULT_NPN_LIST =
+    StringRef::from_lit("h2,h2-16,h2-14,http/1.1");
 } // namespace
 
 namespace {
-constexpr auto DEFAULT_TLS_MIN_PROTO_VERSION = StringRef::from_lit("TLSv1.1");
+constexpr auto DEFAULT_TLS_MIN_PROTO_VERSION = StringRef::from_lit("TLSv1.2");
 #ifdef TLS1_3_VERSION
 constexpr auto DEFAULT_TLS_MAX_PROTO_VERSION = StringRef::from_lit("TLSv1.3");
 #else  // !TLS1_3_VERSION
@@ -1430,18 +1458,18 @@ void fill_default_config(Config *config) {
   }
 
   tlsconf.session_timeout = std::chrono::hours(12);
-  tlsconf.ciphers = StringRef::from_lit(nghttp2::ssl::DEFAULT_CIPHER_LIST);
+  tlsconf.ciphers = StringRef::from_lit(nghttp2::tls::DEFAULT_CIPHER_LIST);
   tlsconf.client.ciphers =
-      StringRef::from_lit(nghttp2::ssl::DEFAULT_CIPHER_LIST);
+      StringRef::from_lit(nghttp2::tls::DEFAULT_CIPHER_LIST);
   tlsconf.min_proto_version =
-      ssl::proto_version_from_string(DEFAULT_TLS_MIN_PROTO_VERSION);
+      tls::proto_version_from_string(DEFAULT_TLS_MIN_PROTO_VERSION);
   tlsconf.max_proto_version =
-      ssl::proto_version_from_string(DEFAULT_TLS_MAX_PROTO_VERSION);
-#if OPENSSL_1_1_API
+      tls::proto_version_from_string(DEFAULT_TLS_MAX_PROTO_VERSION);
+#if OPENSSL_1_1_API || defined(OPENSSL_IS_BORINGSSL)
   tlsconf.ecdh_curves = StringRef::from_lit("X25519:P-256:P-384:P-521");
-#else  // !OPENSSL_1_1_API
+#else  // !OPENSSL_1_1_API && !defined(OPENSSL_IS_BORINGSSL)
   tlsconf.ecdh_curves = StringRef::from_lit("P-256:P-384:P-521");
-#endif // !OPENSSL_1_1_API
+#endif // !OPENSSL_1_1_API && !defined(OPENSSL_IS_BORINGSSL)
 
   auto &httpconf = config->http;
   httpconf.server_name = StringRef::from_lit("nghttpx");
@@ -1452,6 +1480,8 @@ void fill_default_config(Config *config) {
   httpconf.max_response_header_fields = 500;
   httpconf.redirect_https_port = StringRef::from_lit("443");
   httpconf.max_requests = std::numeric_limits<size_t>::max();
+  httpconf.xfp.add = true;
+  httpconf.xfp.strip_incoming = true;
 
   auto &http2conf = config->http2;
   {
@@ -1462,13 +1492,11 @@ void fill_default_config(Config *config) {
       timeoutconf.settings = 10_s;
     }
 
-    // window size for HTTP/2 and SPDY upstream connection per stream.
-    // 2**16-1 = 64KiB-1, which is HTTP/2 default.  Please note that
-    // SPDY/3 default is 64KiB.
+    // window size for HTTP/2 upstream connection per stream.  2**16-1
+    // = 64KiB-1, which is HTTP/2 default.
     upstreamconf.window_size = 64_k - 1;
-    // HTTP/2 and SPDY/3.1 has connection-level flow control. The
-    // default window size for HTTP/2 is 64KiB - 1.  SPDY/3's default
-    // is 64KiB
+    // HTTP/2 has connection-level flow control. The default window
+    // size for HTTP/2 is 64KiB - 1.
     upstreamconf.connection_window_size = 64_k - 1;
     upstreamconf.max_concurrent_streams = 100;
 
@@ -1574,7 +1602,7 @@ void fill_default_config(Config *config) {
   }
 
   auto &apiconf = config->api;
-  apiconf.max_request_body = 16_k;
+  apiconf.max_request_body = 32_m;
 
   auto &dnsconf = config->dns;
   {
@@ -1589,14 +1617,14 @@ void fill_default_config(Config *config) {
 
 namespace {
 void print_version(std::ostream &out) {
-  out << get_config()->http.server_name.c_str() << std::endl;
+  out << "nghttpx nghttp2/" NGHTTP2_VERSION << std::endl;
 }
 } // namespace
 
 namespace {
 void print_usage(std::ostream &out) {
   out << R"(Usage: nghttpx [OPTIONS]... [<PRIVATE_KEY> <CERT>]
-A reverse proxy for HTTP/2, HTTP/1 and SPDY.)"
+A reverse proxy for HTTP/2, and HTTP/1.)"
       << std::endl;
 }
 } // namespace
@@ -1626,8 +1654,7 @@ Connections:
               with "unix:" (e.g., unix:/var/run/backend.sock).
 
               Optionally, if <PATTERN>s are given, the backend address
-              is  only  used  if  request  matches  the  pattern.   If
-              --http2-proxy  is  used,  <PATTERN>s are  ignored.   The
+              is  only  used  if  request matches  the  pattern.   The
               pattern  matching is  closely  designed  to ServeMux  in
               net/http package of  Go programming language.  <PATTERN>
               consists of  path, host +  path or just host.   The path
@@ -1638,11 +1665,16 @@ Connections:
               which  only  lacks  trailing  '/'  (e.g.,  path  "/foo/"
               matches request path  "/foo").  If it does  not end with
               "/", it  performs exact match against  the request path.
-              If host  is given, it  performs exact match  against the
-              request host.  If  host alone is given,  "/" is appended
-              to it,  so that it  matches all request paths  under the
-              host   (e.g.,   specifying   "nghttp2.org"   equals   to
-              "nghttp2.org/").
+              If  host  is given,  it  performs  a match  against  the
+              request host.   For a  request received on  the frontend
+              listener with  "sni-fwd" parameter enabled, SNI  host is
+              used instead of a request host.  If host alone is given,
+              "/" is  appended to it,  so that it matches  all request
+              paths  under the  host  (e.g., specifying  "nghttp2.org"
+              equals  to "nghttp2.org/").   CONNECT method  is treated
+              specially.  It  does not have  path, and we  don't allow
+              empty path.  To workaround  this, we assume that CONNECT
+              method has "/" as path.
 
               Patterns with  host take  precedence over  patterns with
               just path.   Then, longer patterns take  precedence over
@@ -1656,6 +1688,18 @@ Connections:
               match  against  "nghttp2.org".   The exact  hosts  match
               takes precedence over the wildcard hosts match.
 
+              If path  part ends with  "*", it is treated  as wildcard
+              path.  The  wildcard path  behaves differently  from the
+              normal path.  For normal path,  match is made around the
+              boundary of path component  separator,"/".  On the other
+              hand, the wildcard  path does not take  into account the
+              path component  separator.  All paths which  include the
+              wildcard  path  without  last  "*" as  prefix,  and  are
+              strictly longer than wildcard  path without last "*" are
+              matched.  "*"  must match  at least one  character.  For
+              example,  the   pattern  "/foo*"  matches   "/foo/"  and
+              "/foobar".  But it does not match "/foo", or "/fo".
+
               If <PATTERN> is omitted or  empty string, "/" is used as
               pattern,  which  matches  all request  paths  (catch-all
               pattern).  The catch-all backend must be given.
@@ -1728,16 +1772,32 @@ Connections:
               The     session     affinity    is     enabled     using
               "affinity=<METHOD>"  parameter.   If  "ip" is  given  in
               <METHOD>, client  IP based session affinity  is enabled.
-              If  "none" is  given  in <METHOD>,  session affinity  is
-              disabled, and this is the default.  The session affinity
-              is enabled per  <PATTERN>.  If at least  one backend has
-              "affinity" parameter,  and its  <METHOD> is  not "none",
-              session  affinity is  enabled  for  all backend  servers
-              sharing  the  same  <PATTERN>.   It is  advised  to  set
-              "affinity"  parameter  to   all  backend  explicitly  if
-              session affinity  is desired.  The session  affinity may
-              break if one of the backend gets unreachable, or backend
-              settings are reloaded or replaced by API.
+              If "cookie"  is given in <METHOD>,  cookie based session
+              affinity is  enabled.  If  "none" is given  in <METHOD>,
+              session affinity  is disabled, and this  is the default.
+              The session  affinity is  enabled per <PATTERN>.   If at
+              least  one backend  has  "affinity"  parameter, and  its
+              <METHOD> is not "none",  session affinity is enabled for
+              all backend  servers sharing the same  <PATTERN>.  It is
+              advised  to  set  "affinity" parameter  to  all  backend
+              explicitly if session affinity  is desired.  The session
+              affinity  may   break  if   one  of  the   backend  gets
+              unreachable,  or   backend  settings  are   reloaded  or
+              replaced by API.
+
+              If   "affinity=cookie"    is   used,    the   additional
+              configuration                is                required.
+              "affinity-cookie-name=<NAME>" must be  used to specify a
+              name     of     cookie      to     use.      Optionally,
+              "affinity-cookie-path=<PATH>" can  be used to  specify a
+              path   which   cookie    is   applied.    The   optional
+              "affinity-cookie-secure=<SECURE>"  controls  the  Secure
+              attribute of a cookie.  The default value is "auto", and
+              the Secure attribute is  determined by a request scheme.
+              If a request scheme is "https", then Secure attribute is
+              set.  Otherwise, it  is not set.  If  <SECURE> is "yes",
+              the  Secure attribute  is  always set.   If <SECURE>  is
+              "no", the Secure attribute is always omitted.
 
               By default, name resolution of backend host name is done
               at  start  up,  or reloading  configuration.   If  "dns"
@@ -1760,6 +1820,13 @@ Connections:
               "redirect-if-no-tls"    parameter   to    all   backends
               explicitly if this feature is desired.
 
+              If "upgrade-scheme"  parameter is used along  with "tls"
+              parameter, HTTP/2 :scheme pseudo header field is changed
+              to "https" from "http" when forwarding a request to this
+              particular backend.  This is  a workaround for a backend
+              server  which  requires  "https" :scheme  pseudo  header
+              field on TLS encrypted connection.
+
               Since ";" and ":" are  used as delimiter, <PATTERN> must
               not  contain these  characters.  Since  ";" has  special
               meaning in shell, the option value must be quoted.
@@ -1781,6 +1848,11 @@ Connections:
               Optionally, TLS  can be disabled by  specifying "no-tls"
               parameter.  TLS is enabled by default.
 
+              If "sni-fwd" parameter is  used, when performing a match
+              to select a backend server,  SNI host name received from
+              the client  is used  instead of  the request  host.  See
+              --backend option about the pattern match.
+
               To  make this  frontend as  API endpoint,  specify "api"
               parameter.   This   is  disabled  by  default.    It  is
               important  to  limit the  access  to  the API  frontend.
@@ -1825,8 +1897,7 @@ Connections:
 Performance:
   -n, --workers=<N>
               Set the number of worker threads.
-              Default: )"
-      << config->num_worker << R"(
+              Default: )" << config->num_worker << R"(
   --single-thread
               Run everything in one  thread inside the worker process.
               This   feature   is   provided  for   better   debugging
@@ -1926,8 +1997,7 @@ Performance:
 
 Timeout:
   --frontend-http2-read-timeout=<DURATION>
-              Specify  read  timeout  for  HTTP/2  and  SPDY  frontend
-              connection.
+              Specify read timeout for HTTP/2 frontend connection.
               Default: )"
       << util::duration_str(config->conn.upstream.timeout.http2_read) << R"(
   --frontend-read-timeout=<DURATION>
@@ -1944,13 +2014,13 @@ Timeout:
               Default: )"
       << util::duration_str(config->conn.upstream.timeout.idle_read) << R"(
   --stream-read-timeout=<DURATION>
-              Specify  read timeout  for HTTP/2  and SPDY  streams.  0
-              means no timeout.
+              Specify  read timeout  for HTTP/2  streams.  0  means no
+              timeout.
               Default: )"
       << util::duration_str(config->http2.timeout.stream_read) << R"(
   --stream-write-timeout=<DURATION>
-              Specify write  timeout for  HTTP/2 and SPDY  streams.  0
-              means no timeout.
+              Specify write  timeout for  HTTP/2 streams.  0  means no
+              timeout.
               Default: )"
       << util::duration_str(config->http2.timeout.stream_write) << R"(
   --backend-read-timeout=<DURATION>
@@ -2008,8 +2078,7 @@ SSL/TLS:
   --client-ciphers=<SUITE>
               Set  allowed cipher  list for  backend connection.   The
               format of the string is described in OpenSSL ciphers(1).
-              Default: )"
-      << config->tls.client.ciphers << R"(
+              Default: )" << config->tls.client.ciphers << R"(
   --ecdh-curves=<LIST>
               Set  supported  curve  list  for  frontend  connections.
               <LIST> is a  colon separated list of curve  NID or names
@@ -2022,11 +2091,14 @@ SSL/TLS:
               Don't  verify backend  server's  certificate  if TLS  is
               enabled for backend connections.
   --cacert=<PATH>
-              Set path to trusted CA  certificate file used in backend
-              TLS connections.   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.
+              Set path to trusted CA  certificate file.  It is used in
+              backend  TLS connections  to verify  peer's certificate.
+              It is also used to  verify OCSP response from the script
+              set by --fetch-ocsp-response-file.  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>
               Path  to file  that contains  password for  the server's
               private key.   If none is  given and the private  key is
@@ -2035,12 +2107,12 @@ SSL/TLS:
               Specify  additional certificate  and  private key  file.
               nghttpx will  choose certificates based on  the hostname
               indicated by client using TLS SNI extension.  If nghttpx
-              is  built with  OpenSSL >=  1.0.2, signature  algorithms
-              (e.g., ECDSA+SHA256, RSA+SHA256) presented by client are
-              also taken  into consideration.  This allows  nghttpx to
-              send ECDSA certificate to  modern clients, while sending
-              RSA based certificate to older clients.  This option can
-              be  used multiple  times.  To  make OCSP  stapling work,
+              is  built with  OpenSSL  >= 1.0.2,  the shared  elliptic
+              curves (e.g., P-256) between  client and server are also
+              taken into  consideration.  This allows nghttpx  to send
+              ECDSA certificate  to modern clients, while  sending RSA
+              based certificate to older  clients.  This option can be
+              used  multiple  times.   To  make  OCSP  stapling  work,
               <CERTPATH> must be absolute path.
 
               Additional parameter  can be specified in  <PARAM>.  The
@@ -2062,14 +2134,19 @@ SSL/TLS:
               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"(
+              Default: )" << DEFAULT_NPN_LIST
+      << R"(
   --verify-client
               Require and verify client certificate.
   --verify-client-cacert=<PATH>
               Path  to file  that contains  CA certificates  to verify
               client certificate.  The file must be in PEM format.  It
               can contain multiple certificates.
+  --verify-client-tolerate-expired
+              Accept  expired  client  certificate.   Operator  should
+              handle  the expired  client  certificate  by some  means
+              (e.g.,  mruby  script).   Otherwise, this  option  might
+              cause a security risk.
   --client-private-key-file=<PATH>
               Path to  file that contains  client private key  used in
               backend client authentication.
@@ -2082,15 +2159,20 @@ SSL/TLS:
               --tls-min-proto-version and  --tls-max-proto-version are
               enabled.  If the protocol list advertised by client does
               not  overlap  this range,  you  will  receive the  error
-              message "unknown protocol".  The available versions are:
+              message "unknown protocol".  If a protocol version lower
+              than TLSv1.2 is specified, make sure that the compatible
+              ciphers are  included in --ciphers option.   The default
+              cipher  list  only   includes  ciphers  compatible  with
+              TLSv1.2 or above.  The available versions are:
               )"
 #ifdef TLS1_3_VERSION
-                             "TLSv1.3, "
+         "TLSv1.3, "
 #endif // TLS1_3_VERSION
-                             "TLSv1.2, TLSv1.1, and TLSv1.0"
-                             R"(
+         "TLSv1.2, TLSv1.1, and TLSv1.0"
+         R"(
               Default: )"
-      << DEFAULT_TLS_MIN_PROTO_VERSION << R"(
+      << DEFAULT_TLS_MIN_PROTO_VERSION
+      << R"(
   --tls-max-proto-version=<VER>
               Specify maximum SSL/TLS protocol.   The name matching is
               done in  case-insensitive manner.  The  versions between
@@ -2100,10 +2182,10 @@ SSL/TLS:
               message "unknown protocol".  The available versions are:
               )"
 #ifdef TLS1_3_VERSION
-                                          "TLSv1.3, "
+         "TLSv1.3, "
 #endif // TLS1_3_VERSION
-                                          "TLSv1.2, TLSv1.1, and TLSv1.0"
-                                          R"(
+         "TLSv1.2, TLSv1.1, and TLSv1.0"
+         R"(
               Default: )"
       << DEFAULT_TLS_MAX_PROTO_VERSION << R"(
   --tls-ticket-key-file=<PATH>
@@ -2185,6 +2267,14 @@ SSL/TLS:
               Set interval to update OCSP response cache.
               Default: )"
       << util::duration_str(config->tls.ocsp.update_interval) << R"(
+  --ocsp-startup
+              Start  accepting connections  after initial  attempts to
+              get OCSP responses  finish.  It does not  matter some of
+              the  attempts  fail.  This  feature  is  useful if  OCSP
+              responses   must    be   available    before   accepting
+              connections.
+  --no-verify-ocsp
+              nghttpx does not verify OCSP response.
   --no-ocsp   Disable OCSP stapling.
   --tls-session-cache-memcached=<HOST>,<PORT>[;tls]
               Specify  address of  memcached server  to store  session
@@ -2272,11 +2362,11 @@ SSL/TLS:
               consider   to  use   --client-no-http2-cipher-black-list
               option.  But be aware its implications.
 
-HTTP/2 and SPDY:
+HTTP/2:
   -c, --frontend-http2-max-concurrent-streams=<N>
               Set the maximum number of  the concurrent streams in one
-              frontend HTTP/2 and SPDY session.
-              Default:  )"
+              frontend HTTP/2 session.
+              Default: )"
       << config->http2.upstream.max_concurrent_streams << R"(
   --backend-http2-max-concurrent-streams=<N>
               Set the maximum number of  the concurrent streams in one
@@ -2286,14 +2376,13 @@ HTTP/2 and SPDY:
               Default: )"
       << config->http2.downstream.max_concurrent_streams << R"(
   --frontend-http2-window-size=<SIZE>
-              Sets the  per-stream initial  window size of  HTTP/2 and
-              SPDY frontend connection.
+              Sets  the  per-stream  initial  window  size  of  HTTP/2
+              frontend connection.
               Default: )"
       << config->http2.upstream.window_size << R"(
   --frontend-http2-connection-window-size=<SIZE>
-              Sets the  per-connection window size of  HTTP/2 and SPDY
-              frontend  connection.  For  SPDY  connection, the  value
-              less than 64KiB is rounded up to 64KiB.
+              Sets the  per-connection window size of  HTTP/2 frontend
+              connection.
               Default: )"
       << config->http2.upstream.connection_window_size << R"(
   --backend-http2-window-size=<SIZE>
@@ -2319,8 +2408,7 @@ HTTP/2 and SPDY:
               It is  also supported if  both frontend and  backend are
               HTTP/2 in default mode.  In  this case, server push from
               backend session is relayed  to frontend, and server push
-              via Link header field  is also supported.  SPDY frontend
-              does not support server push.
+              via Link header field is also supported.
   --frontend-http2-optimize-write-buffer-size
               (Experimental) Enable write  buffer size optimization in
               frontend HTTP/2 TLS  connection.  This optimization aims
@@ -2375,7 +2463,7 @@ HTTP/2 and SPDY:
 
 Mode:
   (default mode)
-              Accept HTTP/2, SPDY and HTTP/1.1 over SSL/TLS.  "no-tls"
+              Accept  HTTP/2,  and  HTTP/1.1 over  SSL/TLS.   "no-tls"
               parameter is  used in  --frontend option,  accept HTTP/2
               and HTTP/1.1 over cleartext  TCP.  The incoming HTTP/1.1
               connection  can  be  upgraded  to  HTTP/2  through  HTTP
@@ -2417,11 +2505,22 @@ Logging:
               * $alpn: ALPN identifier of the protocol which generates
                 the response.   For HTTP/1,  ALPN is  always http/1.1,
                 regardless of minor version.
-              * $ssl_cipher: cipher used for SSL/TLS connection.
-              * $ssl_protocol: protocol for SSL/TLS connection.
-              * $ssl_session_id: session ID for SSL/TLS connection.
-              * $ssl_session_reused:  "r"   if  SSL/TLS   session  was
+              * $tls_cipher: cipher used for SSL/TLS connection.
+              * $tls_client_fingerprint_sha256: SHA-256 fingerprint of
+                client certificate.
+              * $tls_client_fingerprint_sha1:  SHA-1   fingerprint  of
+                client certificate.
+              * $tls_client_subject_name:   subject  name   in  client
+                certificate.
+              * $tls_client_issuer_name:   issuer   name   in   client
+                certificate.
+              * $tls_client_serial:    serial    number   in    client
+                certificate.
+              * $tls_protocol: protocol for SSL/TLS connection.
+              * $tls_session_id: session ID for SSL/TLS connection.
+              * $tls_session_reused:  "r"   if  SSL/TLS   session  was
                 reused.  Otherwise, "."
+              * $tls_sni: SNI server name for SSL/TLS connection.
               * $backend_host:  backend  host   used  to  fulfill  the
                 request.  "-" if backend host is not available.
               * $backend_port:  backend  port   used  to  fulfill  the
@@ -2440,8 +2539,7 @@ Logging:
               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: )"
-      << config->logging.error.file << R"(
+              Default: )" << config->logging.error.file << R"(
   --errorlog-syslog
               Send  error log  to  syslog.  If  this  option is  used,
               --errorlog-file option is ignored.
@@ -2457,6 +2555,15 @@ HTTP:
   --strip-incoming-x-forwarded-for
               Strip X-Forwarded-For  header field from  inbound client
               requests.
+  --no-add-x-forwarded-proto
+              Don't append  additional X-Forwarded-Proto  header field
+              to  the   backend  request.   If  inbound   client  sets
+              X-Forwarded-Proto,                                   and
+              --no-strip-incoming-x-forwarded-proto  option  is  used,
+              they are passed to the backend.
+  --no-strip-incoming-x-forwarded-proto
+              Don't strip X-Forwarded-Proto  header field from inbound
+              client requests.
   --add-forwarded=<LIST>
               Append RFC  7239 Forwarded header field  with parameters
               specified in comma delimited list <LIST>.  The supported
@@ -2564,8 +2671,8 @@ HTTP:
               Specify the port number which appears in Location header
               field  when  redirect  to  HTTPS  URI  is  made  due  to
               "redirect-if-not-tls" parameter in --backend option.
-              Default: )"
-      << config->http.redirect_https_port << R"(
+              Default: )" << config->http.redirect_https_port
+      << R"(
 
 API:
   --api-max-request-body=<SIZE>
@@ -2626,6 +2733,14 @@ Process:
   --user=<USER>
               Run this program as <USER>.   This option is intended to
               be used to drop root privileges.
+  --single-process
+              Run this program in a  single process mode for debugging
+              purpose.  Without this option,  nghttpx creates at least
+              2  processes:  master  and worker  processes.   If  this
+              option is  used, master  and worker  are unified  into a
+              single process.  nghttpx still spawns additional process
+              if neverbleed is used.  In  the single process mode, the
+              signal handling feature is disabled.
 
 Scripting:
   --mruby-file=<PATH>
@@ -2633,9 +2748,10 @@ Scripting:
 
 Misc:
   --conf=<PATH>
-              Load configuration from <PATH>.
-              Default: )"
-      << config->conf_path << R"(
+              Load  configuration  from   <PATH>.   Please  note  that
+              nghttpx always  tries to read the  default configuration
+              file if --conf is not given.
+              Default: )" << config->conf_path << R"(
   --include=<PATH>
               Load additional configurations from <PATH>.  File <PATH>
               is  read  when  configuration  parser  encountered  this
@@ -2653,8 +2769,7 @@ Misc:
   The <DURATION> 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;
+  is omitted, a second is used as unit.)" << std::endl;
 }
 } // namespace
 
@@ -2662,9 +2777,12 @@ namespace {
 int process_options(Config *config,
                     std::vector<std::pair<StringRef, StringRef>> &cmdcfgs) {
   std::array<char, STRERROR_BUFSIZE> errbuf;
+  std::map<StringRef, size_t> pattern_addr_indexer;
   if (conf_exists(config->conf_path.c_str())) {
+    LOG(NOTICE) << "Loading configuration from " << config->conf_path;
     std::set<StringRef> include_set;
-    if (load_config(config, config->conf_path.c_str(), include_set) == -1) {
+    if (load_config(config, config->conf_path.c_str(), include_set,
+                    pattern_addr_indexer) == -1) {
       LOG(FATAL) << "Failed to load configuration from " << config->conf_path;
       return -1;
     }
@@ -2678,7 +2796,8 @@ int process_options(Config *config,
     std::set<StringRef> include_set;
 
     for (auto &p : cmdcfgs) {
-      if (parse_config(config, p.first, p.second, include_set) == -1) {
+      if (parse_config(config, p.first, p.second, include_set,
+                       pattern_addr_indexer) == -1) {
         LOG(FATAL) << "Failed to parse command-line argument.";
         return -1;
       }
@@ -2777,7 +2896,7 @@ int process_options(Config *config,
   }
 
   if (!tlsconf.tls_proto_list.empty()) {
-    tlsconf.tls_proto_mask = ssl::create_tls_proto_mask(tlsconf.tls_proto_list);
+    tlsconf.tls_proto_mask = tls::create_tls_proto_mask(tlsconf.tls_proto_list);
   }
 
   // TODO We depends on the ordering of protocol version macro in
@@ -2788,7 +2907,7 @@ int process_options(Config *config,
     return -1;
   }
 
-  if (ssl::set_alpn_prefs(tlsconf.alpn_prefs, tlsconf.npn_list) != 0) {
+  if (tls::set_alpn_prefs(tlsconf.alpn_prefs, tlsconf.npn_list) != 0) {
     return -1;
   }
 
@@ -2812,7 +2931,7 @@ int process_options(Config *config,
     upstreamconf.worker_connections = std::numeric_limits<size_t>::max();
   }
 
-  if (ssl::upstream_tls_enabled(config->conn) &&
+  if (tls::upstream_tls_enabled(config->conn) &&
       (tlsconf.private_key_file.empty() || tlsconf.cert_file.empty())) {
     LOG(FATAL) << "TLS private key and certificate files are required.  "
                   "Specify them in command-line, or in configuration file "
@@ -2820,7 +2939,7 @@ int process_options(Config *config,
     return -1;
   }
 
-  if (ssl::upstream_tls_enabled(config->conn) && !tlsconf.ocsp.disabled) {
+  if (tls::upstream_tls_enabled(config->conn) && !tlsconf.ocsp.disabled) {
     struct stat buf;
     if (stat(tlsconf.ocsp.fetch_ocsp_response_file.c_str(), &buf) != 0) {
       tlsconf.ocsp.disabled = true;
@@ -2906,7 +3025,7 @@ int process_options(Config *config,
     auto iov = make_byte_ref(config->balloc, SHRPX_OBFUSCATED_NODE_LENGTH + 2);
     auto p = iov.base;
     *p++ = '_';
-    std::mt19937 gen(rd());
+    auto gen = util::make_mt19937();
     p = util::random_alpha_digit(p, p + SHRPX_OBFUSCATED_NODE_LENGTH, gen);
     *p = '\0';
     fwdconf.by_obfuscated = StringRef{iov.base, p};
@@ -2985,7 +3104,7 @@ void reload_config(WorkerProcess *wp) {
   // already created first default loop.
   auto loop = ev_default_loop(new_config->ev_loop_flags);
 
-  int ipc_fd;
+  int ipc_fd = 0;
 
   // fork_worker_process and forked child process assumes new
   // configuration can be obtained from get_config().
@@ -3023,10 +3142,10 @@ int main(int argc, char **argv) {
   int rv;
   std::array<char, STRERROR_BUFSIZE> errbuf;
 
-  nghttp2::ssl::libssl_init();
+  nghttp2::tls::libssl_init();
 
 #ifndef NOTHREADS
-  nghttp2::ssl::LibsslGlobalLock lock;
+  nghttp2::tls::LibsslGlobalLock lock;
 #endif // NOTHREADS
 
   Log::set_severity_level(NOTICE);
@@ -3113,7 +3232,9 @@ int main(int argc, char **argv) {
         {SHRPX_OPT_BACKEND_HTTP_PROXY_URI.c_str(), required_argument, &flag,
          26},
         {SHRPX_OPT_BACKEND_NO_TLS.c_str(), no_argument, &flag, 27},
+        {SHRPX_OPT_OCSP_STARTUP.c_str(), no_argument, &flag, 28},
         {SHRPX_OPT_FRONTEND_NO_TLS.c_str(), no_argument, &flag, 29},
+        {SHRPX_OPT_NO_VERIFY_OCSP.c_str(), no_argument, &flag, 30},
         {SHRPX_OPT_BACKEND_TLS_SNI_FIELD.c_str(), required_argument, &flag, 31},
         {SHRPX_OPT_DH_PARAM_FILE.c_str(), required_argument, &flag, 33},
         {SHRPX_OPT_READ_RATE.c_str(), required_argument, &flag, 34},
@@ -3291,6 +3412,12 @@ int main(int argc, char **argv) {
         {SHRPX_OPT_FRONTEND_MAX_REQUESTS.c_str(), required_argument, &flag,
          155},
         {SHRPX_OPT_SINGLE_THREAD.c_str(), no_argument, &flag, 156},
+        {SHRPX_OPT_NO_ADD_X_FORWARDED_PROTO.c_str(), no_argument, &flag, 157},
+        {SHRPX_OPT_NO_STRIP_INCOMING_X_FORWARDED_PROTO.c_str(), no_argument,
+         &flag, 158},
+        {SHRPX_OPT_SINGLE_PROCESS.c_str(), no_argument, &flag, 159},
+        {SHRPX_OPT_VERIFY_CLIENT_TOLERATE_EXPIRED.c_str(), no_argument, &flag,
+         160},
         {nullptr, 0, nullptr, 0}};
 
     int option_index = 0;
@@ -3459,11 +3586,21 @@ int main(int argc, char **argv) {
         cmdcfgs.emplace_back(SHRPX_OPT_BACKEND_NO_TLS,
                              StringRef::from_lit("yes"));
         break;
+      case 28:
+        // --ocsp-startup
+        cmdcfgs.emplace_back(SHRPX_OPT_OCSP_STARTUP,
+                             StringRef::from_lit("yes"));
+        break;
       case 29:
         // --frontend-no-tls
         cmdcfgs.emplace_back(SHRPX_OPT_FRONTEND_NO_TLS,
                              StringRef::from_lit("yes"));
         break;
+      case 30:
+        // --no-verify-ocsp
+        cmdcfgs.emplace_back(SHRPX_OPT_NO_VERIFY_OCSP,
+                             StringRef::from_lit("yes"));
+        break;
       case 31:
         // --backend-tls-sni-field
         cmdcfgs.emplace_back(SHRPX_OPT_BACKEND_TLS_SNI_FIELD,
@@ -4027,6 +4164,26 @@ int main(int argc, char **argv) {
         cmdcfgs.emplace_back(SHRPX_OPT_SINGLE_THREAD,
                              StringRef::from_lit("yes"));
         break;
+      case 157:
+        // --no-add-x-forwarded-proto
+        cmdcfgs.emplace_back(SHRPX_OPT_NO_ADD_X_FORWARDED_PROTO,
+                             StringRef::from_lit("yes"));
+        break;
+      case 158:
+        // --no-strip-incoming-x-forwarded-proto
+        cmdcfgs.emplace_back(SHRPX_OPT_NO_STRIP_INCOMING_X_FORWARDED_PROTO,
+                             StringRef::from_lit("yes"));
+        break;
+      case 159:
+        // --single-process
+        cmdcfgs.emplace_back(SHRPX_OPT_SINGLE_PROCESS,
+                             StringRef::from_lit("yes"));
+        break;
+      case 160:
+        // --verify-client-tolerate-expired
+        cmdcfgs.emplace_back(SHRPX_OPT_VERIFY_CLIENT_TOLERATE_EXPIRED,
+                             StringRef::from_lit("yes"));
+        break;
       default:
         break;
       }
index f83b6ff..3f41b83 100644 (file)
@@ -99,8 +99,6 @@ void AcceptHandler::accept_connection() {
   util::make_socket_closeonexec(cfd);
 #endif // !HAVE_ACCEPT4
 
-  util::make_socket_nodelay(cfd);
-
   conn_hnr_->handle_connection(cfd, &sockaddr.sa, addrlen, faddr_);
 }
 
index 2be26be..4d56ecf 100644 (file)
  */
 #include "shrpx_api_downstream_connection.h"
 
+#include <sys/mman.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <cstdlib>
+
 #include "shrpx_client_handler.h"
 #include "shrpx_upstream.h"
 #include "shrpx_downstream.h"
@@ -35,31 +40,43 @@ namespace shrpx {
 
 namespace {
 // List of API endpoints
-const APIEndpoint apis[] = {
-    APIEndpoint{
-        StringRef::from_lit("/api/v1beta1/backendconfig"), true,
-        (1 << API_METHOD_POST) | (1 << API_METHOD_PUT),
-        &APIDownstreamConnection::handle_backendconfig,
-    },
-    APIEndpoint{
-        StringRef::from_lit("/api/v1beta1/configrevision"), true,
-        (1 << API_METHOD_GET), &APIDownstreamConnection::handle_configrevision,
-    },
-};
+const std::array<APIEndpoint, 2> &apis() {
+  static const auto apis = new std::array<APIEndpoint, 2>{{
+      APIEndpoint{
+          StringRef::from_lit("/api/v1beta1/backendconfig"),
+          true,
+          (1 << API_METHOD_POST) | (1 << API_METHOD_PUT),
+          &APIDownstreamConnection::handle_backendconfig,
+      },
+      APIEndpoint{
+          StringRef::from_lit("/api/v1beta1/configrevision"),
+          true,
+          (1 << API_METHOD_GET),
+          &APIDownstreamConnection::handle_configrevision,
+      },
+  }};
+
+  return *apis;
+}
 } // namespace
 
 namespace {
 // The method string.  This must be same order of APIMethod.
 constexpr StringRef API_METHOD_STRING[] = {
-    StringRef::from_lit("GET"), StringRef::from_lit("POST"),
+    StringRef::from_lit("GET"),
+    StringRef::from_lit("POST"),
     StringRef::from_lit("PUT"),
 };
 } // namespace
 
 APIDownstreamConnection::APIDownstreamConnection(Worker *worker)
-    : worker_(worker), api_(nullptr), shutdown_read_(false) {}
+    : worker_(worker), api_(nullptr), fd_(-1), shutdown_read_(false) {}
 
-APIDownstreamConnection::~APIDownstreamConnection() {}
+APIDownstreamConnection::~APIDownstreamConnection() {
+  if (fd_ != -1) {
+    close(fd_);
+  }
+}
 
 int APIDownstreamConnection::attach_downstream(Downstream *downstream) {
   if (LOG_ENABLED(INFO)) {
@@ -160,7 +177,7 @@ const APIEndpoint *lookup_api(const StringRef &path) {
     switch (path[25]) {
     case 'g':
       if (util::streq_l("/api/v1beta1/backendconfi", std::begin(path), 25)) {
-        return &apis[0];
+        return &apis()[0];
       }
       break;
     }
@@ -169,7 +186,7 @@ const APIEndpoint *lookup_api(const StringRef &path) {
     switch (path[26]) {
     case 'n':
       if (util::streq_l("/api/v1beta1/configrevisio", std::begin(path), 26)) {
-        return &apis[1];
+        return &apis()[1];
       }
       break;
     }
@@ -226,6 +243,28 @@ int APIDownstreamConnection::push_request_headers() {
     return 0;
   }
 
+  switch (req.method) {
+  case HTTP_POST:
+  case HTTP_PUT: {
+    char tempname[] = "/tmp/nghttpx-api.XXXXXX";
+#ifdef HAVE_MKOSTEMP
+    fd_ = mkostemp(tempname, O_CLOEXEC);
+#else  // !HAVE_MKOSTEMP
+    fd_ = mkstemp(tempname);
+#endif // !HAVE_MKOSTEMP
+    if (fd_ == -1) {
+      send_reply(500, API_FAILURE);
+
+      return 0;
+    }
+#ifndef HAVE_MKOSTEMP
+    util::make_socket_closeonexec(fd_);
+#endif // HAVE_MKOSTEMP
+    unlink(tempname);
+    break;
+  }
+  }
+
   return 0;
 }
 
@@ -268,17 +307,25 @@ int APIDownstreamConnection::push_upload_data_chunk(const uint8_t *data,
     return 0;
   }
 
-  auto output = downstream_->get_request_buf();
-
+  auto &req = downstream_->request();
   auto &apiconf = get_config()->api;
 
-  if (output->rleft() + datalen > apiconf.max_request_body) {
+  if (static_cast<size_t>(req.recv_body_length) > apiconf.max_request_body) {
     send_reply(413, API_FAILURE);
 
     return 0;
   }
 
-  output->append(data, datalen);
+  ssize_t nwrite;
+  while ((nwrite = write(fd_, data, datalen)) == -1 && errno == EINTR)
+    ;
+  if (nwrite == -1) {
+    auto error = errno;
+    LOG(ERROR) << "Could not write API request body: errno=" << error;
+    send_reply(500, API_FAILURE);
+
+    return 0;
+  }
 
   // We don't have to call Upstream::resume_read() here, because
   // request buffer is effectively unlimited.  Actually, we cannot
@@ -296,30 +343,21 @@ int APIDownstreamConnection::end_upload_data() {
 }
 
 int APIDownstreamConnection::handle_backendconfig() {
-  auto output = downstream_->get_request_buf();
-
-  std::array<struct iovec, 2> iov;
-  auto iovcnt = output->riovec(iov.data(), 2);
+  auto &req = downstream_->request();
 
-  if (iovcnt == 0) {
+  if (req.recv_body_length == 0) {
     send_reply(200, API_SUCCESS);
 
     return 0;
   }
 
-  std::unique_ptr<uint8_t[]> large_buf;
-
-  // If data spans in multiple chunks, pull them up into one
-  // contiguous buffer.
-  if (iovcnt > 1) {
-    large_buf = make_unique<uint8_t[]>(output->rleft());
-    auto len = output->rleft();
-    output->remove(large_buf.get(), len);
-
-    iov[0].iov_base = large_buf.get();
-    iov[0].iov_len = len;
+  auto rp = mmap(nullptr, req.recv_body_length, PROT_READ, MAP_SHARED, fd_, 0);
+  if (rp == reinterpret_cast<void *>(-1)) {
+    send_reply(500, API_FAILURE);
   }
 
+  auto unmapper = defer(munmap, rp, req.recv_body_length);
+
   Config new_config{};
   new_config.conn.downstream = std::make_shared<DownstreamConfig>();
   const auto &downstreamconf = new_config.conn.downstream;
@@ -335,9 +373,10 @@ int APIDownstreamConnection::handle_backendconfig() {
   downstreamconf->family = src->family;
 
   std::set<StringRef> include_set;
+  std::map<StringRef, size_t> pattern_addr_indexer;
 
-  for (auto first = reinterpret_cast<const uint8_t *>(iov[0].iov_base),
-            last = first + iov[0].iov_len;
+  for (auto first = reinterpret_cast<const uint8_t *>(rp),
+            last = first + req.recv_body_length;
        first != last;) {
     auto eol = std::find(first, last, '\n');
     if (eol == last) {
@@ -368,7 +407,8 @@ int APIDownstreamConnection::handle_backendconfig() {
       continue;
     }
 
-    if (parse_config(&new_config, optid, opt, optval, include_set) != 0) {
+    if (parse_config(&new_config, optid, opt, optval, include_set,
+                     pattern_addr_indexer) != 0) {
       send_reply(400, API_FAILURE);
       return 0;
     }
index 356ee53..4a230dc 100644 (file)
@@ -96,6 +96,8 @@ private:
   Worker *worker_;
   // This points to the requested APIEndpoint struct.
   const APIEndpoint *api_;
+  // The file descriptor for temporary file to store request body.
+  int fd_;
   // true if we stop reading request body.
   bool shutdown_read_;
 };
index 706b0d2..21430dd 100644 (file)
@@ -42,7 +42,7 @@
 #include "shrpx_config.h"
 #include "shrpx_http_downstream_connection.h"
 #include "shrpx_http2_downstream_connection.h"
-#include "shrpx_ssl.h"
+#include "shrpx_tls.h"
 #include "shrpx_worker.h"
 #include "shrpx_downstream_connection_pool.h"
 #include "shrpx_downstream.h"
 #include "shrpx_api_downstream_connection.h"
 #include "shrpx_health_monitor_downstream_connection.h"
 #include "shrpx_log.h"
-#ifdef HAVE_SPDYLAY
-#include "shrpx_spdy_upstream.h"
-#endif // HAVE_SPDYLAY
 #include "util.h"
 #include "template.h"
-#include "ssl.h"
+#include "tls.h"
 
 using namespace nghttp2;
 
@@ -96,10 +93,6 @@ void readcb(struct ev_loop *loop, ev_io *w, int revents) {
     delete handler;
     return;
   }
-  if (handler->do_write() != 0) {
-    delete handler;
-    return;
-  }
 }
 } // namespace
 
@@ -449,25 +442,32 @@ ClientHandler::ClientHandler(Worker *worker, int fd, SSL *ssl,
       *p = '\0';
 
       forwarded_for_ = StringRef{buf.base, p};
-    } else if (family == AF_INET6) {
-      // 2 for '[' and ']'
-      auto len = 2 + ipaddr_.size();
-      // 1 for terminating NUL.
-      auto buf = make_byte_ref(balloc_, len + 1);
-      auto p = buf.base;
-      *p++ = '[';
-      p = std::copy(std::begin(ipaddr_), std::end(ipaddr_), p);
-      *p++ = ']';
-      *p = '\0';
-
-      forwarded_for_ = StringRef{buf.base, p};
-    } else {
-      // family == AF_INET or family == AF_UNIX
-      forwarded_for_ = ipaddr_;
+    } else if (!faddr_->accept_proxy_protocol &&
+               !config->conn.upstream.accept_proxy_protocol) {
+      init_forwarded_for(family, ipaddr_);
     }
   }
 }
 
+void ClientHandler::init_forwarded_for(int family, const StringRef &ipaddr) {
+  if (family == AF_INET6) {
+    // 2 for '[' and ']'
+    auto len = 2 + ipaddr.size();
+    // 1 for terminating NUL.
+    auto buf = make_byte_ref(balloc_, len + 1);
+    auto p = buf.base;
+    *p++ = '[';
+    p = std::copy(std::begin(ipaddr), std::end(ipaddr), p);
+    *p++ = ']';
+    *p = '\0';
+
+    forwarded_for_ = StringRef{buf.base, p};
+  } else {
+    // family == AF_INET or family == AF_UNIX
+    forwarded_for_ = ipaddr;
+  }
+}
+
 void ClientHandler::setup_upstream_io_callback() {
   if (conn_.tls.ssl) {
     conn_.prepare_server_handshake();
@@ -556,31 +556,23 @@ int ClientHandler::validate_next_proto() {
   }
 #endif // OPENSSL_VERSION_NUMBER >= 0x10002000L
 
-  if (next_proto == nullptr) {
-    if (LOG_ENABLED(INFO)) {
-      CLOG(INFO, this) << "No protocol negotiated. Fallback to HTTP/1.1";
-    }
+  StringRef proto;
 
-    upstream_ = make_unique<HttpsUpstream>(this);
-    alpn_ = StringRef::from_lit("http/1.1");
+  if (next_proto) {
+    proto = StringRef{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;
+    if (LOG_ENABLED(INFO)) {
+      CLOG(INFO, this) << "The negotiated next protocol: " << proto;
+    }
+  } else {
+    if (LOG_ENABLED(INFO)) {
+      CLOG(INFO, this) << "No protocol negotiated. Fallback to HTTP/1.1";
     }
 
-    return 0;
-  }
-
-  auto proto = StringRef{next_proto, next_proto_len};
-
-  if (LOG_ENABLED(INFO)) {
-    CLOG(INFO, this) << "The negotiated next protocol: " << proto;
+    proto = StringRef::from_lit("http/1.1");
   }
 
-  if (!ssl::in_proto_list(get_config()->tls.npn_list, proto)) {
+  if (!tls::in_proto_list(get_config()->tls.npn_list, proto)) {
     if (LOG_ENABLED(INFO)) {
       CLOG(INFO, this) << "The negotiated protocol is not supported: " << proto;
     }
@@ -605,36 +597,6 @@ int ClientHandler::validate_next_proto() {
     return 0;
   }
 
-#ifdef HAVE_SPDYLAY
-  auto spdy_version = spdylay_npn_get_version(proto.byte(), proto.size());
-  if (spdy_version) {
-    upstream_ = make_unique<SpdyUpstream>(spdy_version, this);
-
-    switch (spdy_version) {
-    case SPDYLAY_PROTO_SPDY2:
-      alpn_ = StringRef::from_lit("spdy/2");
-      break;
-    case SPDYLAY_PROTO_SPDY3:
-      alpn_ = StringRef::from_lit("spdy/3");
-      break;
-    case SPDYLAY_PROTO_SPDY3_1:
-      alpn_ = StringRef::from_lit("spdy/3.1");
-      break;
-    default:
-      alpn_ = StringRef::from_lit("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 (proto == StringRef::from_lit("http/1.1")) {
     upstream_ = make_unique<HttpsUpstream>(this);
     alpn_ = StringRef::from_lit("http/1.1");
@@ -696,7 +658,7 @@ void ClientHandler::pool_downstream_connection(
 
   auto &shared_addr = group->shared_addr;
 
-  if (shared_addr->affinity == AFFINITY_NONE) {
+  if (shared_addr->affinity.type == AFFINITY_NONE) {
     auto &dconn_pool = group->shared_addr->dconn_pool;
     dconn_pool.add_downstream_connection(std::move(dconn));
 
@@ -888,11 +850,6 @@ Http2Session *ClientHandler::select_http2_session(
       break;
     }
 
-    if (addr.http2_extra_freelist.size() == 0 &&
-        addr.connect_blocker->blocked()) {
-      continue;
-    }
-
     if (selected_addr == nullptr || load_lighter(&addr, selected_addr)) {
       selected_addr = &addr;
     }
@@ -960,6 +917,24 @@ uint32_t next_cycle(const WeightedPri &pri) {
 }
 } // namespace
 
+uint32_t ClientHandler::get_affinity_cookie(Downstream *downstream,
+                                            const StringRef &cookie_name) {
+  auto h = downstream->find_affinity_cookie(cookie_name);
+  if (h) {
+    return h;
+  }
+
+  auto d = std::uniform_int_distribution<uint32_t>(
+      1, std::numeric_limits<uint32_t>::max());
+  auto rh = d(worker_->get_randgen());
+  h = util::hash32(StringRef{reinterpret_cast<uint8_t *>(&rh),
+                             reinterpret_cast<uint8_t *>(&rh) + sizeof(rh)});
+
+  downstream->renew_affinity_cookie(h);
+
+  return h;
+}
+
 std::unique_ptr<DownstreamConnection>
 ClientHandler::get_downstream_connection(int &err, Downstream *downstream) {
   size_t group_idx;
@@ -980,31 +955,33 @@ ClientHandler::get_downstream_connection(int &err, Downstream *downstream) {
     return make_unique<HealthMonitorDownstreamConnection>();
   }
 
+  auto &balloc = downstream->get_block_allocator();
+
   // Fast path.  If we have one group, it must be catch-all group.
-  // proxy mode falls in this case.
   if (groups.size() == 1) {
     group_idx = 0;
-  } else if (req.method == HTTP_CONNECT) {
-    //  We don't know how to treat CONNECT request in host-path
-    //  mapping.  It most likely appears in proxy scenario.  Since we
-    //  have dealt with proxy case already, just use catch-all group.
-    group_idx = catch_all;
   } else {
-    auto &balloc = downstream->get_block_allocator();
-
-    if (!req.authority.empty()) {
-      group_idx = match_downstream_addr_group(
-          routerconf, req.authority, req.path, groups, catch_all, balloc);
+    StringRef authority;
+    if (faddr_->sni_fwd) {
+      authority = sni_;
+    } else if (!req.authority.empty()) {
+      authority = req.authority;
     } else {
       auto h = req.fs.header(http2::HD_HOST);
       if (h) {
-        group_idx = match_downstream_addr_group(routerconf, h->value, req.path,
-                                                groups, catch_all, balloc);
-      } else {
-        group_idx = match_downstream_addr_group(
-            routerconf, StringRef{}, req.path, groups, catch_all, balloc);
+        authority = h->value;
       }
     }
+
+    StringRef path;
+    // CONNECT method does not have path.  But we requires path in
+    // host-path mapping.  As workaround, we assume that path is "/".
+    if (req.method != HTTP_CONNECT) {
+      path = req.path;
+    }
+
+    group_idx = match_downstream_addr_group(routerconf, authority, path, groups,
+                                            catch_all, balloc);
   }
 
   if (LOG_ENABLED(INFO)) {
@@ -1023,27 +1000,59 @@ ClientHandler::get_downstream_connection(int &err, Downstream *downstream) {
   auto &group = groups[group_idx];
   auto &shared_addr = group->shared_addr;
 
-  if (shared_addr->affinity == AFFINITY_IP) {
-    if (!affinity_hash_computed_) {
-      affinity_hash_ = compute_affinity_from_ip(ipaddr_);
-      affinity_hash_computed_ = true;
+  if (shared_addr->affinity.type != AFFINITY_NONE) {
+    uint32_t hash;
+    switch (shared_addr->affinity.type) {
+    case AFFINITY_IP:
+      if (!affinity_hash_computed_) {
+        affinity_hash_ = compute_affinity_from_ip(ipaddr_);
+        affinity_hash_computed_ = true;
+      }
+      hash = affinity_hash_;
+      break;
+    case AFFINITY_COOKIE:
+      hash = get_affinity_cookie(downstream, shared_addr->affinity.cookie.name);
+      break;
+    default:
+      assert(0);
     }
 
     const auto &affinity_hash = shared_addr->affinity_hash;
 
     auto it = std::lower_bound(
-        std::begin(affinity_hash), std::end(affinity_hash), affinity_hash_,
+        std::begin(affinity_hash), std::end(affinity_hash), hash,
         [](const AffinityHash &lhs, uint32_t rhs) { return lhs.hash < rhs; });
 
     if (it == std::end(affinity_hash)) {
       it = std::begin(affinity_hash);
     }
 
+    auto aff_idx =
+        static_cast<size_t>(std::distance(std::begin(affinity_hash), it));
     auto idx = (*it).idx;
+    auto addr = &shared_addr->addrs[idx];
 
-    auto &addr = shared_addr->addrs[idx];
-    if (addr.proto == PROTO_HTTP2) {
-      auto http2session = select_http2_session_with_affinity(group, &addr);
+    if (addr->connect_blocker->blocked()) {
+      size_t i;
+      for (i = aff_idx + 1; i != aff_idx; ++i) {
+        if (i == shared_addr->affinity_hash.size()) {
+          i = 0;
+        }
+        addr = &shared_addr->addrs[shared_addr->affinity_hash[i].idx];
+        if (addr->connect_blocker->blocked()) {
+          continue;
+        }
+        break;
+      }
+      if (i == aff_idx) {
+        err = -1;
+        return nullptr;
+      }
+      aff_idx = i;
+    }
+
+    if (addr->proto == PROTO_HTTP2) {
+      auto http2session = select_http2_session_with_affinity(group, addr);
 
       auto dconn = make_unique<Http2DownstreamConnection>(http2session);
 
@@ -1052,11 +1061,11 @@ ClientHandler::get_downstream_connection(int &err, Downstream *downstream) {
       return std::move(dconn);
     }
 
-    auto &dconn_pool = addr.dconn_pool;
+    auto &dconn_pool = addr->dconn_pool;
     auto dconn = dconn_pool->pop_downstream_connection();
 
     if (!dconn) {
-      dconn = make_unique<HttpDownstreamConnection>(group, idx, conn_.loop,
+      dconn = make_unique<HttpDownstreamConnection>(group, aff_idx, conn_.loop,
                                                     worker_);
     }
 
@@ -1132,7 +1141,7 @@ ClientHandler::get_downstream_connection(int &err, Downstream *downstream) {
     }
 
     dconn =
-        make_unique<HttpDownstreamConnection>(group, -1, conn_.loop, worker_);
+        make_unique<HttpDownstreamConnection>(group, 0, conn_.loop, worker_);
   }
 
   dconn->set_client_handler(this);
@@ -1205,7 +1214,6 @@ void ClientHandler::start_immediate_shutdown() {
 }
 
 void ClientHandler::write_accesslog(Downstream *downstream) {
-  nghttp2::ssl::TLSSessionInfo tls_info;
   auto &req = downstream->request();
 
   auto config = get_config();
@@ -1219,10 +1227,15 @@ void ClientHandler::write_accesslog(Downstream *downstream) {
   upstream_accesslog(
       config->logging.access.format,
       LogSpec{
-          downstream, ipaddr_, alpn_,
-          nghttp2::ssl::get_tls_session_info(&tls_info, conn_.tls.ssl),
+          downstream,
+          ipaddr_,
+          alpn_,
+          sni_,
+          conn_.tls.ssl,
           std::chrono::high_resolution_clock::now(), // request_end_time
-          port_, faddr_->port, config->pid,
+          port_,
+          faddr_->port,
+          config->pid,
       });
 }
 
@@ -1230,14 +1243,6 @@ ClientHandler::ReadBuf *ClientHandler::get_rb() { return &rb_; }
 
 void ClientHandler::signal_write() { conn_.wlimit.startw(); }
 
-void ClientHandler::signal_write_no_wait() {
-  // ev_feed_event works without starting watcher.  But rate limiter
-  // requires active watcher.  Without that, we might not send pending
-  // data.  Also ClientHandler::write_tls requires it.
-  conn_.wlimit.startw();
-  ev_feed_event(conn_.loop, &conn_.wev, EV_WRITE);
-}
-
 RateLimit *ClientHandler::get_rlimit() { return &conn_.rlimit; }
 RateLimit *ClientHandler::get_wlimit() { return &conn_.wlimit; }
 
@@ -1469,6 +1474,14 @@ int ClientHandler::proxy_protocol_read() {
                      << " bytes read";
   }
 
+  auto config = get_config();
+  auto &fwdconf = config->http.forwarded;
+
+  if ((fwdconf.params & FORWARDED_FOR) &&
+      fwdconf.for_node_type == FORWARDED_NODE_IP) {
+    init_forwarded_for(family, ipaddr_);
+  }
+
   return on_proxy_protocol_finish();
 }
 
@@ -1494,6 +1507,8 @@ void ClientHandler::set_tls_sni(const StringRef &sni) {
 
 StringRef ClientHandler::get_tls_sni() const { return sni_; }
 
+StringRef ClientHandler::get_alpn() const { return alpn_; }
+
 BlockAllocator &ClientHandler::get_block_allocator() { return balloc_; }
 
 } // namespace shrpx
index 4617fcd..8d4073b 100644 (file)
@@ -125,6 +125,9 @@ public:
 
   Worker *get_worker() const;
 
+  // Initializes forwarded_for_.
+  void init_forwarded_for(int family, const StringRef &ipaddr);
+
   using ReadBuf = DefaultMemchunkBuffer;
 
   ReadBuf *get_rb();
@@ -133,8 +136,6 @@ public:
   RateLimit *get_wlimit();
 
   void signal_write();
-  // Use this for HTTP/1 frontend since it produces better result.
-  void signal_write_no_wait();
   ev_io *get_wev();
 
   void setup_upstream_io_callback();
@@ -152,6 +153,11 @@ public:
   Http2Session *select_http2_session_with_affinity(
       const std::shared_ptr<DownstreamAddrGroup> &group, DownstreamAddr *addr);
 
+  // Returns an affinity cookie value for |downstream|.  |cookie_name|
+  // is used to inspect cookie header field in request header fields.
+  uint32_t get_affinity_cookie(Downstream *downstream,
+                               const StringRef &cookie_name);
+
   const UpstreamAddr *get_upstream_addr() const;
 
   void repeat_read_timer();
@@ -165,6 +171,9 @@ public:
   // Returns TLS SNI extension value client sent in this connection.
   StringRef get_tls_sni() const;
 
+  // Returns ALPN negotiated in this connection.
+  StringRef get_alpn() const;
+
   BlockAllocator &get_block_allocator();
 
 private:
index f64714d..e7efed2 100644 (file)
@@ -53,7 +53,7 @@
 #include "http-parser/http_parser.h"
 
 #include "shrpx_log.h"
-#include "shrpx_ssl.h"
+#include "shrpx_tls.h"
 #include "shrpx_http.h"
 #include "util.h"
 #include "base64.h"
 namespace shrpx {
 
 namespace {
-std::unique_ptr<Config> config;
+Config *config;
 } // namespace
 
 constexpr auto SHRPX_UNIX_PATH_PREFIX = StringRef::from_lit("unix:");
 
-const Config *get_config() { return config.get(); }
+const Config *get_config() { return config; }
 
-Config *mod_config() { return config.get(); }
+Config *mod_config() { return config; }
 
 std::unique_ptr<Config> replace_config(std::unique_ptr<Config> another) {
-  config.swap(another);
-  return another;
+  auto p = config;
+  config = another.release();
+  return std::unique_ptr<Config>(p);
 }
 
-void create_config() { config = make_unique<Config>(); }
+void create_config() { config = new Config(); }
 
 Config::~Config() {
   auto &upstreamconf = http2.upstream;
@@ -400,6 +401,11 @@ LogFragmentType log_var_lookup_token(const char *name, size_t namelen) {
     break;
   case 7:
     switch (name[6]) {
+    case 'i':
+      if (util::strieq_l("tls_sn", name, 6)) {
+        return SHRPX_LOGF_TLS_SNI;
+      }
+      break;
     case 't':
       if (util::strieq_l("reques", name, 6)) {
         return SHRPX_LOGF_REQUEST;
@@ -418,6 +424,9 @@ LogFragmentType log_var_lookup_token(const char *name, size_t namelen) {
       if (util::strieq_l("ssl_ciphe", name, 9)) {
         return SHRPX_LOGF_SSL_CIPHER;
       }
+      if (util::strieq_l("tls_ciphe", name, 9)) {
+        return SHRPX_LOGF_TLS_CIPHER;
+      }
       break;
     }
     break;
@@ -454,6 +463,9 @@ LogFragmentType log_var_lookup_token(const char *name, size_t namelen) {
       if (util::strieq_l("ssl_protoco", name, 11)) {
         return SHRPX_LOGF_SSL_PROTOCOL;
       }
+      if (util::strieq_l("tls_protoco", name, 11)) {
+        return SHRPX_LOGF_TLS_PROTOCOL;
+      }
       break;
     case 't':
       if (util::strieq_l("backend_hos", name, 11)) {
@@ -471,6 +483,9 @@ LogFragmentType log_var_lookup_token(const char *name, size_t namelen) {
       if (util::strieq_l("ssl_session_i", name, 13)) {
         return SHRPX_LOGF_SSL_SESSION_ID;
       }
+      if (util::strieq_l("tls_session_i", name, 13)) {
+        return SHRPX_LOGF_TLS_SESSION_ID;
+      }
       break;
     }
     break;
@@ -483,12 +498,60 @@ LogFragmentType log_var_lookup_token(const char *name, size_t namelen) {
       break;
     }
     break;
+  case 17:
+    switch (name[16]) {
+    case 'l':
+      if (util::strieq_l("tls_client_seria", name, 16)) {
+        return SHRPX_LOGF_TLS_CLIENT_SERIAL;
+      }
+      break;
+    }
+    break;
   case 18:
     switch (name[17]) {
     case 'd':
       if (util::strieq_l("ssl_session_reuse", name, 17)) {
         return SHRPX_LOGF_SSL_SESSION_REUSED;
       }
+      if (util::strieq_l("tls_session_reuse", name, 17)) {
+        return SHRPX_LOGF_TLS_SESSION_REUSED;
+      }
+      break;
+    }
+    break;
+  case 22:
+    switch (name[21]) {
+    case 'e':
+      if (util::strieq_l("tls_client_issuer_nam", name, 21)) {
+        return SHRPX_LOGF_TLS_CLIENT_ISSUER_NAME;
+      }
+      break;
+    }
+    break;
+  case 23:
+    switch (name[22]) {
+    case 'e':
+      if (util::strieq_l("tls_client_subject_nam", name, 22)) {
+        return SHRPX_LOGF_TLS_CLIENT_SUBJECT_NAME;
+      }
+      break;
+    }
+    break;
+  case 27:
+    switch (name[26]) {
+    case '1':
+      if (util::strieq_l("tls_client_fingerprint_sha", name, 26)) {
+        return SHRPX_LOGF_TLS_CLIENT_FINGERPRINT_SHA1;
+      }
+      break;
+    }
+    break;
+  case 29:
+    switch (name[28]) {
+    case '6':
+      if (util::strieq_l("tls_client_fingerprint_sha25", name, 28)) {
+        return SHRPX_LOGF_TLS_CLIENT_FINGERPRINT_SHA256;
+      }
       break;
     }
     break;
@@ -641,7 +704,7 @@ int parse_duration(ev_tstamp *dest, const StringRef &opt,
 namespace {
 int parse_tls_proto_version(int &dest, const StringRef &opt,
                             const StringRef &optarg) {
-  auto v = ssl::proto_version_from_string(optarg);
+  auto v = tls::proto_version_from_string(optarg);
   if (v == -1) {
     LOG(ERROR) << opt << ": invalid TLS protocol version: " << optarg;
     return -1;
@@ -692,6 +755,7 @@ int parse_memcached_connection_params(MemcachedConnectionParams &out,
 struct UpstreamParams {
   int alt_mode;
   bool tls;
+  bool sni_fwd;
   bool proxyproto;
 };
 
@@ -707,6 +771,8 @@ int parse_upstream_params(UpstreamParams &out, const StringRef &src_params) {
 
     if (util::strieq_l("tls", param)) {
       out.tls = true;
+    } else if (util::strieq_l("sni-fwd", param)) {
+      out.sni_fwd = true;
     } else if (util::strieq_l("no-tls", param)) {
       out.tls = false;
     } else if (util::strieq_l("api", param)) {
@@ -741,13 +807,14 @@ int parse_upstream_params(UpstreamParams &out, const StringRef &src_params) {
 
 struct DownstreamParams {
   StringRef sni;
+  AffinityConfig affinity;
   size_t fall;
   size_t rise;
   shrpx_proto proto;
-  shrpx_session_affinity affinity;
   bool tls;
   bool dns;
   bool redirect_if_not_tls;
+  bool upgrade_scheme;
 };
 
 namespace {
@@ -814,17 +881,46 @@ int parse_downstream_params(DownstreamParams &out,
     } else if (util::istarts_with_l(param, "affinity=")) {
       auto valstr = StringRef{first + str_size("affinity="), end};
       if (util::strieq_l("none", valstr)) {
-        out.affinity = AFFINITY_NONE;
+        out.affinity.type = AFFINITY_NONE;
       } else if (util::strieq_l("ip", valstr)) {
-        out.affinity = AFFINITY_IP;
+        out.affinity.type = AFFINITY_IP;
+      } else if (util::strieq_l("cookie", valstr)) {
+        out.affinity.type = AFFINITY_COOKIE;
+      } else {
+        LOG(ERROR)
+            << "backend: affinity: value must be one of none, ip, and cookie";
+        return -1;
+      }
+    } else if (util::istarts_with_l(param, "affinity-cookie-name=")) {
+      auto val = StringRef{first + str_size("affinity-cookie-name="), end};
+      if (val.empty()) {
+        LOG(ERROR)
+            << "backend: affinity-cookie-name: non empty string is expected";
+        return -1;
+      }
+      out.affinity.cookie.name = val;
+    } else if (util::istarts_with_l(param, "affinity-cookie-path=")) {
+      out.affinity.cookie.path =
+          StringRef{first + str_size("affinity-cookie-path="), end};
+    } else if (util::istarts_with_l(param, "affinity-cookie-secure=")) {
+      auto valstr = StringRef{first + str_size("affinity-cookie-secure="), end};
+      if (util::strieq_l("auto", valstr)) {
+        out.affinity.cookie.secure = COOKIE_SECURE_AUTO;
+      } else if (util::strieq_l("yes", valstr)) {
+        out.affinity.cookie.secure = COOKIE_SECURE_YES;
+      } else if (util::strieq_l("no", valstr)) {
+        out.affinity.cookie.secure = COOKIE_SECURE_NO;
       } else {
-        LOG(ERROR) << "backend: affinity: value must be either none or ip";
+        LOG(ERROR) << "backend: affinity-cookie-secure: value must be one of "
+                      "auto, yes, and no";
         return -1;
       }
     } else if (util::strieq_l("dns", param)) {
       out.dns = true;
     } else if (util::strieq_l("redirect-if-not-tls", param)) {
       out.redirect_if_not_tls = true;
+    } else if (util::strieq_l("upgrade-scheme", param)) {
+      out.upgrade_scheme = true;
     } else if (!param.empty()) {
       LOG(ERROR) << "backend: " << param << ": unknown keyword";
       return -1;
@@ -850,6 +946,7 @@ namespace {
 //
 // This function returns 0 if it succeeds, or -1.
 int parse_mapping(Config *config, DownstreamAddrConfig &addr,
+                  std::map<StringRef, size_t> &pattern_addr_indexer,
                   const StringRef &src_pattern, const StringRef &src_params) {
   // This returns at least 1 element (it could be empty string).  We
   // will append '/' to all patterns, so it becomes catch-all pattern.
@@ -870,12 +967,20 @@ int parse_mapping(Config *config, DownstreamAddrConfig &addr,
     return -1;
   }
 
+  if (params.affinity.type == AFFINITY_COOKIE &&
+      params.affinity.cookie.name.empty()) {
+    LOG(ERROR) << "backend: affinity-cookie-name is mandatory if "
+                  "affinity=cookie is specified";
+    return -1;
+  }
+
   addr.fall = params.fall;
   addr.rise = params.rise;
   addr.proto = params.proto;
   addr.tls = params.tls;
   addr.sni = make_string_ref(downstreamconf.balloc, params.sni);
   addr.dns = params.dns;
+  addr.upgrade_scheme = params.upgrade_scheme;
 
   auto &routerconf = downstreamconf.router;
   auto &router = routerconf.router;
@@ -883,7 +988,6 @@ int parse_mapping(Config *config, DownstreamAddrConfig &addr,
   auto &wildcard_patterns = routerconf.wildcard_patterns;
 
   for (const auto &raw_pattern : mapping) {
-    auto done = false;
     StringRef pattern;
     auto slash = std::find(std::begin(raw_pattern), std::end(raw_pattern), '/');
     if (slash == std::end(raw_pattern)) {
@@ -910,32 +1014,56 @@ int parse_mapping(Config *config, DownstreamAddrConfig &addr,
       *p = '\0';
       pattern = StringRef{iov.base, p};
     }
-    for (auto &g : addr_groups) {
-      if (g.pattern == pattern) {
-        // Last value wins if we have multiple different affinity
-        // value under one group.
-        if (params.affinity != AFFINITY_NONE) {
-          g.affinity = params.affinity;
-        }
-        // If at least one backend requires frontend TLS connection,
-        // enable it for all backends sharing the same pattern.
-        if (params.redirect_if_not_tls) {
-          g.redirect_if_not_tls = true;
+    auto it = pattern_addr_indexer.find(pattern);
+    if (it != std::end(pattern_addr_indexer)) {
+      auto &g = addr_groups[(*it).second];
+      // Last value wins if we have multiple different affinity
+      // value under one group.
+      if (params.affinity.type != AFFINITY_NONE) {
+        if (g.affinity.type == AFFINITY_NONE) {
+          g.affinity.type = params.affinity.type;
+          if (params.affinity.type == AFFINITY_COOKIE) {
+            g.affinity.cookie.name = make_string_ref(
+                downstreamconf.balloc, params.affinity.cookie.name);
+            if (!params.affinity.cookie.path.empty()) {
+              g.affinity.cookie.path = make_string_ref(
+                  downstreamconf.balloc, params.affinity.cookie.path);
+            }
+            g.affinity.cookie.secure = params.affinity.cookie.secure;
+          }
+        } else if (g.affinity.type != params.affinity.type ||
+                   g.affinity.cookie.name != params.affinity.cookie.name ||
+                   g.affinity.cookie.path != params.affinity.cookie.path ||
+                   g.affinity.cookie.secure != params.affinity.cookie.secure) {
+          LOG(ERROR) << "backend: affinity: multiple different affinity "
+                        "configurations found in a single group";
+          return -1;
         }
-        g.addrs.push_back(addr);
-        done = true;
-        break;
       }
-    }
-    if (done) {
+      // If at least one backend requires frontend TLS connection,
+      // enable it for all backends sharing the same pattern.
+      if (params.redirect_if_not_tls) {
+        g.redirect_if_not_tls = true;
+      }
+      g.addrs.push_back(addr);
       continue;
     }
 
     auto idx = addr_groups.size();
+    pattern_addr_indexer.emplace(pattern, idx);
     addr_groups.emplace_back(pattern);
     auto &g = addr_groups.back();
     g.addrs.push_back(addr);
-    g.affinity = params.affinity;
+    g.affinity.type = params.affinity.type;
+    if (params.affinity.type == AFFINITY_COOKIE) {
+      g.affinity.cookie.name =
+          make_string_ref(downstreamconf.balloc, params.affinity.cookie.name);
+      if (!params.affinity.cookie.path.empty()) {
+        g.affinity.cookie.path =
+            make_string_ref(downstreamconf.balloc, params.affinity.cookie.path);
+      }
+      g.affinity.cookie.secure = params.affinity.cookie.secure;
+    }
     g.redirect_if_not_tls = params.redirect_if_not_tls;
 
     if (pattern[0] == '*') {
@@ -946,6 +1074,12 @@ int parse_mapping(Config *config, DownstreamAddrConfig &addr,
       auto host = StringRef{std::begin(g.pattern) + 1, path_first};
       auto path = StringRef{path_first, std::end(g.pattern)};
 
+      auto path_is_wildcard = false;
+      if (path[path.size() - 1] == '*') {
+        path = StringRef{std::begin(path), std::begin(path) + path.size() - 1};
+        path_is_wildcard = true;
+      }
+
       auto it = std::find_if(
           std::begin(wildcard_patterns), std::end(wildcard_patterns),
           [&host](const WildcardPattern &wp) { return wp.host == host; });
@@ -954,7 +1088,7 @@ int parse_mapping(Config *config, DownstreamAddrConfig &addr,
         wildcard_patterns.emplace_back(host);
 
         auto &router = wildcard_patterns.back().router;
-        router.add_route(path, idx);
+        router.add_route(path, idx, path_is_wildcard);
 
         auto iov = make_byte_ref(downstreamconf.balloc, host.size() + 1);
         auto p = iov.base;
@@ -964,13 +1098,20 @@ int parse_mapping(Config *config, DownstreamAddrConfig &addr,
 
         rw_router.add_route(rev_host, wildcard_patterns.size() - 1);
       } else {
-        (*it).router.add_route(path, idx);
+        (*it).router.add_route(path, idx, path_is_wildcard);
       }
 
       continue;
     }
 
-    router.add_route(g.pattern, idx);
+    auto path_is_wildcard = false;
+    if (pattern[pattern.size() - 1] == '*') {
+      pattern = StringRef{std::begin(pattern),
+                          std::begin(pattern) + pattern.size() - 1};
+      path_is_wildcard = true;
+    }
+
+    router.add_route(pattern, idx, path_is_wildcard);
   }
   return 0;
 }
@@ -1557,6 +1698,11 @@ int option_lookup_token(const char *name, size_t namelen) {
         return SHRPX_OPTID_HTTP2_BRIDGE;
       }
       break;
+    case 'p':
+      if (util::strieq_l("ocsp-startu", name, 11)) {
+        return SHRPX_OPTID_OCSP_STARTUP;
+      }
+      break;
     case 'y':
       if (util::strieq_l("client-prox", name, 11)) {
         return SHRPX_OPTID_CLIENT_PROXY;
@@ -1612,6 +1758,11 @@ int option_lookup_token(const char *name, size_t namelen) {
         return SHRPX_OPTID_NO_SERVER_PUSH;
       }
       break;
+    case 'p':
+      if (util::strieq_l("no-verify-ocs", name, 13)) {
+        return SHRPX_OPTID_NO_VERIFY_OCSP;
+      }
+      break;
     case 's':
       if (util::strieq_l("backend-no-tl", name, 13)) {
         return SHRPX_OPTID_BACKEND_NO_TLS;
@@ -1619,6 +1770,9 @@ int option_lookup_token(const char *name, size_t namelen) {
       if (util::strieq_l("client-cipher", name, 13)) {
         return SHRPX_OPTID_CLIENT_CIPHERS;
       }
+      if (util::strieq_l("single-proces", name, 13)) {
+        return SHRPX_OPTID_SINGLE_PROCESS;
+      }
       break;
     case 't':
       if (util::strieq_l("tls-proto-lis", name, 13)) {
@@ -1901,6 +2055,11 @@ int option_lookup_token(const char *name, size_t namelen) {
         return SHRPX_OPTID_FETCH_OCSP_RESPONSE_FILE;
       }
       break;
+    case 'o':
+      if (util::strieq_l("no-add-x-forwarded-prot", name, 23)) {
+        return SHRPX_OPTID_NO_ADD_X_FORWARDED_PROTO;
+      }
+      break;
     case 't':
       if (util::strieq_l("listener-disable-timeou", name, 23)) {
         return SHRPX_OPTID_LISTENER_DISABLE_TIMEOUT;
@@ -2014,6 +2173,11 @@ int option_lookup_token(const char *name, size_t namelen) {
     break;
   case 30:
     switch (name[29]) {
+    case 'd':
+      if (util::strieq_l("verify-client-tolerate-expire", name, 29)) {
+        return SHRPX_OPTID_VERIFY_CLIENT_TOLERATE_EXPIRED;
+      }
+      break;
     case 'r':
       if (util::strieq_l("strip-incoming-x-forwarded-fo", name, 29)) {
         return SHRPX_OPTID_STRIP_INCOMING_X_FORWARDED_FOR;
@@ -2097,6 +2261,11 @@ int option_lookup_token(const char *name, size_t namelen) {
         return SHRPX_OPTID_FRONTEND_HTTP2_OPTIMIZE_WINDOW_SIZE;
       }
       break;
+    case 'o':
+      if (util::strieq_l("no-strip-incoming-x-forwarded-prot", name, 34)) {
+        return SHRPX_OPTID_NO_STRIP_INCOMING_X_FORWARDED_PROTO;
+      }
+      break;
     case 'r':
       if (util::strieq_l("frontend-http2-dump-response-heade", name, 34)) {
         return SHRPX_OPTID_FRONTEND_HTTP2_DUMP_RESPONSE_HEADER;
@@ -2223,13 +2392,16 @@ int option_lookup_token(const char *name, size_t namelen) {
 }
 
 int parse_config(Config *config, const StringRef &opt, const StringRef &optarg,
-                 std::set<StringRef> &included_set) {
+                 std::set<StringRef> &included_set,
+                 std::map<StringRef, size_t> &pattern_addr_indexer) {
   auto optid = option_lookup_token(opt.c_str(), opt.size());
-  return parse_config(config, optid, opt, optarg, included_set);
+  return parse_config(config, optid, opt, optarg, included_set,
+                      pattern_addr_indexer);
 }
 
 int parse_config(Config *config, int optid, const StringRef &opt,
-                 const StringRef &optarg, std::set<StringRef> &included_set) {
+                 const StringRef &optarg, std::set<StringRef> &included_set,
+                 std::map<StringRef, size_t> &pattern_addr_indexer) {
   std::array<char, STRERROR_BUFSIZE> errbuf;
   char host[NI_MAXHOST];
   uint16_t port;
@@ -2261,7 +2433,8 @@ int parse_config(Config *config, int optid, const StringRef &opt,
     auto params =
         mapping_end == std::end(optarg) ? mapping_end : mapping_end + 1;
 
-    if (parse_mapping(config, addr, StringRef{mapping, mapping_end},
+    if (parse_mapping(config, addr, pattern_addr_indexer,
+                      StringRef{mapping, mapping_end},
                       StringRef{params, std::end(optarg)}) != 0) {
       return -1;
     }
@@ -2282,9 +2455,15 @@ int parse_config(Config *config, int optid, const StringRef &opt,
       return -1;
     }
 
+    if (params.sni_fwd && !params.tls) {
+      LOG(ERROR) << "frontend: sni_fwd requires tls";
+      return -1;
+    }
+
     UpstreamAddr addr{};
     addr.fd = -1;
     addr.tls = params.tls;
+    addr.sni_fwd = params.sni_fwd;
     addr.alt_mode = params.alt_mode;
     addr.accept_proxy_protocol = params.proxyproto;
 
@@ -2460,8 +2639,7 @@ int parse_config(Config *config, int optid, const StringRef &opt,
     }
 
     // Make 16 bits to the HTTP/2 default 64KiB - 1.  This is the same
-    // behaviour of previous code.  For SPDY, we adjust this value in
-    // SpdyUpstream to look like the SPDY default.
+    // behaviour of previous code.
     *resp = (1 << n) - 1;
 
     return 0;
@@ -2503,14 +2681,16 @@ int parse_config(Config *config, int optid, const StringRef &opt,
               << SHRPX_OPT_FRONTEND;
     return 0;
   case SHRPX_OPTID_BACKEND_NO_TLS:
-    LOG(WARN) << opt << ": deprecated.  backend connection is not encrypted by "
-                        "default.  See also "
+    LOG(WARN) << opt
+              << ": deprecated.  backend connection is not encrypted by "
+                 "default.  See also "
               << SHRPX_OPT_BACKEND_TLS;
     return 0;
   case SHRPX_OPTID_BACKEND_TLS_SNI_FIELD:
-    LOG(WARN) << opt << ": deprecated.  Use sni keyword in --backend option.  "
-                        "For now, all sni values of all backends are "
-                        "overridden by the given value "
+    LOG(WARN) << opt
+              << ": deprecated.  Use sni keyword in --backend option.  "
+                 "For now, all sni values of all backends are "
+                 "overridden by the given value "
               << optarg;
     config->tls.backend_sni_name = make_string_ref(config->balloc, optarg);
 
@@ -2621,8 +2801,9 @@ int parse_config(Config *config, int optid, const StringRef &opt,
 
     return 0;
   case SHRPX_OPTID_CLIENT:
-    LOG(ERROR) << opt << ": deprecated.  Use frontend=<addr>,<port>;no-tls, "
-                         "backend=<addr>,<port>;;proto=h2;tls";
+    LOG(ERROR) << opt
+               << ": deprecated.  Use frontend=<addr>,<port>;no-tls, "
+                  "backend=<addr>,<port>;;proto=h2;tls";
     return -1;
   case SHRPX_OPTID_INSECURE:
     config->tls.insecure = util::strieq_l("yes", optarg);
@@ -2717,8 +2898,9 @@ int parse_config(Config *config, int optid, const StringRef &opt,
     return 0;
   }
   case SHRPX_OPTID_TLS_PROTO_LIST: {
-    LOG(WARN) << opt << ": deprecated.  Use tls-min-proto-version and "
-                        "tls-max-proto-version instead.";
+    LOG(WARN) << opt
+              << ": deprecated.  Use tls-min-proto-version and "
+                 "tls-max-proto-version instead.";
     auto list = util::split_str(optarg, ',');
     config->tls.tls_proto_list.resize(list.size());
     for (size_t i = 0; i < list.size(); ++i) {
@@ -2952,7 +3134,8 @@ int parse_config(Config *config, int optid, const StringRef &opt,
     }
 
     included_set.insert(optarg);
-    auto rv = load_config(config, optarg.c_str(), included_set);
+    auto rv =
+        load_config(config, optarg.c_str(), included_set, pattern_addr_indexer);
     included_set.erase(optarg);
 
     if (rv != 0) {
@@ -3343,8 +3526,9 @@ int parse_config(Config *config, int optid, const StringRef &opt,
   case SHRPX_OPTID_REDIRECT_HTTPS_PORT: {
     auto n = util::parse_uint(optarg);
     if (n == -1 || n < 0 || n > 65535) {
-      LOG(ERROR) << opt << ": bad value.  Specify an integer in the range [0, "
-                           "65535], inclusive";
+      LOG(ERROR) << opt
+                 << ": bad value.  Specify an integer in the range [0, "
+                    "65535], inclusive";
       return -1;
     }
     config->http.redirect_https_port = optarg;
@@ -3356,6 +3540,30 @@ int parse_config(Config *config, int optid, const StringRef &opt,
     config->single_thread = util::strieq_l("yes", optarg);
 
     return 0;
+  case SHRPX_OPTID_SINGLE_PROCESS:
+    config->single_process = util::strieq_l("yes", optarg);
+
+    return 0;
+  case SHRPX_OPTID_NO_ADD_X_FORWARDED_PROTO:
+    config->http.xfp.add = !util::strieq_l("yes", optarg);
+
+    return 0;
+  case SHRPX_OPTID_NO_STRIP_INCOMING_X_FORWARDED_PROTO:
+    config->http.xfp.strip_incoming = !util::strieq_l("yes", optarg);
+
+    return 0;
+  case SHRPX_OPTID_OCSP_STARTUP:
+    config->tls.ocsp.startup = util::strieq_l("yes", optarg);
+
+    return 0;
+  case SHRPX_OPTID_NO_VERIFY_OCSP:
+    config->tls.ocsp.no_verify = util::strieq_l("yes", optarg);
+
+    return 0;
+  case SHRPX_OPTID_VERIFY_CLIENT_TOLERATE_EXPIRED:
+    config->tls.client_verify.tolerate_expired = util::strieq_l("yes", optarg);
+
+    return 0;
   case SHRPX_OPTID_CONF:
     LOG(WARN) << "conf: ignored";
 
@@ -3368,7 +3576,8 @@ int parse_config(Config *config, int optid, const StringRef &opt,
 }
 
 int load_config(Config *config, const char *filename,
-                std::set<StringRef> &include_set) {
+                std::set<StringRef> &include_set,
+                std::map<StringRef, size_t> &pattern_addr_indexer) {
   std::ifstream in(filename, std::ios::binary);
   if (!in) {
     LOG(ERROR) << "Could not open config file " << filename;
@@ -3390,7 +3599,8 @@ int load_config(Config *config, const char *filename,
     *eq = '\0';
 
     if (parse_config(config, StringRef{std::begin(line), eq},
-                     StringRef{eq + 1, std::end(line)}, include_set) != 0) {
+                     StringRef{eq + 1, std::end(line)}, include_set,
+                     pattern_addr_indexer) != 0) {
       return -1;
     }
   }
@@ -3548,6 +3758,7 @@ StringRef strproto(shrpx_proto proto) {
 
   // gcc needs this.
   assert(0);
+  abort();
 }
 
 namespace {
@@ -3610,19 +3821,6 @@ int configure_downstream_group(Config *config, bool http2_proxy,
     g.addrs.push_back(std::move(addr));
     router.add_route(g.pattern, addr_groups.size());
     addr_groups.push_back(std::move(g));
-  } else if (http2_proxy) {
-    // We don't support host mapping in these cases.  Move all
-    // non-catch-all patterns to catch-all pattern.
-    DownstreamAddrGroupConfig catch_all(StringRef::from_lit("/"));
-    for (auto &g : addr_groups) {
-      std::move(std::begin(g.addrs), std::end(g.addrs),
-                std::back_inserter(catch_all.addrs));
-    }
-    std::vector<DownstreamAddrGroupConfig>().swap(addr_groups);
-    // maybe not necessary?
-    routerconf = RouterConfig{};
-    router.add_route(catch_all.pattern, addr_groups.size());
-    addr_groups.push_back(std::move(catch_all));
   }
 
   // backward compatibility: override all SNI fields with the option
@@ -3725,7 +3923,7 @@ int configure_downstream_group(Config *config, bool http2_proxy,
       }
     }
 
-    if (g.affinity == AFFINITY_IP) {
+    if (g.affinity.type != AFFINITY_NONE) {
       size_t idx = 0;
       for (auto &addr : g.addrs) {
         StringRef key;
@@ -3773,6 +3971,13 @@ int resolve_hostname(Address *addr, const char *hostname, uint16_t port,
   addrinfo *res;
 
   rv = getaddrinfo(hostname, service.c_str(), &hints, &res);
+#ifdef AI_ADDRCONFIG
+  if (rv != 0) {
+    // Retry without AI_ADDRCONFIG
+    hints.ai_flags &= ~AI_ADDRCONFIG;
+    rv = getaddrinfo(hostname, service.c_str(), &hints, &res);
+  }
+#endif // AI_ADDRCONFIG
   if (rv != 0) {
     LOG(FATAL) << "Unable to resolve address for " << hostname << ": "
                << gai_strerror(rv);
index 21c2a77..d631a56 100644 (file)
@@ -64,11 +64,11 @@ struct LogFragment;
 class ConnectBlocker;
 class Http2Session;
 
-namespace ssl {
+namespace tls {
 
 class CertLookupTree;
 
-} // namespace ssl
+} // namespace tls
 
 constexpr auto SHRPX_OPT_PRIVATE_KEY_FILE =
     StringRef::from_lit("private-key-file");
@@ -336,6 +336,15 @@ constexpr auto SHRPX_OPT_REDIRECT_HTTPS_PORT =
 constexpr auto SHRPX_OPT_FRONTEND_MAX_REQUESTS =
     StringRef::from_lit("frontend-max-requests");
 constexpr auto SHRPX_OPT_SINGLE_THREAD = StringRef::from_lit("single-thread");
+constexpr auto SHRPX_OPT_SINGLE_PROCESS = StringRef::from_lit("single-process");
+constexpr auto SHRPX_OPT_NO_ADD_X_FORWARDED_PROTO =
+    StringRef::from_lit("no-add-x-forwarded-proto");
+constexpr auto SHRPX_OPT_NO_STRIP_INCOMING_X_FORWARDED_PROTO =
+    StringRef::from_lit("no-strip-incoming-x-forwarded-proto");
+constexpr auto SHRPX_OPT_OCSP_STARTUP = StringRef::from_lit("ocsp-startup");
+constexpr auto SHRPX_OPT_NO_VERIFY_OCSP = StringRef::from_lit("no-verify-ocsp");
+constexpr auto SHRPX_OPT_VERIFY_CLIENT_TOLERATE_EXPIRED =
+    StringRef::from_lit("verify-client-tolerate-expired");
 
 constexpr size_t SHRPX_OBFUSCATED_NODE_LENGTH = 8;
 
@@ -349,6 +358,31 @@ enum shrpx_session_affinity {
   AFFINITY_NONE,
   // Client IP affinity
   AFFINITY_IP,
+  // Cookie based affinity
+  AFFINITY_COOKIE,
+};
+
+enum shrpx_cookie_secure {
+  // Secure attribute of session affinity cookie is determined by the
+  // request scheme.
+  COOKIE_SECURE_AUTO,
+  // Secure attribute of session affinity cookie is always set.
+  COOKIE_SECURE_YES,
+  // Secure attribute of session affinity cookie is always unset.
+  COOKIE_SECURE_NO,
+};
+
+struct AffinityConfig {
+  // Type of session affinity.
+  shrpx_session_affinity type;
+  struct {
+    // Name of a cookie to use.
+    StringRef name;
+    // Path which a cookie is applied to.
+    StringRef path;
+    // Secure attribute
+    shrpx_cookie_secure secure;
+  } cookie;
 };
 
 enum shrpx_forwarded_param {
@@ -399,6 +433,9 @@ struct UpstreamAddr {
   bool host_unix;
   // true if TLS is enabled.
   bool tls;
+  // true if SNI host should be used as a host when selecting backend
+  // server.
+  bool sni_fwd;
   // true if client is supposed to send PROXY protocol v1 header.
   bool accept_proxy_protocol;
   int fd;
@@ -426,6 +463,10 @@ struct DownstreamAddrConfig {
   bool tls;
   // true if dynamic DNS is enabled
   bool dns;
+  // true if :scheme pseudo header field should be upgraded to secure
+  // variant (e.g., "https") when forwarding request to a backend
+  // connected by TLS connection.
+  bool upgrade_scheme;
 };
 
 // Mapping hash to idx which is an index into
@@ -439,15 +480,15 @@ struct AffinityHash {
 
 struct DownstreamAddrGroupConfig {
   DownstreamAddrGroupConfig(const StringRef &pattern)
-      : pattern(pattern), affinity(AFFINITY_NONE), redirect_if_not_tls(false) {}
+      : pattern(pattern), affinity{AFFINITY_NONE}, redirect_if_not_tls(false) {}
 
   StringRef pattern;
   std::vector<DownstreamAddrConfig> addrs;
   // Bunch of session affinity hash.  Only used if affinity ==
   // AFFINITY_IP.
   std::vector<AffinityHash> affinity_hash;
-  // Session affinity
-  shrpx_session_affinity affinity;
+  // Cookie based session affinity configuration.
+  AffinityConfig affinity;
   // true if this group requires that client connection must be TLS,
   // and the request must be redirected to https URI.
   bool redirect_if_not_tls;
@@ -553,6 +594,8 @@ struct TLSConfig {
     ev_tstamp update_interval;
     StringRef fetch_ocsp_response_file;
     bool disabled;
+    bool startup;
+    bool no_verify;
   } ocsp;
 
   // Client verification configurations
@@ -561,6 +604,8 @@ struct TLSConfig {
     // certificate validation
     StringRef cacert;
     bool enabled;
+    // true if we accept an expired client certificate.
+    bool tolerate_expired;
   } client_verify;
 
   // Client (backend connection) TLS configuration.
@@ -638,6 +683,10 @@ struct HttpConfig {
     bool add;
     bool strip_incoming;
   } xff;
+  struct {
+    bool add;
+    bool strip_incoming;
+  } xfp;
   std::vector<AltSvc> altsvcs;
   std::vector<ErrorPage> error_pages;
   HeaderRefs add_request_headers;
@@ -864,6 +913,7 @@ struct Config {
         verbose{false},
         daemon{false},
         http2_proxy{false},
+        single_process{false},
         single_thread{false},
         ev_loop_flags{0} {}
   ~Config();
@@ -905,6 +955,9 @@ struct Config {
   bool verbose;
   bool daemon;
   bool http2_proxy;
+  // Run nghttpx in single process mode.  With this mode, signal
+  // handling is omitted.
+  bool single_process;
   bool single_thread;
   // flags passed to ev_default_loop() and ev_loop_new()
   int ev_loop_flags;
@@ -1018,6 +1071,7 @@ enum {
   SHRPX_OPTID_MAX_REQUEST_HEADER_FIELDS,
   SHRPX_OPTID_MAX_RESPONSE_HEADER_FIELDS,
   SHRPX_OPTID_MRUBY_FILE,
+  SHRPX_OPTID_NO_ADD_X_FORWARDED_PROTO,
   SHRPX_OPTID_NO_HOST_REWRITE,
   SHRPX_OPTID_NO_HTTP2_CIPHER_BLACK_LIST,
   SHRPX_OPTID_NO_KQUEUE,
@@ -1025,8 +1079,11 @@ enum {
   SHRPX_OPTID_NO_OCSP,
   SHRPX_OPTID_NO_SERVER_PUSH,
   SHRPX_OPTID_NO_SERVER_REWRITE,
+  SHRPX_OPTID_NO_STRIP_INCOMING_X_FORWARDED_PROTO,
+  SHRPX_OPTID_NO_VERIFY_OCSP,
   SHRPX_OPTID_NO_VIA,
   SHRPX_OPTID_NPN_LIST,
+  SHRPX_OPTID_OCSP_STARTUP,
   SHRPX_OPTID_OCSP_UPDATE_INTERVAL,
   SHRPX_OPTID_PADDING,
   SHRPX_OPTID_PID_FILE,
@@ -1040,6 +1097,7 @@ enum {
   SHRPX_OPTID_RESPONSE_HEADER_FIELD_BUFFER,
   SHRPX_OPTID_RLIMIT_NOFILE,
   SHRPX_OPTID_SERVER_NAME,
+  SHRPX_OPTID_SINGLE_PROCESS,
   SHRPX_OPTID_SINGLE_THREAD,
   SHRPX_OPTID_STREAM_READ_TIMEOUT,
   SHRPX_OPTID_STREAM_WRITE_TIMEOUT,
@@ -1071,6 +1129,7 @@ enum {
   SHRPX_OPTID_USER,
   SHRPX_OPTID_VERIFY_CLIENT,
   SHRPX_OPTID_VERIFY_CLIENT_CACERT,
+  SHRPX_OPTID_VERIFY_CLIENT_TOLERATE_EXPIRED,
   SHRPX_OPTID_WORKER_FRONTEND_CONNECTIONS,
   SHRPX_OPTID_WORKER_READ_BURST,
   SHRPX_OPTID_WORKER_READ_RATE,
@@ -1089,20 +1148,26 @@ int option_lookup_token(const char *name, size_t namelen);
 // stored into the object pointed by |config|. This function returns 0
 // if it succeeds, or -1.  The |included_set| contains the all paths
 // already included while processing this configuration, to avoid loop
-// in --include option.
+// in --include option.  The |pattern_addr_indexer| contains a pair of
+// pattern of backend, and its index in DownstreamConfig::addr_groups.
+// It is introduced to speed up loading configuration file with lots
+// of backends.
 int parse_config(Config *config, const StringRef &opt, const StringRef &optarg,
-                 std::set<StringRef> &included_set);
+                 std::set<StringRef> &included_set,
+                 std::map<StringRef, size_t> &pattern_addr_indexer);
 
 // Similar to parse_config() above, but additional |optid| which
 // should be the return value of option_lookup_token(opt).
 int parse_config(Config *config, int optid, const StringRef &opt,
-                 const StringRef &optarg, std::set<StringRef> &included_set);
+                 const StringRef &optarg, std::set<StringRef> &included_set,
+                 std::map<StringRef, size_t> &pattern_addr_indexer);
 
 // Loads configurations from |filename| and stores them in |config|.
 // This function returns 0 if it succeeds, or -1.  See parse_config()
 // for |include_set|.
 int load_config(Config *config, const char *filename,
-                std::set<StringRef> &include_set);
+                std::set<StringRef> &include_set,
+                std::map<StringRef, size_t> &pattern_addr_indexer);
 
 // Parses header field in |optarg|.  We expect header field is formed
 // like "NAME: VALUE".  We require that NAME is non empty string.  ":"
index bc168b4..7531971 100644 (file)
@@ -163,14 +163,14 @@ void test_shrpx_config_parse_log_format(void) {
 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));
+  CU_ASSERT(fd1 != -1);
+  CU_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));
+  CU_ASSERT(fd2 != -1);
+  CU_ASSERT(48 ==
+            write(fd2, "6..............78..............9a..............b", 48));
 
   close(fd1);
   close(fd2);
@@ -204,16 +204,18 @@ void test_shrpx_config_read_tls_ticket_key_file(void) {
 void test_shrpx_config_read_tls_ticket_key_file_aes_256(void) {
   char file1[] = "/tmp/nghttpx-unittest.XXXXXX";
   auto fd1 = mkstemp(file1);
-  assert(fd1 != -1);
-  assert(80 == write(fd1, "0..............12..............................34..."
-                          "...........................5",
-                     80));
+  CU_ASSERT(fd1 != -1);
+  CU_ASSERT(80 == write(fd1,
+                        "0..............12..............................34..."
+                        "...........................5",
+                        80));
   char file2[] = "/tmp/nghttpx-unittest.XXXXXX";
   auto fd2 = mkstemp(file2);
-  assert(fd2 != -1);
-  assert(80 == write(fd2, "6..............78..............................9a..."
-                          "...........................b",
-                     80));
+  CU_ASSERT(fd2 != -1);
+  CU_ASSERT(80 == write(fd2,
+                        "6..............78..............................9a..."
+                        "...........................b",
+                        80));
 
   close(fd1);
   close(fd2);
index 4ccd6a1..1ebe789 100644 (file)
@@ -81,6 +81,6 @@ private:
   bool offline_;
 };
 
-} // namespace
+} // namespace shrpx
 
 #endif // SHRPX_CONNECT_BLOCKER_H
index de74c1d..06ad958 100644 (file)
@@ -33,7 +33,7 @@
 
 #include <openssl/err.h>
 
-#include "shrpx_ssl.h"
+#include "shrpx_tls.h"
 #include "shrpx_memcached_request.h"
 #include "shrpx_log.h"
 #include "memchunk.h"
@@ -123,6 +123,7 @@ void Connection::disconnect() {
     tls.handshake_state = 0;
     tls.initial_handshake_done = false;
     tls.reneg_started = false;
+    tls.sct_requested = false;
   }
 
   if (fd != -1) {
@@ -288,22 +289,18 @@ BIO_METHOD *create_bio_method() {
   return meth;
 }
 
-void delete_bio_method(BIO_METHOD *bio_method) { BIO_meth_free(bio_method); }
-
 #else // !OPENSSL_1_1_API
 
 BIO_METHOD *create_bio_method() {
-  static BIO_METHOD shrpx_bio_method = {
+  static auto meth = new BIO_METHOD{
       BIO_TYPE_FD,    "nghttpx-bio",    shrpx_bio_write,
       shrpx_bio_read, shrpx_bio_puts,   shrpx_bio_gets,
       shrpx_bio_ctrl, shrpx_bio_create, shrpx_bio_destroy,
   };
 
-  return &shrpx_bio_method;
+  return meth;
 }
 
-void delete_bio_method(BIO_METHOD *bio_method) {}
-
 #endif // !OPENSSL_1_1_API
 
 void Connection::set_ssl(SSL *ssl) {
@@ -319,7 +316,7 @@ void Connection::set_ssl(SSL *ssl) {
 namespace {
 // We should buffer at least full encrypted TLS record here.
 // Theoretically, peer can send client hello in several TLS records,
-// which could exeed this limit, but it is not portable, and we don't
+// which could exceed this limit, but it is not portable, and we don't
 // have to handle such exotic behaviour.
 bool read_buffer_full(DefaultPeekMemchunks &rbuf) {
   return rbuf.rleft_buffered() >= 20_k;
@@ -364,7 +361,7 @@ int Connection::tls_handshake() {
     auto ssl_opts = SSL_get_options(tls.ssl);
     SSL_free(tls.ssl);
 
-    auto ssl = ssl::create_ssl(ssl_ctx);
+    auto ssl = tls::create_ssl(ssl_ctx);
     if (!ssl) {
       return -1;
     }
@@ -384,6 +381,8 @@ int Connection::tls_handshake() {
     break;
   }
 
+  ERR_clear_error();
+
   auto rv = SSL_do_handshake(tls.ssl);
 
   if (rv <= 0) {
@@ -506,8 +505,8 @@ int Connection::write_tls_pending_handshake() {
 
   if (LOG_ENABLED(INFO)) {
     LOG(INFO) << "SSL/TLS handshake completed";
-    nghttp2::ssl::TLSSessionInfo tls_info{};
-    if (nghttp2::ssl::get_tls_session_info(&tls_info, tls.ssl)) {
+    nghttp2::tls::TLSSessionInfo tls_info{};
+    if (nghttp2::tls::get_tls_session_info(&tls_info, tls.ssl)) {
       LOG(INFO) << "cipher=" << tls_info.cipher
                 << " protocol=" << tls_info.protocol
                 << " resumption=" << (tls_info.session_reused ? "yes" : "no")
@@ -534,7 +533,7 @@ int Connection::check_http2_requirement() {
       !util::check_h2_is_selected(StringRef{next_proto, next_proto_len})) {
     return 0;
   }
-  if (!nghttp2::ssl::check_http2_tls_version(tls.ssl)) {
+  if (!nghttp2::tls::check_http2_tls_version(tls.ssl)) {
     if (LOG_ENABLED(INFO)) {
       LOG(INFO) << "TLSv1.2 was not negotiated.  HTTP/2 must not be used.";
     }
@@ -549,7 +548,7 @@ int Connection::check_http2_requirement() {
   }
 
   if (check_black_list &&
-      nghttp2::ssl::check_http2_cipher_black_list(tls.ssl)) {
+      nghttp2::tls::check_http2_cipher_black_list(tls.ssl)) {
     if (LOG_ENABLED(INFO)) {
       LOG(INFO) << "The negotiated cipher suite is in HTTP/2 cipher suite "
                    "black list.  HTTP/2 must not be used.";
@@ -561,7 +560,7 @@ int Connection::check_http2_requirement() {
 }
 
 namespace {
-const size_t SHRPX_SMALL_WRITE_LIMIT = 1300;
+constexpr size_t SHRPX_SMALL_WRITE_LIMIT = 1300;
 } // namespace
 
 size_t Connection::get_tls_write_limit() {
@@ -618,6 +617,8 @@ ssize_t Connection::write_tls(const void *data, size_t len) {
 
   tls.last_write_idle = -1.;
 
+  ERR_clear_error();
+
   auto rv = SSL_write(tls.ssl, data, len);
 
   if (rv <= 0) {
@@ -670,6 +671,8 @@ ssize_t Connection::read_tls(void *data, size_t len) {
     tls.last_readlen = 0;
   }
 
+  ERR_clear_error();
+
   auto rv = SSL_read(tls.ssl, data, len);
 
   if (rv <= 0) {
@@ -721,6 +724,10 @@ ssize_t Connection::write_clear(const void *data, size_t len) {
 
   wlimit.drain(nwrite);
 
+  if (ev_is_active(&wt)) {
+    ev_timer_again(loop, &wt);
+  }
+
   return nwrite;
 }
 
@@ -744,6 +751,10 @@ ssize_t Connection::writev_clear(struct iovec *iov, int iovcnt) {
 
   wlimit.drain(nwrite);
 
+  if (ev_is_active(&wt)) {
+    ev_timer_again(loop, &wt);
+  }
+
   return nwrite;
 }
 
@@ -796,11 +807,25 @@ int Connection::get_tcp_hint(TCPHint *hint) const {
                            : 0;
 
   // http://www.slideshare.net/kazuho/programming-tcp-for-responsiveness
+
+  // TODO 29 (5 (header) + 8 (explicit nonce) + 16 (tag)) is TLS
+  // overhead for AES-GCM.  For CHACHA20_POLY1305, it is 21 since it
+  // does not need 8 bytes explicit nonce.
   //
-  // TODO 29 (5 + 8 + 16) is TLS overhead for AES-GCM.  For
-  // CHACHA20_POLY1305, it is 21 since it does not need 8 bytes
-  // explicit nonce.
-  auto writable_size = (avail_packets + 2) * (tcp_info.tcpi_snd_mss - 29);
+  // For TLSv1.3, AES-GCM and CHACHA20_POLY1305 overhead are now 22
+  // bytes (5 (header) + 1 (ContentType) + 16 (tag)).
+  size_t tls_overhead;
+#ifdef TLS1_3_VERSION
+  if (SSL_version(tls.ssl) == TLS1_3_VERSION) {
+    tls_overhead = 22;
+  } else
+#endif // TLS1_3_VERSION
+  {
+    tls_overhead = 29;
+  }
+
+  auto writable_size =
+      (avail_packets + 2) * (tcp_info.tcpi_snd_mss - tls_overhead);
   if (writable_size > 16_k) {
     writable_size = writable_size & ~(16_k - 1);
   } else {
@@ -808,7 +833,7 @@ int Connection::get_tcp_hint(TCPHint *hint) const {
       LOG(INFO) << "writable_size is too small: " << writable_size;
     }
     // TODO is this required?
-    writable_size = std::max(writable_size, static_cast<uint32_t>(536 * 2));
+    writable_size = std::max(writable_size, static_cast<size_t>(536 * 2));
   }
 
   // if (LOG_ENABLED(INFO)) {
index 990a85e..441ff51 100644 (file)
@@ -41,6 +41,10 @@ namespace shrpx {
 
 struct MemcachedRequest;
 
+namespace tls {
+struct TLSSessionCache;
+} // namespace tls
+
 enum {
   TLS_CONN_NORMAL,
   TLS_CONN_WAIT_FOR_SESSION_CACHE,
@@ -55,6 +59,7 @@ struct TLSConnection {
   SSL *ssl;
   SSL_SESSION *cached_session;
   MemcachedRequest *cached_session_lookup_req;
+  tls::TLSSessionCache *client_session_cache;
   ev_tstamp last_write_idle;
   size_t warmup_writelen;
   // length passed to SSL_write and SSL_read last time.  This is
@@ -66,6 +71,9 @@ struct TLSConnection {
   bool reneg_started;
   // true if ssl is prepared to do handshake as server.
   bool server_handshake;
+  // true if ssl is initialized as server, and client requested
+  // signed_certificate_timestamp extension.
+  bool sct_requested;
 };
 
 struct TCPHint {
@@ -154,22 +162,16 @@ struct Connection {
   // used in this object at the moment.  The rest of the program may
   // use this value when it is useful.
   shrpx_proto proto;
-  // The point of time when last read is observed.  Note: sinde we use
+  // The point of time when last read is observed.  Note: since we use
   // |rt| as idle timer, the activity is not limited to read.
   ev_tstamp last_read;
   // Timeout for read timer |rt|.
   ev_tstamp read_timeout;
 };
 
-// Creates BIO_method shared by all SSL objects.  If nghttp2 is built
-// with OpenSSL < 1.1.0, this returns statically allocated object.
-// Otherwise, it returns new BIO_METHOD object every time.
+// Creates BIO_method shared by all SSL objects.
 BIO_METHOD *create_bio_method();
 
-// Deletes given |bio_method|.  If nghttp2 is built with OpenSSL <
-// 1.1.0, this function is noop.
-void delete_bio_method(BIO_METHOD *bio_method);
-
 } // namespace shrpx
 
 #endif // SHRPX_CONNECTION_H
index 6039313..19985aa 100644 (file)
@@ -35,7 +35,7 @@
 #include <random>
 
 #include "shrpx_client_handler.h"
-#include "shrpx_ssl.h"
+#include "shrpx_tls.h"
 #include "shrpx_worker.h"
 #include "shrpx_config.h"
 #include "shrpx_http2_session.h"
@@ -118,7 +118,8 @@ ConnectionHandler::ConnectionHandler(struct ev_loop *loop, std::mt19937 &gen)
       tls_ticket_key_memcached_get_retry_count_(0),
       tls_ticket_key_memcached_fail_count_(0),
       worker_round_robin_cnt_(get_config()->api.enabled ? 1 : 0),
-      graceful_shutdown_(false) {
+      graceful_shutdown_(false),
+      enable_acceptor_on_ocsp_completion_(false) {
   ev_timer_init(&disable_acceptor_timer_, acceptor_disable_cb, 0., 0.);
   disable_acceptor_timer_.data = this;
 
@@ -154,7 +155,7 @@ ConnectionHandler::~ConnectionHandler() {
 
   for (auto ssl_ctx : all_ssl_ctx_) {
     auto tls_ctx_data =
-        static_cast<ssl::TLSContextData *>(SSL_CTX_get_app_data(ssl_ctx));
+        static_cast<tls::TLSContextData *>(SSL_CTX_get_app_data(ssl_ctx));
     if (tls_ctx_data) {
       delete tls_ctx_data;
     }
@@ -199,19 +200,19 @@ void ConnectionHandler::worker_replace_downstream(
 }
 
 int ConnectionHandler::create_single_worker() {
-  cert_tree_ = ssl::create_cert_lookup_tree();
-  auto sv_ssl_ctx = ssl::setup_server_ssl_context(
+  cert_tree_ = tls::create_cert_lookup_tree();
+  auto sv_ssl_ctx = tls::setup_server_ssl_context(
       all_ssl_ctx_, indexed_ssl_ctx_, cert_tree_.get()
 #ifdef HAVE_NEVERBLEED
                                           ,
       nb_.get()
 #endif // HAVE_NEVERBLEED
-          );
-  auto cl_ssl_ctx = ssl::setup_downstream_client_ssl_context(
+  );
+  auto cl_ssl_ctx = tls::setup_downstream_client_ssl_context(
 #ifdef HAVE_NEVERBLEED
       nb_.get()
 #endif // HAVE_NEVERBLEED
-          );
+  );
 
   if (cl_ssl_ctx) {
     all_ssl_ctx_.push_back(cl_ssl_ctx);
@@ -219,17 +220,19 @@ int ConnectionHandler::create_single_worker() {
 
   auto config = get_config();
   auto &tlsconf = config->tls;
-  auto &memcachedconf = config->tls.session_cache.memcached;
 
   SSL_CTX *session_cache_ssl_ctx = nullptr;
-  if (memcachedconf.tls) {
-    session_cache_ssl_ctx = ssl::create_ssl_client_context(
+  {
+    auto &memcachedconf = config->tls.session_cache.memcached;
+    if (memcachedconf.tls) {
+      session_cache_ssl_ctx = tls::create_ssl_client_context(
 #ifdef HAVE_NEVERBLEED
-        nb_.get(),
+          nb_.get(),
 #endif // HAVE_NEVERBLEED
-        tlsconf.cacert, memcachedconf.cert_file, memcachedconf.private_key_file,
-        nullptr);
-    all_ssl_ctx_.push_back(session_cache_ssl_ctx);
+          tlsconf.cacert, memcachedconf.cert_file,
+          memcachedconf.private_key_file, nullptr);
+      all_ssl_ctx_.push_back(session_cache_ssl_ctx);
+    }
   }
 
   single_worker_ = make_unique<Worker>(
@@ -248,19 +251,19 @@ int ConnectionHandler::create_worker_thread(size_t num) {
 #ifndef NOTHREADS
   assert(workers_.size() == 0);
 
-  cert_tree_ = ssl::create_cert_lookup_tree();
-  auto sv_ssl_ctx = ssl::setup_server_ssl_context(
+  cert_tree_ = tls::create_cert_lookup_tree();
+  auto sv_ssl_ctx = tls::setup_server_ssl_context(
       all_ssl_ctx_, indexed_ssl_ctx_, cert_tree_.get()
 #ifdef HAVE_NEVERBLEED
                                           ,
       nb_.get()
 #endif // HAVE_NEVERBLEED
-          );
-  auto cl_ssl_ctx = ssl::setup_downstream_client_ssl_context(
+  );
+  auto cl_ssl_ctx = tls::setup_downstream_client_ssl_context(
 #ifdef HAVE_NEVERBLEED
       nb_.get()
 #endif // HAVE_NEVERBLEED
-          );
+  );
 
   if (cl_ssl_ctx) {
     all_ssl_ctx_.push_back(cl_ssl_ctx);
@@ -268,7 +271,6 @@ int ConnectionHandler::create_worker_thread(size_t num) {
 
   auto config = get_config();
   auto &tlsconf = config->tls;
-  auto &memcachedconf = config->tls.session_cache.memcached;
   auto &apiconf = config->api;
 
   // We have dedicated worker for API request processing.
@@ -276,12 +278,12 @@ int ConnectionHandler::create_worker_thread(size_t num) {
     ++num;
   }
 
-  for (size_t i = 0; i < num; ++i) {
-    auto loop = ev_loop_new(config->ev_loop_flags);
+  SSL_CTX *session_cache_ssl_ctx = nullptr;
+  {
+    auto &memcachedconf = config->tls.session_cache.memcached;
 
-    SSL_CTX *session_cache_ssl_ctx = nullptr;
     if (memcachedconf.tls) {
-      session_cache_ssl_ctx = ssl::create_ssl_client_context(
+      session_cache_ssl_ctx = tls::create_ssl_client_context(
 #ifdef HAVE_NEVERBLEED
           nb_.get(),
 #endif // HAVE_NEVERBLEED
@@ -289,6 +291,11 @@ int ConnectionHandler::create_worker_thread(size_t num) {
           memcachedconf.private_key_file, nullptr);
       all_ssl_ctx_.push_back(session_cache_ssl_ctx);
     }
+  }
+
+  for (size_t i = 0; i < num; ++i) {
+    auto loop = ev_loop_new(config->ev_loop_flags);
+
     auto worker = make_unique<Worker>(
         loop, sv_ssl_ctx, cl_ssl_ctx, session_cache_ssl_ctx, cert_tree_.get(),
         ticket_keys_, this, config->conn.downstream);
@@ -384,7 +391,7 @@ int ConnectionHandler::handle_connection(int fd, sockaddr *addr, int addrlen,
     }
 
     auto client =
-        ssl::accept_connection(single_worker_.get(), fd, addr, addrlen, faddr);
+        tls::accept_connection(single_worker_.get(), fd, addr, addrlen, faddr);
     if (!client) {
       LLOG(ERROR, this) << "ClientHandler creation failed";
 
@@ -445,6 +452,8 @@ void ConnectionHandler::add_acceptor(std::unique_ptr<AcceptHandler> h) {
   acceptors_.push_back(std::move(h));
 }
 
+void ConnectionHandler::delete_acceptor() { acceptors_.clear(); }
+
 void ConnectionHandler::enable_acceptor() {
   for (auto &a : acceptors_) {
     a->enable();
@@ -498,6 +507,9 @@ bool ConnectionHandler::get_graceful_shutdown() const {
 }
 
 void ConnectionHandler::cancel_ocsp_update() {
+  enable_acceptor_on_ocsp_completion_ = false;
+  ev_timer_stop(loop_, &ocsp_timer_);
+
   if (ocsp_.proc.pid == 0) {
     return;
   }
@@ -592,14 +604,14 @@ void ConnectionHandler::handle_ocsp_complete() {
 
   auto ssl_ctx = all_ssl_ctx_[ocsp_.next];
   auto tls_ctx_data =
-      static_cast<ssl::TLSContextData *>(SSL_CTX_get_app_data(ssl_ctx));
+      static_cast<tls::TLSContextData *>(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;
+              << " failed: error=" << ocsp_.error << ", rstatus=" << std::hex
+              << rstatus << std::dec << ", status=" << status;
     ++ocsp_.next;
     proceed_next_cert_ocsp();
     return;
@@ -610,8 +622,13 @@ void ConnectionHandler::handle_ocsp_complete() {
               << " finished successfully";
   }
 
+  auto config = get_config();
+  auto &tlsconf = config->tls;
+
+  if (tlsconf.ocsp.no_verify ||
+      tls::verify_ocsp_response(ssl_ctx, ocsp_.resp.data(),
+                                ocsp_.resp.size()) == 0) {
 #ifndef OPENSSL_IS_BORINGSSL
-  {
 #ifdef HAVE_ATOMIC_STD_SHARED_PTR
     std::atomic_store_explicit(
         &tls_ctx_data->ocsp_data,
@@ -622,10 +639,10 @@ void ConnectionHandler::handle_ocsp_complete() {
     tls_ctx_data->ocsp_data =
         std::make_shared<std::vector<uint8_t>>(std::move(ocsp_.resp));
 #endif // !HAVE_ATOMIC_STD_SHARED_PTR
-  }
 #else  // OPENSSL_IS_BORINGSSL
-  SSL_CTX_set_ocsp_response(ssl_ctx, ocsp_.resp.data(), ocsp_.resp.size());
+    SSL_CTX_set_ocsp_response(ssl_ctx, ocsp_.resp.data(), ocsp_.resp.size());
 #endif // OPENSSL_IS_BORINGSSL
+  }
 
   ++ocsp_.next;
   proceed_next_cert_ocsp();
@@ -650,12 +667,18 @@ void ConnectionHandler::proceed_next_cert_ocsp() {
       // We have updated all ocsp response, and schedule next update.
       ev_timer_set(&ocsp_timer_, get_config()->tls.ocsp.update_interval, 0.);
       ev_timer_start(loop_, &ocsp_timer_);
+
+      if (enable_acceptor_on_ocsp_completion_) {
+        enable_acceptor_on_ocsp_completion_ = false;
+        enable_acceptor();
+      }
+
       return;
     }
 
     auto ssl_ctx = all_ssl_ctx_[ocsp_.next];
     auto tls_ctx_data =
-        static_cast<ssl::TLSContextData *>(SSL_CTX_get_app_data(ssl_ctx));
+        static_cast<tls::TLSContextData *>(SSL_CTX_get_app_data(ssl_ctx));
 
     // client SSL_CTX is also included in all_ssl_ctx_, but has no
     // tls_ctx_data.
@@ -777,7 +800,7 @@ SSL_CTX *ConnectionHandler::create_tls_ticket_key_memcached_ssl_ctx() {
   auto &tlsconf = config->tls;
   auto &memcachedconf = config->tls.ticket.memcached;
 
-  auto ssl_ctx = ssl::create_ssl_client_context(
+  auto ssl_ctx = tls::create_ssl_client_context(
 #ifdef HAVE_NEVERBLEED
       nb_.get(),
 #endif // HAVE_NEVERBLEED
@@ -849,4 +872,8 @@ ConnectionHandler::get_indexed_ssl_ctx(size_t idx) const {
   return indexed_ssl_ctx_[idx];
 }
 
+void ConnectionHandler::set_enable_acceptor_on_ocsp_completion(bool f) {
+  enable_acceptor_on_ocsp_completion_ = f;
+}
+
 } // namespace shrpx
index dab4180..4c5ce74 100644 (file)
@@ -63,11 +63,11 @@ struct TicketKeys;
 class MemcachedDispatcher;
 struct UpstreamAddr;
 
-namespace ssl {
+namespace tls {
 
 class CertLookupTree;
 
-} // namespace ssl
+} // namespace tls
 
 struct OCSPUpdateContext {
   // ocsp response buffer
@@ -117,6 +117,7 @@ public:
   struct ev_loop *get_loop() const;
   Worker *get_single_worker() const;
   void add_acceptor(std::unique_ptr<AcceptHandler> h);
+  void delete_acceptor();
   void enable_acceptor();
   void disable_acceptor();
   void sleep_acceptor(ev_tstamp t);
@@ -174,6 +175,8 @@ public:
   void
   worker_replace_downstream(std::shared_ptr<DownstreamConfig> downstreamconf);
 
+  void set_enable_acceptor_on_ocsp_completion(bool f);
+
 private:
   // Stores all SSL_CTX objects.
   std::vector<SSL_CTX *> all_ssl_ctx_;
@@ -184,7 +187,7 @@ private:
   // and signature algorithm presented by client.
   std::vector<std::vector<SSL_CTX *>> indexed_ssl_ctx_;
   OCSPUpdateContext ocsp_;
-  std::mt19937 gen_;
+  std::mt19937 &gen_;
   // ev_loop for each worker
   std::vector<struct ev_loop *> worker_loops_;
   // Worker instances when multi threaded mode (-nN, N >= 2) is used.
@@ -198,7 +201,7 @@ private:
   // Worker instance used when single threaded mode (-n1) is used.
   // Otherwise, nullptr and workers_ has instances of Worker instead.
   std::unique_ptr<Worker> single_worker_;
-  std::unique_ptr<ssl::CertLookupTree> cert_tree_;
+  std::unique_ptr<tls::CertLookupTree> cert_tree_;
   std::unique_ptr<MemcachedDispatcher> tls_ticket_key_memcached_dispatcher_;
   // Current TLS session ticket keys.  Note that TLS connection does
   // not refer to this field directly.  They use TicketKeys object in
@@ -220,6 +223,9 @@ private:
   size_t tls_ticket_key_memcached_fail_count_;
   unsigned int worker_round_robin_cnt_;
   bool graceful_shutdown_;
+  // true if acceptors should be enabled after the initial ocsp update
+  // has finished.
+  bool enable_acceptor_on_ocsp_completion_;
 };
 
 } // namespace shrpx
index 356fd97..cb77e60 100644 (file)
@@ -204,6 +204,7 @@ int DNSResolver::handle_event(int rfd, int wfd) {
   default:
     // Unreachable
     assert(0);
+    abort();
   }
 }
 
index 9eeca5d..1ab368e 100644 (file)
@@ -250,6 +250,7 @@ int DNSTracker::resolve(Address *result, DNSQuery *dnsq) {
     return DNS_STATUS_OK;
   default:
     assert(0);
+    abort();
   }
 }
 
index afeee3e..360a9a9 100644 (file)
@@ -131,6 +131,7 @@ Downstream::Downstream(Upstream *upstream, MemchunkPool *mcpool,
       assoc_stream_id_(-1),
       downstream_stream_id_(-1),
       response_rst_stream_error_code_(NGHTTP2_NO_ERROR),
+      affinity_cookie_(0),
       request_state_(INITIAL),
       response_state_(INITIAL),
       dispatch_state_(DISPATCH_NONE),
@@ -140,7 +141,8 @@ Downstream::Downstream(Upstream *upstream, MemchunkPool *mcpool,
       expect_final_response_(false),
       request_pending_(false),
       request_header_sent_(false),
-      accesslog_written_(false) {
+      accesslog_written_(false),
+      new_affinity_cookie_(false) {
 
   auto &timeoutconf = get_config()->http2.timeout;
 
@@ -157,6 +159,8 @@ Downstream::Downstream(Upstream *upstream, MemchunkPool *mcpool,
   upstream_wtimer_.data = this;
   downstream_rtimer_.data = this;
   downstream_wtimer_.data = this;
+
+  rcbufs_.reserve(32);
 }
 
 Downstream::~Downstream() {
@@ -303,11 +307,53 @@ StringRef Downstream::assemble_request_cookie() {
   return StringRef{iov.base, p};
 }
 
+uint32_t Downstream::find_affinity_cookie(const StringRef &name) {
+  for (auto &kv : req_.fs.headers()) {
+    if (kv.token != http2::HD_COOKIE) {
+      continue;
+    }
+
+    for (auto it = std::begin(kv.value); it != std::end(kv.value);) {
+      if (*it == '\t' || *it == ' ' || *it == ';') {
+        ++it;
+        continue;
+      }
+
+      auto end = std::find(it, std::end(kv.value), '=');
+      if (end == std::end(kv.value)) {
+        return 0;
+      }
+
+      if (!util::streq(name, StringRef{it, end})) {
+        it = std::find(it, std::end(kv.value), ';');
+        continue;
+      }
+
+      it = std::find(end + 1, std::end(kv.value), ';');
+      auto val = StringRef{end + 1, it};
+      if (val.size() != 8) {
+        return 0;
+      }
+      uint32_t h = 0;
+      for (auto c : val) {
+        auto n = util::hex_to_uint(c);
+        if (n == 256) {
+          return 0;
+        }
+        h <<= 4;
+        h += n;
+      }
+      affinity_cookie_ = h;
+      return h;
+    }
+  }
+  return 0;
+}
+
 size_t Downstream::count_crumble_request_cookie() {
   size_t n = 0;
   for (auto &kv : req_.fs.headers()) {
-    if (kv.name.size() != 6 || kv.name[5] != 'e' ||
-        !util::streq_l("cooki", kv.name.c_str(), 5)) {
+    if (kv.token != http2::HD_COOKIE) {
       continue;
     }
 
@@ -327,8 +373,7 @@ size_t Downstream::count_crumble_request_cookie() {
 
 void Downstream::crumble_request_cookie(std::vector<nghttp2_nv> &nva) {
   for (auto &kv : req_.fs.headers()) {
-    if (kv.name.size() != 6 || kv.name[5] != 'e' ||
-        !util::streq_l("cooki", kv.name.c_str(), 5)) {
+    if (kv.token != http2::HD_COOKIE) {
       continue;
     }
 
@@ -741,6 +786,10 @@ bool Downstream::get_non_final_response() const {
   return !upgraded_ && resp_.http_status / 100 == 1;
 }
 
+bool Downstream::supports_non_final_response() const {
+  return req_.http_major == 2 || (req_.http_major == 1 && req_.http_minor == 1);
+}
+
 bool Downstream::get_upgraded() const { return upgraded_; }
 
 bool Downstream::get_http2_upgrade_request() const {
@@ -991,4 +1040,16 @@ const DownstreamAddr *Downstream::get_addr() const { return addr_; }
 
 void Downstream::set_accesslog_written(bool f) { accesslog_written_ = f; }
 
+void Downstream::renew_affinity_cookie(uint32_t h) {
+  affinity_cookie_ = h;
+  new_affinity_cookie_ = true;
+}
+
+uint32_t Downstream::get_affinity_cookie_to_send() const {
+  if (new_affinity_cookie_) {
+    return affinity_cookie_;
+  }
+  return 0;
+}
+
 } // namespace shrpx
index 28c177d..c81fcf6 100644 (file)
@@ -32,6 +32,7 @@
 #include <string>
 #include <memory>
 #include <chrono>
+#include <algorithm>
 
 #include <ev.h>
 
@@ -207,7 +208,40 @@ struct Response {
     unconsumed_body_length -= len;
   }
 
+  // returns true if a resource denoted by scheme, authority, and path
+  // has already been pushed.
+  bool is_resource_pushed(const StringRef &scheme, const StringRef &authority,
+                          const StringRef &path) const {
+    if (!pushed_resources) {
+      return false;
+    }
+    return std::find(std::begin(*pushed_resources), std::end(*pushed_resources),
+                     std::make_tuple(scheme, authority, path)) !=
+           std::end(*pushed_resources);
+  }
+
+  // remember that a resource denoted by scheme, authority, and path
+  // is pushed.
+  void resource_pushed(const StringRef &scheme, const StringRef &authority,
+                       const StringRef &path) {
+    if (!pushed_resources) {
+      pushed_resources = make_unique<
+          std::vector<std::tuple<StringRef, StringRef, StringRef>>>();
+    }
+    pushed_resources->emplace_back(scheme, authority, path);
+  }
+
   FieldStore fs;
+  // array of the tuple of scheme, authority, and path of pushed
+  // resource.  This is required because RFC 8297 says that server
+  // typically includes header fields appeared in non-final response
+  // header fields in final response header fields.  Without checking
+  // that a particular resource has already been pushed, or not, we
+  // end up pushing the same resource at least twice.  It is unknown
+  // that we should use more complex data structure (e.g., std::set)
+  // to find the resources faster.
+  std::unique_ptr<std::vector<std::tuple<StringRef, StringRef, StringRef>>>
+      pushed_resources;
   // the length of response body received so far
   int64_t recv_body_length;
   // The number of bytes not consumed by the application yet.  This is
@@ -252,7 +286,7 @@ public:
   // Returns true if upgrade (HTTP Upgrade or CONNECT) is succeeded.
   // This should not depend on inspect_http1_response().
   void check_upgrade_fulfilled();
-  // Returns true if the upgrade is succeded as a result of the call
+  // Returns true if the upgrade is succeeded as a result of the call
   // check_upgrade_fulfilled().  HTTP/2 Upgrade is excluded.
   bool get_upgraded() const;
   // Inspects HTTP/2 request.
@@ -348,6 +382,9 @@ public:
   // True if the response is non-final (1xx status code).  Note that
   // if connection was upgraded, 101 status code is treated as final.
   bool get_non_final_response() const;
+  // True if protocol version used by client supports non final
+  // response.  Only HTTP/1.1 and HTTP/2 clients support it.
+  bool supports_non_final_response() const;
   void set_expect_final_response(bool f);
   bool get_expect_final_response() const;
 
@@ -409,6 +446,18 @@ public:
 
   void set_accesslog_written(bool f);
 
+  // Finds affinity cookie from request header fields.  The name of
+  // cookie is given in |name|.  If an affinity cookie is found, it is
+  // assigned to a member function, and is returned.  If it is not
+  // found, or is malformed, returns 0.
+  uint32_t find_affinity_cookie(const StringRef &name);
+  // Set |h| as affinity cookie.
+  void renew_affinity_cookie(uint32_t h);
+  // Returns affinity cookie to send.  If it does not need to be sent,
+  // for example, because the value is retrieved from a request header
+  // field, returns 0.
+  uint32_t get_affinity_cookie_to_send() const;
+
   enum {
     EVENT_ERROR = 0x1,
     EVENT_TIMEOUT = 0x2,
@@ -454,7 +503,7 @@ private:
   Upstream *upstream_;
   std::unique_ptr<DownstreamConnection> dconn_;
 
-  // only used by HTTP/2 or SPDY upstream
+  // only used by HTTP/2 upstream
   BlockedLink *blocked_link_;
   // The backend address used to fulfill this request.  These are for
   // logging purpose.
@@ -471,11 +520,13 @@ private:
   int32_t downstream_stream_id_;
   // RST_STREAM error_code from downstream HTTP2 connection
   uint32_t response_rst_stream_error_code_;
+  // An affinity cookie value.
+  uint32_t affinity_cookie_;
   // request state
   int request_state_;
   // response state
   int response_state_;
-  // only used by HTTP/2 or SPDY upstream
+  // only used by HTTP/2 upstream
   int dispatch_state_;
   // true if the connection is upgraded (HTTP Upgrade or CONNECT),
   // excluding upgrade to HTTP/2.
@@ -494,6 +545,8 @@ private:
   bool request_header_sent_;
   // true if access.log has been written.
   bool accesslog_written_;
+  // true if affinity cookie is generated for this request.
+  bool new_affinity_cookie_;
 };
 
 } // namespace shrpx
index f7f66b7..7446539 100644 (file)
@@ -158,6 +158,8 @@ Downstream *DownstreamQueue::remove_and_get_blocked(Downstream *downstream,
 
   auto next_downstream = link->downstream;
   auto link2 = next_downstream->detach_blocked_link();
+  // This is required with --disable-assert.
+  (void)link2;
   assert(link2 == link);
   ent.blocked.remove(link);
   delete link;
index 7789530..9851443 100644 (file)
@@ -164,4 +164,63 @@ void test_downstream_rewrite_location_response_header(void) {
   CU_ASSERT("https://localhost:8443/" == (*location).value);
 }
 
+void test_downstream_supports_non_final_response(void) {
+  Downstream d(nullptr, nullptr, 0);
+  auto &req = d.request();
+
+  req.http_major = 2;
+  req.http_minor = 0;
+
+  CU_ASSERT(d.supports_non_final_response());
+
+  req.http_major = 1;
+  req.http_minor = 1;
+
+  CU_ASSERT(d.supports_non_final_response());
+
+  req.http_major = 1;
+  req.http_minor = 0;
+
+  CU_ASSERT(!d.supports_non_final_response());
+
+  req.http_major = 0;
+  req.http_minor = 9;
+
+  CU_ASSERT(!d.supports_non_final_response());
+}
+
+void test_downstream_find_affinity_cookie(void) {
+  Downstream d(nullptr, nullptr, 0);
+
+  auto &req = d.request();
+  req.fs.add_header_token(StringRef::from_lit("cookie"), StringRef{}, false,
+                          http2::HD_COOKIE);
+  req.fs.add_header_token(StringRef::from_lit("cookie"),
+                          StringRef::from_lit("a=b;;c=d"), false,
+                          http2::HD_COOKIE);
+  req.fs.add_header_token(StringRef::from_lit("content-length"),
+                          StringRef::from_lit("599"), false,
+                          http2::HD_CONTENT_LENGTH);
+  req.fs.add_header_token(StringRef::from_lit("cookie"),
+                          StringRef::from_lit("lb=deadbeef;LB=f1f2f3f4"), false,
+                          http2::HD_COOKIE);
+  req.fs.add_header_token(StringRef::from_lit("cookie"),
+                          StringRef::from_lit("short=e1e2e3e"), false,
+                          http2::HD_COOKIE);
+
+  uint32_t aff;
+
+  aff = d.find_affinity_cookie(StringRef::from_lit("lb"));
+
+  CU_ASSERT(0xdeadbeef == aff);
+
+  aff = d.find_affinity_cookie(StringRef::from_lit("LB"));
+
+  CU_ASSERT(0xf1f2f3f4 == aff);
+
+  aff = d.find_affinity_cookie(StringRef::from_lit("short"));
+
+  CU_ASSERT(0 == aff);
+}
+
 } // namespace shrpx
index f7b1e60..d9153ee 100644 (file)
@@ -36,6 +36,8 @@ void test_downstream_field_store_header(void);
 void test_downstream_crumble_request_cookie(void);
 void test_downstream_assemble_request_cookie(void);
 void test_downstream_rewrite_location_response_header(void);
+void test_downstream_supports_non_final_response(void);
+void test_downstream_find_affinity_cookie(void);
 
 } // namespace shrpx
 
index a93ba3f..d5cd091 100644 (file)
@@ -76,14 +76,17 @@ int exec_read_command(Process &proc, char *const argv[]) {
   auto pid = fork();
 
   if (pid == 0) {
+    // This is multithreaded program, and we are allowed to use only
+    // async-signal-safe functions here.
+
     // child process
     shrpx_signal_unset_worker_proc_ign_handler();
 
     rv = shrpx_signal_unblock_all();
     if (rv != 0) {
-      auto error = errno;
-      LOG(FATAL) << "Unblocking all signals failed: errno=" << error;
-
+      static constexpr char msg[] = "Unblocking all signals failed\n";
+      while (write(STDERR_FILENO, msg, str_size(msg)) == -1 && errno == EINTR)
+        ;
       nghttp2_Exit(EXIT_FAILURE);
     }
 
@@ -92,9 +95,9 @@ int exec_read_command(Process &proc, char *const argv[]) {
 
     rv = execv(argv[0], argv);
     if (rv == -1) {
-      auto error = errno;
-      LOG(ERROR) << "Could not execute command: " << argv[0]
-                 << ", execve() faild, errno=" << error;
+      static constexpr char msg[] = "Could not execute command\n";
+      while (write(STDERR_FILENO, msg, str_size(msg)) == -1 && errno == EINTR)
+        ;
       nghttp2_Exit(EXIT_FAILURE);
     }
     // unreachable
index c7ade2d..bc860b0 100644 (file)
@@ -164,6 +164,53 @@ ssize_t select_padding_callback(nghttp2_session *session,
   return std::min(max_payload, frame->hd.length + get_config()->padding);
 }
 
+StringRef create_affinity_cookie(BlockAllocator &balloc, const StringRef &name,
+                                 uint32_t affinity_cookie,
+                                 const StringRef &path, bool secure) {
+  static constexpr auto PATH_PREFIX = StringRef::from_lit("; Path=");
+  static constexpr auto SECURE = StringRef::from_lit("; Secure");
+  // <name>=<value>[; Path=<path>][; Secure]
+  size_t len = name.size() + 1 + 8;
+
+  if (!path.empty()) {
+    len += PATH_PREFIX.size() + path.size();
+  }
+  if (secure) {
+    len += SECURE.size();
+  }
+
+  auto iov = make_byte_ref(balloc, len + 1);
+  auto p = iov.base;
+  p = std::copy(std::begin(name), std::end(name), p);
+  *p++ = '=';
+  affinity_cookie = htonl(affinity_cookie);
+  p = util::format_hex(p,
+                       StringRef{reinterpret_cast<uint8_t *>(&affinity_cookie),
+                                 reinterpret_cast<uint8_t *>(&affinity_cookie) +
+                                     sizeof(affinity_cookie)});
+  if (!path.empty()) {
+    p = std::copy(std::begin(PATH_PREFIX), std::end(PATH_PREFIX), p);
+    p = std::copy(std::begin(path), std::end(path), p);
+  }
+  if (secure) {
+    p = std::copy(std::begin(SECURE), std::end(SECURE), p);
+  }
+  *p = '\0';
+  return StringRef{iov.base, p};
+}
+
+bool require_cookie_secure_attribute(shrpx_cookie_secure secure,
+                                     const StringRef &scheme) {
+  switch (secure) {
+  case COOKIE_SECURE_AUTO:
+    return scheme == "https";
+  case COOKIE_SECURE_YES:
+    return true;
+  default:
+    return false;
+  }
+}
+
 } // namespace http
 
 } // namespace shrpx
index 2833f60..dfa5477 100644 (file)
@@ -31,6 +31,7 @@
 
 #include <nghttp2/nghttp2.h>
 
+#include "shrpx_config.h"
 #include "util.h"
 #include "allocator.h"
 
@@ -66,6 +67,18 @@ ssize_t select_padding_callback(nghttp2_session *session,
                                 const nghttp2_frame *frame, size_t max_payload,
                                 void *user_data);
 
+// Creates set-cookie-string for cookie based affinity.  If |path| is
+// not empty, "; <path>" is added.  If |secure| is true, "; Secure" is
+// added.
+StringRef create_affinity_cookie(BlockAllocator &balloc, const StringRef &name,
+                                 uint32_t affinity_cookie,
+                                 const StringRef &path, bool secure);
+
+// Returns true if |secure| indicates that Secure attribute should be
+// set.
+bool require_cookie_secure_attribute(shrpx_cookie_secure secure,
+                                     const StringRef &scheme);
+
 } // namespace http
 
 } // namespace shrpx
index 019f3f9..70a4a05 100644 (file)
@@ -201,9 +201,7 @@ ssize_t http2_data_read_callback(nghttp2_session *session, int32_t stream_id,
     if (!trailers.empty()) {
       std::vector<nghttp2_nv> nva;
       nva.reserve(trailers.size());
-      // We cannot use nocopy version, since nva may be touched after
-      // Downstream object is deleted.
-      http2::copy_headers_to_nva(nva, trailers);
+      http2::copy_headers_to_nva_nocopy(nva, trailers, http2::HDOP_STRIP_ALL);
       if (!nva.empty()) {
         rv = nghttp2_submit_trailer(session, stream_id, nva.data(), nva.size());
         if (rv != 0) {
@@ -293,7 +291,14 @@ int Http2DownstreamConnection::push_request_headers() {
   if (req.method != HTTP_CONNECT) {
     assert(!req.scheme.empty());
 
-    nva.push_back(http2::make_nv_ls_nocopy(":scheme", req.scheme));
+    auto addr = http2session_->get_addr();
+    assert(addr);
+    // We will handle more protocol scheme upgrade in the future.
+    if (addr->tls && addr->upgrade_scheme && req.scheme == "http") {
+      nva.push_back(http2::make_nv_ll(":scheme", "https"));
+    } else {
+      nva.push_back(http2::make_nv_ls_nocopy(":scheme", req.scheme));
+    }
 
     if (req.method == HTTP_OPTIONS && req.path.empty()) {
       nva.push_back(http2::make_nv_ll(":path", "*"));
@@ -310,7 +315,16 @@ int Http2DownstreamConnection::push_request_headers() {
     nva.push_back(http2::make_nv_ls_nocopy(":authority", authority));
   }
 
-  http2::copy_headers_to_nva_nocopy(nva, req.fs.headers());
+  auto &fwdconf = httpconf.forwarded;
+  auto &xffconf = httpconf.xff;
+  auto &xfpconf = httpconf.xfp;
+
+  uint32_t build_flags =
+      (fwdconf.strip_incoming ? http2::HDOP_STRIP_FORWARDED : 0) |
+      (xffconf.strip_incoming ? http2::HDOP_STRIP_X_FORWARDED_FOR : 0) |
+      (xfpconf.strip_incoming ? http2::HDOP_STRIP_X_FORWARDED_PROTO : 0);
+
+  http2::copy_headers_to_nva_nocopy(nva, req.fs.headers(), build_flags);
 
   if (!http2conf.no_cookie_crumbling) {
     downstream_->crumble_request_cookie(nva);
@@ -319,8 +333,6 @@ int Http2DownstreamConnection::push_request_headers() {
   auto upstream = downstream_->get_upstream();
   auto handler = upstream->get_client_handler();
 
-  auto &fwdconf = httpconf.forwarded;
-
   auto fwd =
       fwdconf.strip_incoming ? nullptr : req.fs.header(http2::HD_FORWARDED);
 
@@ -351,8 +363,6 @@ int Http2DownstreamConnection::push_request_headers() {
     nva.push_back(http2::make_nv_ls_nocopy("forwarded", fwd->value));
   }
 
-  auto &xffconf = httpconf.xff;
-
   auto xff = xffconf.strip_incoming ? nullptr
                                     : req.fs.header(http2::HD_X_FORWARDED_FOR);
 
@@ -371,8 +381,23 @@ int Http2DownstreamConnection::push_request_headers() {
   }
 
   if (!config->http2_proxy && req.method != HTTP_CONNECT) {
-    // We use same protocol with :scheme header field
-    nva.push_back(http2::make_nv_ls_nocopy("x-forwarded-proto", req.scheme));
+    auto xfp = xfpconf.strip_incoming
+                   ? nullptr
+                   : req.fs.header(http2::HD_X_FORWARDED_PROTO);
+
+    if (xfpconf.add) {
+      StringRef xfp_value;
+      // We use same protocol with :scheme header field
+      if (xfp) {
+        xfp_value = concat_string_ref(balloc, xfp->value,
+                                      StringRef::from_lit(", "), req.scheme);
+      } else {
+        xfp_value = req.scheme;
+      }
+      nva.push_back(http2::make_nv_ls_nocopy("x-forwarded-proto", xfp_value));
+    } else if (xfp) {
+      nva.push_back(http2::make_nv_ls_nocopy("x-forwarded-proto", xfp->value));
+    }
   }
 
   auto via = req.fs.header(http2::HD_VIA);
index 00d5afe..61c97ca 100644 (file)
@@ -39,7 +39,7 @@
 #include "shrpx_error.h"
 #include "shrpx_http2_downstream_connection.h"
 #include "shrpx_client_handler.h"
-#include "shrpx_ssl.h"
+#include "shrpx_tls.h"
 #include "shrpx_http.h"
 #include "shrpx_worker.h"
 #include "shrpx_connect_blocker.h"
 #include "http2.h"
 #include "util.h"
 #include "base64.h"
-#include "ssl.h"
+#include "tls.h"
 
 using namespace nghttp2;
 
 namespace shrpx {
 
 namespace {
-const ev_tstamp CONNCHK_TIMEOUT = 5.;
-const ev_tstamp CONNCHK_PING_TIMEOUT = 1.;
+constexpr ev_tstamp CONNCHK_TIMEOUT = 5.;
+constexpr ev_tstamp CONNCHK_PING_TIMEOUT = 1.;
 } // namespace
 
 namespace {
@@ -138,13 +138,6 @@ void readcb(struct ev_loop *loop, ev_io *w, int revents) {
     return;
   }
   http2session->connection_alive();
-
-  rv = http2session->do_write();
-  if (rv != 0) {
-    delete http2session;
-
-    return;
-  }
 }
 } // namespace
 
@@ -277,8 +270,8 @@ int Http2Session::disconnect(bool hard) {
   // When deleting Http2DownstreamConnection, it calls this object's
   // remove_downstream_connection().  The multiple
   // Http2DownstreamConnection objects belong to the same
-  // ClientHandler object if upstream is h2 or SPDY.  So be careful
-  // when you delete ClientHandler here.
+  // ClientHandler object if upstream is h2.  So be careful when you
+  // delete ClientHandler here.
   //
   // We allow creating new pending Http2DownstreamConnection with this
   // object.  Upstream::on_downstream_reset() may add
@@ -341,6 +334,7 @@ int Http2Session::resolve_name() {
     return 0;
   default:
     assert(0);
+    abort();
   }
 }
 
@@ -429,14 +423,15 @@ int Http2Session::initiate_connection() {
       assert(ssl_ctx_);
 
       if (state_ != RESOLVING_NAME) {
-        auto ssl = ssl::create_ssl(ssl_ctx_);
+        auto ssl = tls::create_ssl(ssl_ctx_);
         if (!ssl) {
           return -1;
         }
 
-        ssl::setup_downstream_http2_alpn(ssl);
+        tls::setup_downstream_http2_alpn(ssl);
 
         conn_.set_ssl(ssl);
+        conn_.tls.client_session_cache = &addr_->tls_session_cache;
 
         auto sni_name =
             addr_->sni.empty() ? StringRef{addr_->host} : StringRef{addr_->sni};
@@ -448,7 +443,7 @@ int Http2Session::initiate_connection() {
           SSL_set_tlsext_host_name(conn_.tls.ssl, sni_name.c_str());
         }
 
-        auto tls_session = ssl::reuse_tls_session(addr_->tls_session_cache);
+        auto tls_session = tls::reuse_tls_session(addr_->tls_session_cache);
         if (tls_session) {
           SSL_set_session(conn_.tls.ssl, tls_session);
           SSL_SESSION_free(tls_session);
@@ -582,11 +577,11 @@ int Http2Session::initiate_connection() {
       }
     }
 
-    on_write_ = &Http2Session::downstream_write;
-    on_read_ = &Http2Session::downstream_read;
-
     // We have been already connected when no TLS and proxy is used.
     if (state_ == PROXY_CONNECTED) {
+      on_read_ = &Http2Session::read_noop;
+      on_write_ = &Http2Session::write_noop;
+
       return connected();
     }
 
@@ -602,7 +597,8 @@ int Http2Session::initiate_connection() {
   }
 
   // Unreachable
-  DIE();
+  assert(0);
+
   return 0;
 }
 
@@ -821,9 +817,9 @@ int on_stream_close_callback(nghttp2_session *session, int32_t stream_id,
                              uint32_t error_code, void *user_data) {
   auto http2session = static_cast<Http2Session *>(user_data);
   if (LOG_ENABLED(INFO)) {
-    SSLOG(INFO, http2session) << "Stream stream_id=" << stream_id
-                              << " is being closed with error code "
-                              << error_code;
+    SSLOG(INFO, http2session)
+        << "Stream stream_id=" << stream_id
+        << " is being closed with error code " << error_code;
   }
   auto sd = static_cast<StreamData *>(
       nghttp2_session_get_stream_user_data(session, stream_id));
@@ -1149,7 +1145,7 @@ int on_response_headers(Http2Session *http2session, Downstream *downstream,
 
   if (downstream->get_upgraded()) {
     resp.connection_close = true;
-    // On upgrade sucess, both ends can send data
+    // On upgrade success, 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 handler;
@@ -1157,8 +1153,8 @@ int on_response_headers(Http2Session *http2session, Downstream *downstream,
     }
     downstream->set_request_state(Downstream::HEADER_COMPLETE);
     if (LOG_ENABLED(INFO)) {
-      SSLOG(INFO, http2session) << "HTTP upgrade success. stream_id="
-                                << frame->hd.stream_id;
+      SSLOG(INFO, http2session)
+          << "HTTP upgrade success. stream_id=" << frame->hd.stream_id;
     }
   } else {
     auto content_length = resp.fs.header(http2::HD_CONTENT_LENGTH);
@@ -1524,7 +1520,7 @@ int on_frame_not_send_callback(nghttp2_session *session,
 
     if (upstream->on_downstream_reset(downstream, false)) {
       // This should be done for h1 upstream only.  Deleting
-      // ClientHandler for h2 or SPDY upstream may lead to crash.
+      // ClientHandler for h2 upstream may lead to crash.
       delete upstream->get_client_handler();
     }
 
@@ -1646,6 +1642,9 @@ int Http2Session::connection_made() {
 
   state_ = Http2Session::CONNECTED;
 
+  on_write_ = &Http2Session::downstream_write;
+  on_read_ = &Http2Session::downstream_read;
+
   if (addr_->tls) {
     const unsigned char *next_proto = nullptr;
     unsigned int next_proto_len = 0;
@@ -1993,7 +1992,7 @@ int Http2Session::read_clear() {
     auto nread = conn_.read_clear(buf.data(), buf.size());
 
     if (nread == 0) {
-      return 0;
+      return write_clear();
     }
 
     if (nread < 0) {
@@ -2069,20 +2068,12 @@ int Http2Session::tls_handshake() {
   }
 
   if (!get_config()->tls.insecure &&
-      ssl::check_cert(conn_.tls.ssl, addr_, raddr_) != 0) {
+      tls::check_cert(conn_.tls.ssl, addr_, raddr_) != 0) {
     downstream_failure(addr_, raddr_);
 
     return -1;
   }
 
-  if (!SSL_session_reused(conn_.tls.ssl)) {
-    auto tls_session = SSL_get0_session(conn_.tls.ssl);
-    if (tls_session) {
-      ssl::try_cache_tls_session(addr_->tls_session_cache, *raddr_, tls_session,
-                                 ev_now(conn_.loop));
-    }
-  }
-
   read_ = &Http2Session::read_tls;
   write_ = &Http2Session::write_tls;
 
@@ -2105,7 +2096,7 @@ int Http2Session::read_tls() {
     auto nread = conn_.read_tls(buf.data(), buf.size());
 
     if (nread == 0) {
-      return 0;
+      return write_tls();
     }
 
     if (nread < 0) {
@@ -2128,7 +2119,10 @@ int Http2Session::write_tls() {
   for (;;) {
     if (wb_.rleft() > 0) {
       auto iovcnt = wb_.riovec(&iov, 1);
-      assert(iovcnt == 1);
+      if (iovcnt != 1) {
+        assert(0);
+        return -1;
+      }
       auto nwrite = conn_.write_tls(iov.iov_base, iov.iov_len);
 
       if (nwrite == 0) {
index ef59163..e67ea6c 100644 (file)
@@ -449,7 +449,7 @@ void Http2Upstream::initiate_downstream(Downstream *downstream) {
     if (rv == SHRPX_ERR_TLS_REQUIRED) {
       rv = redirect_to_https(downstream);
     } else {
-      rv = error_reply(downstream, 503);
+      rv = error_reply(downstream, 502);
     }
     if (rv != 0) {
       rst_stream(downstream, NGHTTP2_INTERNAL_ERROR);
@@ -464,7 +464,7 @@ void Http2Upstream::initiate_downstream(Downstream *downstream) {
   rv = downstream->attach_downstream_connection(std::move(dconn));
   if (rv != 0) {
     // downstream connection fails, send error page
-    if (error_reply(downstream, 503) != 0) {
+    if (error_reply(downstream, 502) != 0) {
       rst_stream(downstream, NGHTTP2_INTERNAL_ERROR);
     }
 
@@ -477,7 +477,7 @@ void Http2Upstream::initiate_downstream(Downstream *downstream) {
   rv = downstream->push_request_headers();
   if (rv != 0) {
 
-    if (error_reply(downstream, 503) != 0) {
+    if (error_reply(downstream, 502) != 0) {
       rst_stream(downstream, NGHTTP2_INTERNAL_ERROR);
     }
 
@@ -957,8 +957,8 @@ nghttp2_session_callbacks *create_http2_upstream_callbacks() {
   }
 
   if (config->http2.upstream.debug.frame_debug) {
-    nghttp2_session_callbacks_set_error_callback(callbacks,
-                                                 verbose_error_callback);
+    nghttp2_session_callbacks_set_error_callback2(callbacks,
+                                                  verbose_error_callback);
   }
 
   return callbacks;
@@ -1360,7 +1360,7 @@ int Http2Upstream::rst_stream(Downstream *downstream, uint32_t error_code) {
   if (rv < NGHTTP2_ERR_FATAL) {
     ULOG(FATAL, this) << "nghttp2_submit_rst_stream() failed: "
                       << nghttp2_strerror(rv);
-    DIE();
+    return -1;
   }
   return 0;
 }
@@ -1418,7 +1418,7 @@ ssize_t downstream_data_read_callback(nghttp2_session *session,
       if (!trailers.empty()) {
         std::vector<nghttp2_nv> nva;
         nva.reserve(trailers.size());
-        http2::copy_headers_to_nva_nocopy(nva, trailers);
+        http2::copy_headers_to_nva_nocopy(nva, trailers, http2::HDOP_STRIP_ALL);
         if (!nva.empty()) {
           rv = nghttp2_submit_trailer(session, stream_id, nva.data(),
                                       nva.size());
@@ -1657,9 +1657,9 @@ int Http2Upstream::on_downstream_header_complete(Downstream *downstream) {
   }
 
   auto nva = std::vector<nghttp2_nv>();
-  // 4 means :status and possible server, via and x-http2-push header
-  // field.
-  nva.reserve(resp.fs.headers().size() + 4 +
+  // 5 means :status and possible server, via, x-http2-push, and
+  // set-cookie (for affinity cookie) header field.
+  nva.reserve(resp.fs.headers().size() + 5 +
               httpconf.add_response_headers.size());
 
   auto response_status = http2::stringify_status(balloc, resp.http_status);
@@ -1667,7 +1667,8 @@ int Http2Upstream::on_downstream_header_complete(Downstream *downstream) {
   nva.push_back(http2::make_nv_ls_nocopy(":status", response_status));
 
   if (downstream->get_non_final_response()) {
-    http2::copy_headers_to_nva_nocopy(nva, resp.fs.headers());
+    http2::copy_headers_to_nva_nocopy(nva, resp.fs.headers(),
+                                      http2::HDOP_STRIP_ALL);
 
     if (LOG_ENABLED(INFO)) {
       log_response_headers(downstream, nva);
@@ -1687,7 +1688,8 @@ int Http2Upstream::on_downstream_header_complete(Downstream *downstream) {
     return 0;
   }
 
-  http2::copy_headers_to_nva_nocopy(nva, resp.fs.headers());
+  http2::copy_headers_to_nva_nocopy(
+      nva, resp.fs.headers(), http2::HDOP_STRIP_ALL & ~http2::HDOP_STRIP_VIA);
 
   if (!config->http2_proxy && !httpconf.no_server_rewrite) {
     nva.push_back(http2::make_nv_ls_nocopy("server", httpconf.server_name));
@@ -1698,6 +1700,22 @@ int Http2Upstream::on_downstream_header_complete(Downstream *downstream) {
     }
   }
 
+  if (req.method != HTTP_CONNECT || !downstream->get_upgraded()) {
+    auto affinity_cookie = downstream->get_affinity_cookie_to_send();
+    if (affinity_cookie) {
+      auto dconn = downstream->get_downstream_connection();
+      assert(dconn);
+      auto &group = dconn->get_downstream_addr_group();
+      auto &shared_addr = group->shared_addr;
+      auto &cookieconf = shared_addr->affinity.cookie;
+      auto secure =
+          http::require_cookie_secure_attribute(cookieconf.secure, req.scheme);
+      auto cookie_str = http::create_affinity_cookie(
+          balloc, cookieconf.name, affinity_cookie, cookieconf.path, secure);
+      nva.push_back(http2::make_nv_ls_nocopy("set-cookie", cookie_str));
+    }
+  }
+
   auto via = resp.fs.header(http2::HD_VIA);
   if (httpconf.no_via) {
     if (via) {
@@ -2005,7 +2023,7 @@ fail:
   if (rv == SHRPX_ERR_TLS_REQUIRED) {
     rv = on_downstream_abort_request_with_https_redirect(downstream);
   } else {
-    rv = on_downstream_abort_request(downstream, 503);
+    rv = on_downstream_abort_request(downstream, 502);
   }
   if (rv != 0) {
     rst_stream(downstream, NGHTTP2_INTERNAL_ERROR);
@@ -2021,7 +2039,7 @@ int Http2Upstream::prepare_push_promise(Downstream *downstream) {
   int rv;
 
   const auto &req = downstream->request();
-  const auto &resp = downstream->response();
+  auto &resp = downstream->response();
 
   auto base = http2::get_pure_path_component(req.path);
   if (base.empty()) {
@@ -2051,10 +2069,16 @@ int Http2Upstream::prepare_push_promise(Downstream *downstream) {
         authority = req.authority;
       }
 
+      if (resp.is_resource_pushed(scheme, authority, path)) {
+        continue;
+      }
+
       rv = submit_push_promise(scheme, authority, path, downstream);
       if (rv != 0) {
         return -1;
       }
+
+      resp.resource_pushed(scheme, authority, path);
     }
   }
   return 0;
@@ -2164,12 +2188,20 @@ int Http2Upstream::initiate_push(Downstream *downstream, const StringRef &uri) {
     authority = req.authority;
   }
 
+  auto &resp = downstream->response();
+
+  if (resp.is_resource_pushed(scheme, authority, path)) {
+    return 0;
+  }
+
   rv = submit_push_promise(scheme, authority, path, downstream);
 
   if (rv != 0) {
     return -1;
   }
 
+  resp.resource_pushed(scheme, authority, path);
+
   return 0;
 }
 
index cfb1311..f50c0f4 100644 (file)
@@ -35,7 +35,7 @@
 #include "shrpx_downstream_connection_pool.h"
 #include "shrpx_worker.h"
 #include "shrpx_http2_session.h"
-#include "shrpx_ssl.h"
+#include "shrpx_tls.h"
 #include "shrpx_log.h"
 #include "http2.h"
 #include "util.h"
@@ -72,27 +72,25 @@ void timeoutcb(struct ev_loop *loop, ev_timer *w, int revents) {
 } // namespace
 
 namespace {
-void connect_timeoutcb(struct ev_loop *loop, ev_timer *w, int revents) {
-  auto conn = static_cast<Connection *>(w->data);
-  auto dconn = static_cast<HttpDownstreamConnection *>(conn->data);
-  auto addr = dconn->get_addr();
-  auto raddr = dconn->get_raddr();
-
-  DCLOG(WARN, dconn) << "Connect time out; addr="
-                     << util::to_numeric_addr(raddr);
-
-  downstream_failure(addr, raddr);
-
-  auto downstream = dconn->get_downstream();
+void retry_downstream_connection(Downstream *downstream,
+                                 unsigned int status_code) {
   auto upstream = downstream->get_upstream();
   auto handler = upstream->get_client_handler();
 
+  downstream->add_retry();
+
+  if (downstream->no_more_retry()) {
+    delete handler;
+    return;
+  }
+
   downstream->pop_downstream_connection();
 
   int rv;
   auto ndconn = handler->get_downstream_connection(rv, downstream);
   if (ndconn) {
-    if (downstream->attach_downstream_connection(std::move(ndconn)) == 0) {
+    if (downstream->attach_downstream_connection(std::move(ndconn)) == 0 &&
+        downstream->push_request_headers() == 0) {
       return;
     }
   }
@@ -102,7 +100,7 @@ void connect_timeoutcb(struct ev_loop *loop, ev_timer *w, int revents) {
   if (rv == SHRPX_ERR_TLS_REQUIRED) {
     rv = upstream->on_downstream_abort_request_with_https_redirect(downstream);
   } else {
-    rv = upstream->on_downstream_abort_request(downstream, 504);
+    rv = upstream->on_downstream_abort_request(downstream, status_code);
   }
 
   if (rv != 0) {
@@ -112,6 +110,24 @@ void connect_timeoutcb(struct ev_loop *loop, ev_timer *w, int revents) {
 } // namespace
 
 namespace {
+void connect_timeoutcb(struct ev_loop *loop, ev_timer *w, int revents) {
+  auto conn = static_cast<Connection *>(w->data);
+  auto dconn = static_cast<HttpDownstreamConnection *>(conn->data);
+  auto addr = dconn->get_addr();
+  auto raddr = dconn->get_raddr();
+
+  DCLOG(WARN, dconn) << "Connect time out; addr="
+                     << util::to_numeric_addr(raddr);
+
+  downstream_failure(addr, raddr);
+
+  auto downstream = dconn->get_downstream();
+
+  retry_downstream_connection(downstream, 504);
+}
+} // namespace
+
+namespace {
 void readcb(struct ev_loop *loop, ev_io *w, int revents) {
   auto conn = static_cast<Connection *>(w->data);
   auto dconn = static_cast<HttpDownstreamConnection *>(conn->data);
@@ -127,37 +143,7 @@ void readcb(struct ev_loop *loop, ev_io *w, int revents) {
 
 namespace {
 void backend_retry(Downstream *downstream) {
-  auto upstream = downstream->get_upstream();
-  auto handler = upstream->get_client_handler();
-
-  downstream->add_retry();
-
-  if (downstream->no_more_retry()) {
-    delete handler;
-    return;
-  }
-
-  downstream->pop_downstream_connection();
-
-  int rv;
-  auto ndconn = handler->get_downstream_connection(rv, downstream);
-  if (ndconn) {
-    if (downstream->attach_downstream_connection(std::move(ndconn)) == 0) {
-      return;
-    }
-  }
-
-  downstream->set_request_state(Downstream::CONNECT_FAIL);
-
-  if (rv == SHRPX_ERR_TLS_REQUIRED) {
-    rv = upstream->on_downstream_abort_request_with_https_redirect(downstream);
-  } else {
-    rv = upstream->on_downstream_abort_request(downstream, 503);
-  }
-
-  if (rv != 0) {
-    delete handler;
-  }
+  retry_downstream_connection(downstream, 502);
 }
 } // namespace
 
@@ -196,7 +182,7 @@ void connectcb(struct ev_loop *loop, ev_io *w, int revents) {
 } // namespace
 
 HttpDownstreamConnection::HttpDownstreamConnection(
-    const std::shared_ptr<DownstreamAddrGroup> &group, ssize_t initial_addr_idx,
+    const std::shared_ptr<DownstreamAddrGroup> &group, size_t initial_addr_idx,
     struct ev_loop *loop, Worker *worker)
     : conn_(loop, -1, nullptr, worker->get_mcpool(),
             worker->get_downstream_config()->timeout.write,
@@ -269,8 +255,9 @@ int HttpDownstreamConnection::initiate_connection() {
     // initial_addr_idx_.
     size_t temp_idx = initial_addr_idx_;
 
-    auto &next_downstream =
-        shared_addr->affinity == AFFINITY_NONE ? shared_addr->next : temp_idx;
+    auto &next_downstream = shared_addr->affinity.type == AFFINITY_NONE
+                                ? shared_addr->next
+                                : temp_idx;
     auto end = next_downstream;
     for (;;) {
       auto check_dns_result = dns_query_.get() != nullptr;
@@ -283,10 +270,16 @@ int HttpDownstreamConnection::initiate_connection() {
         assert(addr->dns);
       } else {
         assert(addr_ == nullptr);
-        addr = &addrs[next_downstream];
-
-        if (++next_downstream >= addrs.size()) {
-          next_downstream = 0;
+        if (shared_addr->affinity.type == AFFINITY_NONE) {
+          addr = &addrs[next_downstream];
+          if (++next_downstream >= addrs.size()) {
+            next_downstream = 0;
+          }
+        } else {
+          addr = &addrs[shared_addr->affinity_hash[next_downstream].idx];
+          if (++next_downstream >= shared_addr->affinity_hash.size()) {
+            next_downstream = 0;
+          }
         }
 
         if (addr->proto != PROTO_HTTP1) {
@@ -423,14 +416,15 @@ int HttpDownstreamConnection::initiate_connection() {
       if (addr_->tls) {
         assert(ssl_ctx_);
 
-        auto ssl = ssl::create_ssl(ssl_ctx_);
+        auto ssl = tls::create_ssl(ssl_ctx_);
         if (!ssl) {
           return -1;
         }
 
-        ssl::setup_downstream_http1_alpn(ssl);
+        tls::setup_downstream_http1_alpn(ssl);
 
         conn_.set_ssl(ssl);
+        conn_.tls.client_session_cache = &addr_->tls_session_cache;
 
         auto sni_name =
             addr_->sni.empty() ? StringRef{addr_->host} : StringRef{addr_->sni};
@@ -438,7 +432,7 @@ int HttpDownstreamConnection::initiate_connection() {
           SSL_set_tlsext_host_name(conn_.tls.ssl, sni_name.c_str());
         }
 
-        auto session = ssl::reuse_tls_session(addr_->tls_session_cache);
+        auto session = tls::reuse_tls_session(addr_->tls_session_cache);
         if (session) {
           SSL_set_session(conn_.tls.ssl, session);
           SSL_SESSION_free(session);
@@ -481,6 +475,7 @@ int HttpDownstreamConnection::initiate_connection() {
 
 int HttpDownstreamConnection::push_request_headers() {
   if (downstream_->get_request_header_sent()) {
+    signal_write();
     return 0;
   }
 
@@ -537,7 +532,16 @@ int HttpDownstreamConnection::push_request_headers() {
   buf->append(authority);
   buf->append("\r\n");
 
-  http2::build_http1_headers_from_headers(buf, req.fs.headers());
+  auto &fwdconf = httpconf.forwarded;
+  auto &xffconf = httpconf.xff;
+  auto &xfpconf = httpconf.xfp;
+
+  uint32_t build_flags =
+      (fwdconf.strip_incoming ? http2::HDOP_STRIP_FORWARDED : 0) |
+      (xffconf.strip_incoming ? http2::HDOP_STRIP_X_FORWARDED_FOR : 0) |
+      (xfpconf.strip_incoming ? http2::HDOP_STRIP_X_FORWARDED_PROTO : 0);
+
+  http2::build_http1_headers_from_headers(buf, req.fs.headers(), build_flags);
 
   auto cookie = downstream_->assemble_request_cookie();
   if (!cookie.empty()) {
@@ -576,8 +580,6 @@ int HttpDownstreamConnection::push_request_headers() {
   auto upstream = downstream_->get_upstream();
   auto handler = upstream->get_client_handler();
 
-  auto &fwdconf = httpconf.forwarded;
-
   auto fwd =
       fwdconf.strip_incoming ? nullptr : req.fs.header(http2::HD_FORWARDED);
 
@@ -610,8 +612,6 @@ int HttpDownstreamConnection::push_request_headers() {
     buf->append("\r\n");
   }
 
-  auto &xffconf = httpconf.xff;
-
   auto xff = xffconf.strip_incoming ? nullptr
                                     : req.fs.header(http2::HD_X_FORWARDED_FOR);
 
@@ -629,10 +629,24 @@ int HttpDownstreamConnection::push_request_headers() {
     buf->append("\r\n");
   }
   if (!config->http2_proxy && !connect_method) {
-    buf->append("X-Forwarded-Proto: ");
-    assert(!req.scheme.empty());
-    buf->append(req.scheme);
-    buf->append("\r\n");
+    auto xfp = xfpconf.strip_incoming
+                   ? nullptr
+                   : req.fs.header(http2::HD_X_FORWARDED_PROTO);
+
+    if (xfpconf.add) {
+      buf->append("X-Forwarded-Proto: ");
+      if (xfp) {
+        buf->append((*xfp).value);
+        buf->append(", ");
+      }
+      assert(!req.scheme.empty());
+      buf->append(req.scheme);
+      buf->append("\r\n");
+    } else if (xfp) {
+      buf->append("X-Forwarded-Proto: ");
+      buf->append((*xfp).value);
+      buf->append("\r\n");
+    }
   }
   auto via = req.fs.header(http2::HD_VIA);
   if (httpconf.no_via) {
@@ -724,7 +738,8 @@ int HttpDownstreamConnection::end_upload_data() {
     output->append("0\r\n\r\n");
   } else {
     output->append("0\r\n");
-    http2::build_http1_headers_from_headers(output, trailers);
+    http2::build_http1_headers_from_headers(output, trailers,
+                                            http2::HDOP_STRIP_ALL);
     output->append("\r\n");
   }
 
@@ -736,7 +751,7 @@ void remove_from_pool(HttpDownstreamConnection *dconn) {
   auto &group = dconn->get_downstream_addr_group();
   auto &shared_addr = group->shared_addr;
 
-  if (shared_addr->affinity == AFFINITY_NONE) {
+  if (shared_addr->affinity.type == AFFINITY_NONE) {
     auto &dconn_pool =
         dconn->get_downstream_addr_group()->shared_addr->dconn_pool;
     dconn_pool.remove_downstream_connection(dconn);
@@ -767,8 +782,10 @@ void idle_timeoutcb(struct ev_loop *loop, ev_timer *w, int revents) {
   auto conn = static_cast<Connection *>(w->data);
   auto dconn = static_cast<HttpDownstreamConnection *>(conn->data);
 
-  // We don't have to check conn->expired_rt() since we restart timer
-  // when connection gets idle.
+  if (w == &conn->rt && !conn->expired_rt()) {
+    return;
+  }
+
   if (LOG_ENABLED(INFO)) {
     DCLOG(INFO, dconn) << "Idle connection timeout";
   }
@@ -876,13 +893,12 @@ int htp_hdrs_completecb(http_parser *htp) {
     if (resp.fs.parse_content_length() != 0) {
       return -1;
     }
-    if (resp.fs.content_length != 0) {
-      return -1;
-    }
     if (resp.fs.content_length == 0) {
       auto cl = resp.fs.header(http2::HD_CONTENT_LENGTH);
       assert(cl);
       http2::erase_header(cl);
+    } else if (resp.fs.content_length != -1) {
+      return -1;
     }
   } else if (resp.http_status / 100 == 1 ||
              (resp.http_status == 200 && req.method == HTTP_CONNECT)) {
@@ -986,8 +1002,8 @@ int ensure_max_header_fields(const Downstream *downstream,
 
   if (resp.fs.num_fields() >= httpconf.max_response_header_fields) {
     if (LOG_ENABLED(INFO)) {
-      DLOG(INFO, downstream) << "Too many header field num="
-                             << resp.fs.num_fields() + 1;
+      DLOG(INFO, downstream)
+          << "Too many header field num=" << resp.fs.num_fields() + 1;
     }
     return -1;
   }
@@ -1223,20 +1239,12 @@ int HttpDownstreamConnection::tls_handshake() {
   }
 
   if (!get_config()->tls.insecure &&
-      ssl::check_cert(conn_.tls.ssl, addr_, raddr_) != 0) {
+      tls::check_cert(conn_.tls.ssl, addr_, raddr_) != 0) {
     downstream_failure(addr_, raddr_);
 
     return -1;
   }
 
-  if (!SSL_session_reused(conn_.tls.ssl)) {
-    auto session = SSL_get0_session(conn_.tls.ssl);
-    if (session) {
-      ssl::try_cache_tls_session(addr_->tls_session_cache, *raddr_, session,
-                                 ev_now(conn_.loop));
-    }
-  }
-
   auto &connect_blocker = addr_->connect_blocker;
 
   signal_write_ = &HttpDownstreamConnection::actual_signal_write;
@@ -1295,7 +1303,10 @@ int HttpDownstreamConnection::write_tls() {
 
   while (input->rleft() > 0) {
     auto iovcnt = input->riovec(&iov, 1);
-    assert(iovcnt == 1);
+    if (iovcnt != 1) {
+      assert(0);
+      return -1;
+    }
     auto nwrite = conn_.write_tls(iov.iov_base, iov.iov_len);
 
     if (nwrite == 0) {
index 94e1b96..baea026 100644 (file)
@@ -44,7 +44,7 @@ struct DNSQuery;
 class HttpDownstreamConnection : public DownstreamConnection {
 public:
   HttpDownstreamConnection(const std::shared_ptr<DownstreamAddrGroup> &group,
-                           ssize_t initial_addr_idx, struct ev_loop *loop,
+                           size_t initial_addr_idx, struct ev_loop *loop,
                            Worker *worker);
   virtual ~HttpDownstreamConnection();
   virtual int attach_downstream(Downstream *downstream);
@@ -108,7 +108,9 @@ private:
   std::unique_ptr<DNSQuery> dns_query_;
   IOControl ioctrl_;
   http_parser response_htp_;
-  ssize_t initial_addr_idx_;
+  // Index to backend address.  If client affinity is enabled, it is
+  // the index to affinity_hash.  Otherwise, it is 0, and not used.
+  size_t initial_addr_idx_;
   // true if first write of reused connection succeeded.  For
   // convenience, this is initialized as true.
   bool reuse_first_write_done_;
index a93ab9e..0fdcc93 100644 (file)
@@ -43,8 +43,9 @@ void test_shrpx_http_create_forwarded(void) {
 
   CU_ASSERT("by=\"example.com:3000\";for=\"[::1]\";host=\"www.example.com\";"
             "proto=https" ==
-            http::create_forwarded(balloc, FORWARDED_BY | FORWARDED_FOR |
-                                               FORWARDED_HOST | FORWARDED_PROTO,
+            http::create_forwarded(balloc,
+                                   FORWARDED_BY | FORWARDED_FOR |
+                                       FORWARDED_HOST | FORWARDED_PROTO,
                                    StringRef::from_lit("example.com:3000"),
                                    StringRef::from_lit("[::1]"),
                                    StringRef::from_lit("www.example.com"),
@@ -68,11 +69,12 @@ void test_shrpx_http_create_forwarded(void) {
                 StringRef::from_lit("[::1]"), StringRef::from_lit("_hidden"),
                 StringRef::from_lit(""), StringRef::from_lit("")));
 
-  CU_ASSERT("" == http::create_forwarded(
-                      balloc, FORWARDED_BY | FORWARDED_FOR | FORWARDED_HOST |
-                                  FORWARDED_PROTO,
-                      StringRef::from_lit(""), StringRef::from_lit(""),
-                      StringRef::from_lit(""), StringRef::from_lit("")));
+  CU_ASSERT("" ==
+            http::create_forwarded(
+                balloc,
+                FORWARDED_BY | FORWARDED_FOR | FORWARDED_HOST | FORWARDED_PROTO,
+                StringRef::from_lit(""), StringRef::from_lit(""),
+                StringRef::from_lit(""), StringRef::from_lit("")));
 }
 
 void test_shrpx_http_create_via_header_value(void) {
@@ -89,4 +91,31 @@ void test_shrpx_http_create_via_header_value(void) {
   CU_ASSERT(("2 nghttpx" == StringRef{std::begin(buf), end}));
 }
 
+void test_shrpx_http_create_affinity_cookie(void) {
+  BlockAllocator balloc(1024, 1024);
+  StringRef c;
+
+  c = http::create_affinity_cookie(balloc, StringRef::from_lit("cookie-val"),
+                                   0xf1e2d3c4u, StringRef{}, false);
+
+  CU_ASSERT("cookie-val=f1e2d3c4" == c);
+
+  c = http::create_affinity_cookie(balloc, StringRef::from_lit("alpha"),
+                                   0x00000000u, StringRef{}, true);
+
+  CU_ASSERT("alpha=00000000; Secure" == c);
+
+  c = http::create_affinity_cookie(balloc, StringRef::from_lit("bravo"),
+                                   0x01111111u, StringRef::from_lit("bar"),
+                                   false);
+
+  CU_ASSERT("bravo=01111111; Path=bar" == c);
+
+  c = http::create_affinity_cookie(balloc, StringRef::from_lit("charlie"),
+                                   0x01111111u, StringRef::from_lit("bar"),
+                                   true);
+
+  CU_ASSERT("charlie=01111111; Path=bar; Secure" == c);
+}
+
 } // namespace shrpx
index 7692c95..85d2947 100644 (file)
@@ -33,6 +33,7 @@ namespace shrpx {
 
 void test_shrpx_http_create_forwarded(void);
 void test_shrpx_http_create_via_header_value(void);
+void test_shrpx_http_create_affinity_cookie(void);
 
 } // namespace shrpx
 
index e97a698..452ec90 100644 (file)
@@ -150,8 +150,8 @@ int htp_hdr_keycb(http_parser *htp, const char *data, size_t len) {
     } else {
       if (req.fs.num_fields() >= httpconf.max_request_header_fields) {
         if (LOG_ENABLED(INFO)) {
-          ULOG(INFO, upstream) << "Too many header field num="
-                               << req.fs.num_fields() + 1;
+          ULOG(INFO, upstream)
+              << "Too many header field num=" << req.fs.num_fields() + 1;
         }
         downstream->set_request_state(
             Downstream::HTTP1_REQUEST_HEADER_TOO_LARGE);
@@ -166,8 +166,8 @@ int htp_hdr_keycb(http_parser *htp, const char *data, size_t len) {
     } else {
       if (req.fs.num_fields() >= httpconf.max_request_header_fields) {
         if (LOG_ENABLED(INFO)) {
-          ULOG(INFO, upstream) << "Too many header field num="
-                               << req.fs.num_fields() + 1;
+          ULOG(INFO, upstream)
+              << "Too many header field num=" << req.fs.num_fields() + 1;
         }
         return -1;
       }
@@ -207,7 +207,7 @@ int htp_hdr_valcb(http_parser *htp, const char *data, size_t len) {
 namespace {
 void rewrite_request_host_path_from_uri(BlockAllocator &balloc, Request &req,
                                         const StringRef &uri,
-                                        http_parser_url &u, bool http2_proxy) {
+                                        http_parser_url &u) {
   assert(u.field_set & (1 << UF_HOST));
 
   // As per https://tools.ietf.org/html/rfc7230#section-5.4, we
@@ -276,11 +276,7 @@ void rewrite_request_host_path_from_uri(BlockAllocator &balloc, Request &req,
     }
   }
 
-  if (http2_proxy) {
-    req.path = path;
-  } else {
-    req.path = http2::rewrite_clean_path(balloc, path);
-  }
+  req.path = http2::rewrite_clean_path(balloc, path);
 }
 } // namespace
 
@@ -395,8 +391,7 @@ int htp_hdrs_completecb(http_parser *htp) {
         req.scheme = StringRef::from_lit("http");
       }
     } else {
-      rewrite_request_host_path_from_uri(
-          balloc, req, req.path, u, config->http2_proxy && !faddr->alt_mode);
+      rewrite_request_host_path_from_uri(balloc, req, req.path, u);
     }
   }
 
@@ -459,7 +454,7 @@ int htp_hdrs_completecb(http_parser *htp) {
       auto output = downstream->get_response_buf();
       constexpr auto res = StringRef::from_lit("HTTP/1.1 100 Continue\r\n\r\n");
       output->append(res);
-      handler->signal_write_no_wait();
+      handler->signal_write();
     }
   }
 
@@ -505,7 +500,7 @@ int htp_msg_completecb(http_parser *htp) {
       // in request phase hook.  We only delete and proceed to the
       // next request handling (if we don't close the connection).  We
       // first pause parser here just as we normally do, and call
-      // signal_write_no_wait() to run on_write().
+      // signal_write() to run on_write().
       http_parser_pause(htp, 1);
 
       return 0;
@@ -547,7 +542,7 @@ int HttpsUpstream::on_read() {
   auto rlimit = handler_->get_rlimit();
   auto downstream = get_downstream();
 
-  if (rb->rleft() == 0) {
+  if (rb->rleft() == 0 || handler_->get_should_close_after_write()) {
     return 0;
   }
 
@@ -610,7 +605,7 @@ int HttpsUpstream::on_read() {
     if (downstream &&
         downstream->get_request_state() == Downstream::MSG_COMPLETE &&
         downstream->get_response_state() == Downstream::MSG_COMPLETE) {
-      handler_->signal_write_no_wait();
+      handler_->signal_write();
     }
     return 0;
   }
@@ -624,7 +619,7 @@ int HttpsUpstream::on_read() {
 
     if (downstream && downstream->get_response_state() != Downstream::INITIAL) {
       handler_->set_should_close_after_write(true);
-      handler_->signal_write_no_wait();
+      handler_->signal_write();
       return 0;
     }
 
@@ -636,7 +631,7 @@ int HttpsUpstream::on_read() {
       status_code = downstream->response().http_status;
       if (status_code == 0) {
         if (downstream->get_request_state() == Downstream::CONNECT_FAIL) {
-          status_code = 503;
+          status_code = 502;
         } else if (downstream->get_request_state() ==
                    Downstream::HTTP1_REQUEST_HEADER_TOO_LARGE) {
           status_code = 431;
@@ -650,7 +645,7 @@ int HttpsUpstream::on_read() {
 
     error_reply(status_code);
 
-    handler_->signal_write_no_wait();
+    handler_->signal_write();
 
     return 0;
   }
@@ -777,7 +772,7 @@ int HttpsUpstream::downstream_read(DownstreamConnection *dconn) {
   }
 
 end:
-  handler_->signal_write_no_wait();
+  handler_->signal_write();
 
   return 0;
 }
@@ -834,7 +829,7 @@ int HttpsUpstream::downstream_eof(DownstreamConnection *dconn) {
   // drop connection.
   return -1;
 end:
-  handler_->signal_write_no_wait();
+  handler_->signal_write();
 
   return 0;
 }
@@ -862,7 +857,7 @@ int HttpsUpstream::downstream_error(DownstreamConnection *dconn, int events) {
 
   downstream->pop_downstream_connection();
 
-  handler_->signal_write_no_wait();
+  handler_->signal_write();
   return 0;
 }
 
@@ -1027,6 +1022,12 @@ int HttpsUpstream::on_downstream_header_complete(Downstream *downstream) {
   auto &resp = downstream->response();
   auto &balloc = downstream->get_block_allocator();
 
+  if (downstream->get_non_final_response() &&
+      !downstream->supports_non_final_response()) {
+    resp.fs.clear_headers();
+    return 0;
+  }
+
 #ifdef HAVE_MRUBY
   if (!downstream->get_non_final_response()) {
     auto worker = handler_->get_worker();
@@ -1064,9 +1065,10 @@ int HttpsUpstream::on_downstream_header_complete(Downstream *downstream) {
         get_client_handler()->get_upstream_scheme());
   }
 
-  http2::build_http1_headers_from_headers(buf, resp.fs.headers());
-
   if (downstream->get_non_final_response()) {
+    http2::build_http1_headers_from_headers(buf, resp.fs.headers(),
+                                            http2::HDOP_STRIP_ALL);
+
     buf->append("\r\n");
 
     if (LOG_ENABLED(INFO)) {
@@ -1078,6 +1080,9 @@ int HttpsUpstream::on_downstream_header_complete(Downstream *downstream) {
     return 0;
   }
 
+  http2::build_http1_headers_from_headers(
+      buf, resp.fs.headers(), http2::HDOP_STRIP_ALL & ~http2::HDOP_STRIP_VIA);
+
   auto worker = handler_->get_worker();
 
   // after graceful shutdown commenced, add connection: close header
@@ -1142,6 +1147,24 @@ int HttpsUpstream::on_downstream_header_complete(Downstream *downstream) {
     }
   }
 
+  if (req.method != HTTP_CONNECT || !downstream->get_upgraded()) {
+    auto affinity_cookie = downstream->get_affinity_cookie_to_send();
+    if (affinity_cookie) {
+      auto dconn = downstream->get_downstream_connection();
+      assert(dconn);
+      auto &group = dconn->get_downstream_addr_group();
+      auto &shared_addr = group->shared_addr;
+      auto &cookieconf = shared_addr->affinity.cookie;
+      auto secure =
+          http::require_cookie_secure_attribute(cookieconf.secure, req.scheme);
+      auto cookie_str = http::create_affinity_cookie(
+          balloc, cookieconf.name, affinity_cookie, cookieconf.path, secure);
+      buf->append("Set-Cookie: ");
+      buf->append(cookie_str);
+      buf->append("\r\n");
+    }
+  }
+
   auto via = resp.fs.header(http2::HD_VIA);
   if (httpconf.no_via) {
     if (via) {
@@ -1210,7 +1233,8 @@ int HttpsUpstream::on_downstream_body_complete(Downstream *downstream) {
       output->append("0\r\n\r\n");
     } else {
       output->append("0\r\n");
-      http2::build_http1_headers_from_headers(output, trailers);
+      http2::build_http1_headers_from_headers(output, trailers,
+                                              http2::HDOP_STRIP_ALL);
       output->append("\r\n");
     }
   }
@@ -1234,14 +1258,14 @@ int HttpsUpstream::on_downstream_body_complete(Downstream *downstream) {
 int HttpsUpstream::on_downstream_abort_request(Downstream *downstream,
                                                unsigned int status_code) {
   error_reply(status_code);
-  handler_->signal_write_no_wait();
+  handler_->signal_write();
   return 0;
 }
 
 int HttpsUpstream::on_downstream_abort_request_with_https_redirect(
     Downstream *downstream) {
   redirect_to_https(downstream);
-  handler_->signal_write_no_wait();
+  handler_->signal_write();
   return 0;
 }
 
@@ -1315,7 +1339,7 @@ int HttpsUpstream::on_downstream_reset(Downstream *downstream, bool no_retry) {
       // We have got all response body already.  Send it off.
       return 0;
     case Downstream::INITIAL:
-      if (on_downstream_abort_request(downstream_.get(), 503) != 0) {
+      if (on_downstream_abort_request(downstream_.get(), 502) != 0) {
         return -1;
       }
       return 0;
@@ -1353,7 +1377,7 @@ fail:
   if (rv == SHRPX_ERR_TLS_REQUIRED) {
     rv = on_downstream_abort_request_with_https_redirect(downstream);
   } else {
-    rv = on_downstream_abort_request(downstream_.get(), 503);
+    rv = on_downstream_abort_request(downstream_.get(), 502);
   }
   if (rv != 0) {
     return -1;
index 5241f05..863ffa5 100644 (file)
@@ -25,7 +25,7 @@
 #include "shrpx_live_check.h"
 #include "shrpx_worker.h"
 #include "shrpx_connect_blocker.h"
-#include "shrpx_ssl.h"
+#include "shrpx_tls.h"
 #include "shrpx_log.h"
 
 namespace shrpx {
@@ -205,23 +205,24 @@ int LiveCheck::initiate_connection() {
   if (!dns_query_ && addr_->tls) {
     assert(ssl_ctx_);
 
-    auto ssl = ssl::create_ssl(ssl_ctx_);
+    auto ssl = tls::create_ssl(ssl_ctx_);
     if (!ssl) {
       return -1;
     }
 
     switch (addr_->proto) {
     case PROTO_HTTP1:
-      ssl::setup_downstream_http1_alpn(ssl);
+      tls::setup_downstream_http1_alpn(ssl);
       break;
     case PROTO_HTTP2:
-      ssl::setup_downstream_http2_alpn(ssl);
+      tls::setup_downstream_http2_alpn(ssl);
       break;
     default:
       assert(0);
     }
 
     conn_.set_ssl(ssl);
+    conn_.tls.client_session_cache = &addr_->tls_session_cache;
   }
 
   if (addr_->dns) {
@@ -303,7 +304,7 @@ int LiveCheck::initiate_connection() {
       SSL_set_tlsext_host_name(conn_.tls.ssl, sni_name.c_str());
     }
 
-    auto session = ssl::reuse_tls_session(addr_->tls_session_cache);
+    auto session = tls::reuse_tls_session(addr_->tls_session_cache);
     if (session) {
       SSL_set_session(conn_.tls.ssl, session);
       SSL_SESSION_free(session);
@@ -396,18 +397,10 @@ int LiveCheck::tls_handshake() {
   }
 
   if (!get_config()->tls.insecure &&
-      ssl::check_cert(conn_.tls.ssl, addr_, raddr_) != 0) {
+      tls::check_cert(conn_.tls.ssl, addr_, raddr_) != 0) {
     return -1;
   }
 
-  if (!SSL_session_reused(conn_.tls.ssl)) {
-    auto tls_session = SSL_get0_session(conn_.tls.ssl);
-    if (tls_session) {
-      ssl::try_cache_tls_session(addr_->tls_session_cache, *raddr_, tls_session,
-                                 ev_now(conn_.loop));
-    }
-  }
-
   // Check negotiated ALPN
 
   const unsigned char *next_proto = nullptr;
@@ -485,7 +478,10 @@ int LiveCheck::write_tls() {
   for (;;) {
     if (wb_.rleft() > 0) {
       auto iovcnt = wb_.riovec(&iov, 1);
-      assert(iovcnt == 1);
+      if (iovcnt != 1) {
+        assert(0);
+        return -1;
+      }
       auto nwrite = conn_.write_tls(iov.iov_base, iov.iov_len);
 
       if (nwrite == 0) {
@@ -551,7 +547,10 @@ int LiveCheck::write_clear() {
   for (;;) {
     if (wb_.rleft() > 0) {
       auto iovcnt = wb_.riovec(&iov, 1);
-      assert(iovcnt == 1);
+      if (iovcnt != 1) {
+        assert(0);
+        return -1;
+      }
       auto nwrite = conn_.write_clear(iov.iov_base, iov.iov_len);
 
       if (nwrite == 0) {
@@ -775,7 +774,7 @@ int LiveCheck::connection_made() {
   }
 
   auto must_terminate =
-      addr_->tls && !nghttp2::ssl::check_http2_requirement(conn_.tls.ssl);
+      addr_->tls && !nghttp2::tls::check_http2_requirement(conn_.tls.ssl);
 
   if (must_terminate) {
     if (LOG_ENABLED(INFO)) {
index 8b17779..d43b6fa 100644 (file)
@@ -241,6 +241,105 @@ std::pair<OutputIterator, OutputIterator> copy(T n, OutputIterator d_first,
 } // namespace
 
 namespace {
+// 1 means that character must be escaped as "\xNN", where NN is ascii
+// code of the character in hex notation.
+constexpr uint8_t ESCAPE_TBL[] = {
+    1 /* NUL  */, 1 /* SOH  */, 1 /* STX  */, 1 /* ETX  */, 1 /* EOT  */,
+    1 /* ENQ  */, 1 /* ACK  */, 1 /* BEL  */, 1 /* BS   */, 1 /* HT   */,
+    1 /* LF   */, 1 /* VT   */, 1 /* FF   */, 1 /* CR   */, 1 /* SO   */,
+    1 /* SI   */, 1 /* DLE  */, 1 /* DC1  */, 1 /* DC2  */, 1 /* DC3  */,
+    1 /* DC4  */, 1 /* NAK  */, 1 /* SYN  */, 1 /* ETB  */, 1 /* CAN  */,
+    1 /* EM   */, 1 /* SUB  */, 1 /* ESC  */, 1 /* FS   */, 1 /* GS   */,
+    1 /* RS   */, 1 /* US   */, 0 /* SPC  */, 0 /* !    */, 1 /* "    */,
+    0 /* #    */, 0 /* $    */, 0 /* %    */, 0 /* &    */, 0 /* '    */,
+    0 /* (    */, 0 /* )    */, 0 /* *    */, 0 /* +    */, 0 /* ,    */,
+    0 /* -    */, 0 /* .    */, 0 /* /    */, 0 /* 0    */, 0 /* 1    */,
+    0 /* 2    */, 0 /* 3    */, 0 /* 4    */, 0 /* 5    */, 0 /* 6    */,
+    0 /* 7    */, 0 /* 8    */, 0 /* 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 /* [    */, 1 /* \    */, 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 /* }    */, 0 /* ~    */, 1 /* 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 */,
+};
+} // namespace
+
+namespace {
+template <typename OutputIterator>
+std::pair<OutputIterator, OutputIterator>
+copy_escape(const char *src, size_t srclen, OutputIterator d_first,
+            OutputIterator d_last) {
+  auto safe_first = src;
+  for (auto p = src; p != src + srclen && d_first != d_last; ++p) {
+    unsigned char c = *p;
+    if (!ESCAPE_TBL[c]) {
+      continue;
+    }
+
+    auto n =
+        std::min(std::distance(d_first, d_last), std::distance(safe_first, p));
+    d_first = std::copy_n(safe_first, n, d_first);
+    if (std::distance(d_first, d_last) < 4) {
+      return std::make_pair(d_first, d_last);
+    }
+    *d_first++ = '\\';
+    *d_first++ = 'x';
+    *d_first++ = LOWER_XDIGITS[c >> 4];
+    *d_first++ = LOWER_XDIGITS[c & 0xf];
+    safe_first = p + 1;
+  }
+
+  auto n = std::min(std::distance(d_first, d_last),
+                    std::distance(safe_first, src + srclen));
+  return std::make_pair(std::copy_n(safe_first, n, d_first), d_last);
+}
+} // namespace
+
+namespace {
+template <typename OutputIterator>
+std::pair<OutputIterator, OutputIterator> copy_escape(const StringRef &src,
+                                                      OutputIterator d_first,
+                                                      OutputIterator d_last) {
+  return copy_escape(src.c_str(), src.size(), d_first, d_last);
+}
+} // namespace
+
+namespace {
 // Construct absolute request URI from |Request|, mainly to log
 // request URI for proxy request (HTTP/2 proxy or client proxy).  This
 // is mostly same routine found in
@@ -303,11 +402,10 @@ void upstream_accesslog(const std::vector<LogFragment> &lfv,
                   ? req.authority
                   : config->http2_proxy
                         ? construct_absolute_request_uri(balloc, req)
-                        : req.path.empty()
-                              ? req.method == HTTP_OPTIONS
-                                    ? StringRef::from_lit("*")
-                                    : StringRef::from_lit("-")
-                              : req.path;
+                        : req.path.empty() ? req.method == HTTP_OPTIONS
+                                                 ? StringRef::from_lit("*")
+                                                 : StringRef::from_lit("-")
+                                           : req.path;
 
   auto p = std::begin(buf);
   auto last = std::end(buf) - 2;
@@ -329,7 +427,7 @@ void upstream_accesslog(const std::vector<LogFragment> &lfv,
     case SHRPX_LOGF_REQUEST:
       std::tie(p, last) = copy(method, p, last);
       std::tie(p, last) = copy(' ', p, last);
-      std::tie(p, last) = copy(path, p, last);
+      std::tie(p, last) = copy_escape(path, p, last);
       std::tie(p, last) = copy_l(" HTTP/", p, last);
       std::tie(p, last) = copy(req.http_major, p, last);
       if (req.http_major < 2) {
@@ -346,7 +444,7 @@ void upstream_accesslog(const std::vector<LogFragment> &lfv,
     case SHRPX_LOGF_HTTP: {
       auto hd = req.fs.header(lf.value);
       if (hd) {
-        std::tie(p, last) = copy((*hd).value, p, last);
+        std::tie(p, last) = copy_escape((*hd).value, p, last);
         break;
       }
 
@@ -387,38 +485,118 @@ void upstream_accesslog(const std::vector<LogFragment> &lfv,
       std::tie(p, last) = copy(lgsp.pid, p, last);
       break;
     case SHRPX_LOGF_ALPN:
-      std::tie(p, last) = copy(lgsp.alpn, p, last);
+      std::tie(p, last) = copy_escape(lgsp.alpn, p, last);
       break;
-    case SHRPX_LOGF_SSL_CIPHER:
-      if (!lgsp.tls_info) {
+    case SHRPX_LOGF_TLS_CIPHER:
+      if (!lgsp.ssl) {
         std::tie(p, last) = copy('-', p, last);
         break;
       }
-      std::tie(p, last) = copy(lgsp.tls_info->cipher, p, last);
+      std::tie(p, last) = copy(SSL_get_cipher_name(lgsp.ssl), p, last);
       break;
-    case SHRPX_LOGF_SSL_PROTOCOL:
-      if (!lgsp.tls_info) {
+    case SHRPX_LOGF_TLS_PROTOCOL:
+      if (!lgsp.ssl) {
         std::tie(p, last) = copy('-', p, last);
         break;
       }
-      std::tie(p, last) = copy(lgsp.tls_info->protocol, p, last);
+      std::tie(p, last) =
+          copy(nghttp2::tls::get_tls_protocol(lgsp.ssl), p, last);
       break;
-    case SHRPX_LOGF_SSL_SESSION_ID:
-      if (!lgsp.tls_info || lgsp.tls_info->session_id_length == 0) {
+    case SHRPX_LOGF_TLS_SESSION_ID: {
+      auto session = SSL_get_session(lgsp.ssl);
+      if (!session) {
+        std::tie(p, last) = copy('-', p, last);
+        break;
+      }
+      unsigned int session_id_length = 0;
+      auto session_id = SSL_SESSION_get_id(session, &session_id_length);
+      if (session_id_length == 0) {
         std::tie(p, last) = copy('-', p, last);
         break;
       }
-      std::tie(p, last) = copy_hex_low(
-          lgsp.tls_info->session_id, lgsp.tls_info->session_id_length, p, last);
+      std::tie(p, last) = copy_hex_low(session_id, session_id_length, p, last);
       break;
-    case SHRPX_LOGF_SSL_SESSION_REUSED:
-      if (!lgsp.tls_info) {
+    }
+    case SHRPX_LOGF_TLS_SESSION_REUSED:
+      if (!lgsp.ssl) {
         std::tie(p, last) = copy('-', p, last);
         break;
       }
       std::tie(p, last) =
-          copy(lgsp.tls_info->session_reused ? 'r' : '.', p, last);
+          copy(SSL_session_reused(lgsp.ssl) ? 'r' : '.', p, last);
+      break;
+    case SHRPX_LOGF_TLS_SNI:
+      if (lgsp.sni.empty()) {
+        std::tie(p, last) = copy('-', p, last);
+        break;
+      }
+      std::tie(p, last) = copy_escape(lgsp.sni, p, last);
+      break;
+    case SHRPX_LOGF_TLS_CLIENT_FINGERPRINT_SHA1:
+    case SHRPX_LOGF_TLS_CLIENT_FINGERPRINT_SHA256: {
+      if (!lgsp.ssl) {
+        std::tie(p, last) = copy('-', p, last);
+        break;
+      }
+      auto x = SSL_get_peer_certificate(lgsp.ssl);
+      if (!x) {
+        std::tie(p, last) = copy('-', p, last);
+        break;
+      }
+      std::array<uint8_t, 32> buf;
+      auto len = tls::get_x509_fingerprint(
+          buf.data(), buf.size(), x,
+          lf.type == SHRPX_LOGF_TLS_CLIENT_FINGERPRINT_SHA256 ? EVP_sha256()
+                                                              : EVP_sha1());
+      X509_free(x);
+      if (len <= 0) {
+        std::tie(p, last) = copy('-', p, last);
+        break;
+      }
+      std::tie(p, last) = copy_hex_low(buf.data(), len, p, last);
       break;
+    }
+    case SHRPX_LOGF_TLS_CLIENT_ISSUER_NAME:
+    case SHRPX_LOGF_TLS_CLIENT_SUBJECT_NAME: {
+      if (!lgsp.ssl) {
+        std::tie(p, last) = copy('-', p, last);
+        break;
+      }
+      auto x = SSL_get_peer_certificate(lgsp.ssl);
+      if (!x) {
+        std::tie(p, last) = copy('-', p, last);
+        break;
+      }
+      auto name = lf.type == SHRPX_LOGF_TLS_CLIENT_ISSUER_NAME
+                      ? tls::get_x509_issuer_name(balloc, x)
+                      : tls::get_x509_subject_name(balloc, x);
+      X509_free(x);
+      if (name.empty()) {
+        std::tie(p, last) = copy('-', p, last);
+        break;
+      }
+      std::tie(p, last) = copy(name, p, last);
+      break;
+    }
+    case SHRPX_LOGF_TLS_CLIENT_SERIAL: {
+      if (!lgsp.ssl) {
+        std::tie(p, last) = copy('-', p, last);
+        break;
+      }
+      auto x = SSL_get_peer_certificate(lgsp.ssl);
+      if (!x) {
+        std::tie(p, last) = copy('-', p, last);
+        break;
+      }
+      auto sn = tls::get_x509_serial(balloc, x);
+      X509_free(x);
+      if (sn.empty()) {
+        std::tie(p, last) = copy('-', p, last);
+        break;
+      }
+      std::tie(p, last) = copy(sn, p, last);
+      break;
+    }
     case SHRPX_LOGF_BACKEND_HOST:
       if (!downstream_addr) {
         std::tie(p, last) = copy('-', p, last);
index 1afb6e1..ea77fbc 100644 (file)
@@ -36,7 +36,7 @@
 
 #include "shrpx_config.h"
 #include "shrpx_log_config.h"
-#include "ssl.h"
+#include "tls.h"
 #include "template.h"
 
 using namespace nghttp2;
@@ -68,8 +68,8 @@ using namespace nghttp2;
 
 // Downstream log
 #define DLOG(SEVERITY, DOWNSTREAM)                                             \
-  (shrpx::Log(SEVERITY, __FILE__, __LINE__) << "[DOWNSTREAM:" << DOWNSTREAM    \
-                                            << "] ")
+  (shrpx::Log(SEVERITY, __FILE__, __LINE__)                                    \
+   << "[DOWNSTREAM:" << DOWNSTREAM << "] ")
 
 // Downstream connection log
 #define DCLOG(SEVERITY, DCONN)                                                 \
@@ -129,10 +129,20 @@ enum LogFragmentType {
   SHRPX_LOGF_REQUEST_TIME,
   SHRPX_LOGF_PID,
   SHRPX_LOGF_ALPN,
-  SHRPX_LOGF_SSL_CIPHER,
-  SHRPX_LOGF_SSL_PROTOCOL,
-  SHRPX_LOGF_SSL_SESSION_ID,
-  SHRPX_LOGF_SSL_SESSION_REUSED,
+  SHRPX_LOGF_TLS_CIPHER,
+  SHRPX_LOGF_SSL_CIPHER = SHRPX_LOGF_TLS_CIPHER,
+  SHRPX_LOGF_TLS_PROTOCOL,
+  SHRPX_LOGF_SSL_PROTOCOL = SHRPX_LOGF_TLS_PROTOCOL,
+  SHRPX_LOGF_TLS_SESSION_ID,
+  SHRPX_LOGF_SSL_SESSION_ID = SHRPX_LOGF_TLS_SESSION_ID,
+  SHRPX_LOGF_TLS_SESSION_REUSED,
+  SHRPX_LOGF_SSL_SESSION_REUSED = SHRPX_LOGF_TLS_SESSION_REUSED,
+  SHRPX_LOGF_TLS_SNI,
+  SHRPX_LOGF_TLS_CLIENT_FINGERPRINT_SHA1,
+  SHRPX_LOGF_TLS_CLIENT_FINGERPRINT_SHA256,
+  SHRPX_LOGF_TLS_CLIENT_ISSUER_NAME,
+  SHRPX_LOGF_TLS_CLIENT_SERIAL,
+  SHRPX_LOGF_TLS_CLIENT_SUBJECT_NAME,
   SHRPX_LOGF_BACKEND_HOST,
   SHRPX_LOGF_BACKEND_PORT,
 };
@@ -148,7 +158,8 @@ struct LogSpec {
   Downstream *downstream;
   StringRef remote_addr;
   StringRef alpn;
-  const nghttp2::ssl::TLSSessionInfo *tls_info;
+  StringRef sni;
+  SSL *ssl;
   std::chrono::high_resolution_clock::time_point request_end_time;
   StringRef remote_port;
   uint16_t server_port;
index c7d3fa9..d087ac8 100644 (file)
@@ -32,7 +32,7 @@
 #include "shrpx_memcached_request.h"
 #include "shrpx_memcached_result.h"
 #include "shrpx_config.h"
-#include "shrpx_ssl.h"
+#include "shrpx_tls.h"
 #include "shrpx_log.h"
 #include "util.h"
 
@@ -150,11 +150,12 @@ int MemcachedConnection::initiate_connection() {
   assert(conn_.fd == -1);
 
   if (ssl_ctx_) {
-    auto ssl = ssl::create_ssl(ssl_ctx_);
+    auto ssl = tls::create_ssl(ssl_ctx_);
     if (!ssl) {
       return -1;
     }
     conn_.set_ssl(ssl);
+    conn_.tls.client_session_cache = &tls_session_cache_;
   }
 
   conn_.fd = util::create_nonblock_socket(addr_->su.storage.ss_family);
@@ -183,7 +184,7 @@ int MemcachedConnection::initiate_connection() {
       SSL_set_tlsext_host_name(conn_.tls.ssl, sni_name_.c_str());
     }
 
-    auto session = ssl::reuse_tls_session(tls_session_cache_);
+    auto session = tls::reuse_tls_session(tls_session_cache_);
     if (session) {
       SSL_set_session(conn_.tls.ssl, session);
       SSL_SESSION_free(session);
@@ -275,19 +276,11 @@ int MemcachedConnection::tls_handshake() {
   auto &tlsconf = get_config()->tls;
 
   if (!tlsconf.insecure &&
-      ssl::check_cert(conn_.tls.ssl, addr_, sni_name_) != 0) {
+      tls::check_cert(conn_.tls.ssl, addr_, sni_name_) != 0) {
     connect_blocker_.on_failure();
     return -1;
   }
 
-  if (!SSL_session_reused(conn_.tls.ssl)) {
-    auto tls_session = SSL_get0_session(conn_.tls.ssl);
-    if (tls_session) {
-      ssl::try_cache_tls_session(tls_session_cache_, *addr_, tls_session,
-                                 ev_now(conn_.loop));
-    }
-  }
-
   ev_timer_stop(conn_.loop, &conn_.rt);
   ev_timer_stop(conn_.loop, &conn_.wt);
 
@@ -545,8 +538,8 @@ int MemcachedConnection::parse_packet() {
 
       if (LOG_ENABLED(INFO)) {
         if (parse_state_.status_code) {
-          MCLOG(INFO, this) << "response returned error status: "
-                            << parse_state_.status_code;
+          MCLOG(INFO, this)
+              << "response returned error status: " << parse_state_.status_code;
         }
       }
 
index f2837a5..516996c 100644 (file)
@@ -33,7 +33,7 @@
 #include <ev.h>
 
 #include "shrpx_connection.h"
-#include "shrpx_ssl.h"
+#include "shrpx_tls.h"
 #include "shrpx_connect_blocker.h"
 #include "buffer.h"
 #include "network.h"
@@ -136,7 +136,7 @@ private:
   std::deque<MemcachedSendbuf> sendbufv_;
   std::function<int(MemcachedConnection &)> do_read_, do_write_;
   StringRef sni_name_;
-  ssl::TLSSessionCache tls_session_cache_;
+  tls::TLSSessionCache tls_session_cache_;
   ConnectBlocker connect_blocker_;
   MemcachedParseState parse_state_;
   const Address *addr_;
index 078a475..9a7c364 100644 (file)
@@ -34,6 +34,7 @@
 #include "shrpx_mruby.h"
 #include "shrpx_mruby_module.h"
 #include "shrpx_log.h"
+#include "shrpx_tls.h"
 
 namespace shrpx {
 
@@ -140,6 +141,262 @@ mrb_value env_get_tls_sni(mrb_state *mrb, mrb_value self) {
 }
 } // namespace
 
+namespace {
+mrb_value env_get_tls_client_fingerprint_md(mrb_state *mrb, const EVP_MD *md) {
+  auto data = static_cast<MRubyAssocData *>(mrb->ud);
+  auto downstream = data->downstream;
+  auto upstream = downstream->get_upstream();
+  auto handler = upstream->get_client_handler();
+  auto ssl = handler->get_ssl();
+
+  if (!ssl) {
+    return mrb_str_new_static(mrb, "", 0);
+  }
+
+  auto x = SSL_get_peer_certificate(ssl);
+  if (!x) {
+    return mrb_str_new_static(mrb, "", 0);
+  }
+
+  // Currently the largest hash value is SHA-256, which is 32 bytes.
+  std::array<uint8_t, 32> buf;
+  auto slen = tls::get_x509_fingerprint(buf.data(), buf.size(), x, md);
+  X509_free(x);
+  if (slen == -1) {
+    mrb_raise(mrb, E_RUNTIME_ERROR, "could not compute client fingerprint");
+  }
+
+  // TODO Use template version of format_hex
+  auto &balloc = downstream->get_block_allocator();
+  auto f = util::format_hex(balloc,
+                            StringRef{std::begin(buf), std::begin(buf) + slen});
+  return mrb_str_new(mrb, f.c_str(), f.size());
+}
+} // namespace
+
+namespace {
+mrb_value env_get_tls_client_fingerprint_sha256(mrb_state *mrb,
+                                                mrb_value self) {
+  return env_get_tls_client_fingerprint_md(mrb, EVP_sha256());
+}
+} // namespace
+
+namespace {
+mrb_value env_get_tls_client_fingerprint_sha1(mrb_state *mrb, mrb_value self) {
+  return env_get_tls_client_fingerprint_md(mrb, EVP_sha1());
+}
+} // namespace
+
+namespace {
+mrb_value env_get_tls_client_subject_name(mrb_state *mrb, mrb_value self) {
+  auto data = static_cast<MRubyAssocData *>(mrb->ud);
+  auto downstream = data->downstream;
+  auto upstream = downstream->get_upstream();
+  auto handler = upstream->get_client_handler();
+  auto ssl = handler->get_ssl();
+
+  if (!ssl) {
+    return mrb_str_new_static(mrb, "", 0);
+  }
+
+  auto x = SSL_get_peer_certificate(ssl);
+  if (!x) {
+    return mrb_str_new_static(mrb, "", 0);
+  }
+
+  auto &balloc = downstream->get_block_allocator();
+  auto name = tls::get_x509_subject_name(balloc, x);
+  X509_free(x);
+  return mrb_str_new(mrb, name.c_str(), name.size());
+}
+} // namespace
+
+namespace {
+mrb_value env_get_tls_client_issuer_name(mrb_state *mrb, mrb_value self) {
+  auto data = static_cast<MRubyAssocData *>(mrb->ud);
+  auto downstream = data->downstream;
+  auto upstream = downstream->get_upstream();
+  auto handler = upstream->get_client_handler();
+  auto ssl = handler->get_ssl();
+
+  if (!ssl) {
+    return mrb_str_new_static(mrb, "", 0);
+  }
+
+  auto x = SSL_get_peer_certificate(ssl);
+  if (!x) {
+    return mrb_str_new_static(mrb, "", 0);
+  }
+
+  auto &balloc = downstream->get_block_allocator();
+  auto name = tls::get_x509_issuer_name(balloc, x);
+  X509_free(x);
+  return mrb_str_new(mrb, name.c_str(), name.size());
+}
+} // namespace
+
+namespace {
+mrb_value env_get_tls_client_serial(mrb_state *mrb, mrb_value self) {
+  auto data = static_cast<MRubyAssocData *>(mrb->ud);
+  auto downstream = data->downstream;
+  auto upstream = downstream->get_upstream();
+  auto handler = upstream->get_client_handler();
+  auto ssl = handler->get_ssl();
+
+  if (!ssl) {
+    return mrb_str_new_static(mrb, "", 0);
+  }
+
+  auto x = SSL_get_peer_certificate(ssl);
+  if (!x) {
+    return mrb_str_new_static(mrb, "", 0);
+  }
+
+  auto &balloc = downstream->get_block_allocator();
+  auto sn = tls::get_x509_serial(balloc, x);
+  X509_free(x);
+  return mrb_str_new(mrb, sn.c_str(), sn.size());
+}
+} // namespace
+
+namespace {
+mrb_value env_get_tls_client_not_before(mrb_state *mrb, mrb_value self) {
+  auto data = static_cast<MRubyAssocData *>(mrb->ud);
+  auto downstream = data->downstream;
+  auto upstream = downstream->get_upstream();
+  auto handler = upstream->get_client_handler();
+  auto ssl = handler->get_ssl();
+
+  if (!ssl) {
+    return mrb_fixnum_value(0);
+  }
+
+  auto x = SSL_get_peer_certificate(ssl);
+  if (!x) {
+    return mrb_fixnum_value(0);
+  }
+
+  time_t t;
+  if (tls::get_x509_not_before(t, x) != 0) {
+    return mrb_fixnum_value(0);
+  }
+
+  return mrb_fixnum_value(t);
+}
+} // namespace
+
+namespace {
+mrb_value env_get_tls_client_not_after(mrb_state *mrb, mrb_value self) {
+  auto data = static_cast<MRubyAssocData *>(mrb->ud);
+  auto downstream = data->downstream;
+  auto upstream = downstream->get_upstream();
+  auto handler = upstream->get_client_handler();
+  auto ssl = handler->get_ssl();
+
+  if (!ssl) {
+    return mrb_fixnum_value(0);
+  }
+
+  auto x = SSL_get_peer_certificate(ssl);
+  if (!x) {
+    return mrb_fixnum_value(0);
+  }
+
+  time_t t;
+  if (tls::get_x509_not_after(t, x) != 0) {
+    return mrb_fixnum_value(0);
+  }
+
+  return mrb_fixnum_value(t);
+}
+} // namespace
+
+namespace {
+mrb_value env_get_tls_cipher(mrb_state *mrb, mrb_value self) {
+  auto data = static_cast<MRubyAssocData *>(mrb->ud);
+  auto downstream = data->downstream;
+  auto upstream = downstream->get_upstream();
+  auto handler = upstream->get_client_handler();
+  auto ssl = handler->get_ssl();
+
+  if (!ssl) {
+    return mrb_str_new_static(mrb, "", 0);
+  }
+
+  return mrb_str_new_cstr(mrb, SSL_get_cipher_name(ssl));
+}
+} // namespace
+
+namespace {
+mrb_value env_get_tls_protocol(mrb_state *mrb, mrb_value self) {
+  auto data = static_cast<MRubyAssocData *>(mrb->ud);
+  auto downstream = data->downstream;
+  auto upstream = downstream->get_upstream();
+  auto handler = upstream->get_client_handler();
+  auto ssl = handler->get_ssl();
+
+  if (!ssl) {
+    return mrb_str_new_static(mrb, "", 0);
+  }
+
+  return mrb_str_new_cstr(mrb, nghttp2::tls::get_tls_protocol(ssl));
+}
+} // namespace
+
+namespace {
+mrb_value env_get_tls_session_id(mrb_state *mrb, mrb_value self) {
+  auto data = static_cast<MRubyAssocData *>(mrb->ud);
+  auto downstream = data->downstream;
+  auto upstream = downstream->get_upstream();
+  auto handler = upstream->get_client_handler();
+  auto ssl = handler->get_ssl();
+
+  if (!ssl) {
+    return mrb_str_new_static(mrb, "", 0);
+  }
+
+  auto session = SSL_get_session(ssl);
+  if (!session) {
+    return mrb_str_new_static(mrb, "", 0);
+  }
+
+  unsigned int session_id_length = 0;
+  auto session_id = SSL_SESSION_get_id(session, &session_id_length);
+
+  // TODO Use template version of util::format_hex.
+  auto &balloc = downstream->get_block_allocator();
+  auto id = util::format_hex(balloc, StringRef{session_id, session_id_length});
+  return mrb_str_new(mrb, id.c_str(), id.size());
+}
+} // namespace
+
+namespace {
+mrb_value env_get_tls_session_reused(mrb_state *mrb, mrb_value self) {
+  auto data = static_cast<MRubyAssocData *>(mrb->ud);
+  auto downstream = data->downstream;
+  auto upstream = downstream->get_upstream();
+  auto handler = upstream->get_client_handler();
+  auto ssl = handler->get_ssl();
+
+  if (!ssl) {
+    return mrb_false_value();
+  }
+
+  return SSL_session_reused(ssl) ? mrb_true_value() : mrb_false_value();
+}
+} // namespace
+
+namespace {
+mrb_value env_get_alpn(mrb_state *mrb, mrb_value self) {
+  auto data = static_cast<MRubyAssocData *>(mrb->ud);
+  auto downstream = data->downstream;
+  auto upstream = downstream->get_upstream();
+  auto handler = upstream->get_client_handler();
+  auto alpn = handler->get_alpn();
+  return mrb_str_new(mrb, alpn.c_str(), alpn.size());
+}
+} // namespace
+
 void init_env_class(mrb_state *mrb, RClass *module) {
   auto env_class =
       mrb_define_class_under(mrb, module, "Env", mrb->object_class);
@@ -159,6 +416,29 @@ void init_env_class(mrb_state *mrb, RClass *module) {
                     MRB_ARGS_NONE());
   mrb_define_method(mrb, env_class, "tls_sni", env_get_tls_sni,
                     MRB_ARGS_NONE());
+  mrb_define_method(mrb, env_class, "tls_client_fingerprint_sha256",
+                    env_get_tls_client_fingerprint_sha256, MRB_ARGS_NONE());
+  mrb_define_method(mrb, env_class, "tls_client_fingerprint_sha1",
+                    env_get_tls_client_fingerprint_sha1, MRB_ARGS_NONE());
+  mrb_define_method(mrb, env_class, "tls_client_issuer_name",
+                    env_get_tls_client_issuer_name, MRB_ARGS_NONE());
+  mrb_define_method(mrb, env_class, "tls_client_subject_name",
+                    env_get_tls_client_subject_name, MRB_ARGS_NONE());
+  mrb_define_method(mrb, env_class, "tls_client_serial",
+                    env_get_tls_client_serial, MRB_ARGS_NONE());
+  mrb_define_method(mrb, env_class, "tls_client_not_before",
+                    env_get_tls_client_not_before, MRB_ARGS_NONE());
+  mrb_define_method(mrb, env_class, "tls_client_not_after",
+                    env_get_tls_client_not_after, MRB_ARGS_NONE());
+  mrb_define_method(mrb, env_class, "tls_cipher", env_get_tls_cipher,
+                    MRB_ARGS_NONE());
+  mrb_define_method(mrb, env_class, "tls_protocol", env_get_tls_protocol,
+                    MRB_ARGS_NONE());
+  mrb_define_method(mrb, env_class, "tls_session_id", env_get_tls_session_id,
+                    MRB_ARGS_NONE());
+  mrb_define_method(mrb, env_class, "tls_session_reused",
+                    env_get_tls_session_reused, MRB_ARGS_NONE());
+  mrb_define_method(mrb, env_class, "alpn", env_get_alpn, MRB_ARGS_NONE());
 }
 
 } // namespace mruby
index dff99ce..0cd0d03 100644 (file)
@@ -246,8 +246,9 @@ mrb_value request_mod_header(mrb_state *mrb, mrb_value self, bool repl) {
         continue;
       }
       if (i != p) {
-        headers[p++] = std::move(kv);
+        headers[p] = std::move(kv);
       }
+      ++p;
     }
     headers.resize(p);
   }
index 850d46e..62f46d5 100644 (file)
@@ -138,8 +138,9 @@ mrb_value response_mod_header(mrb_state *mrb, mrb_value self, bool repl) {
         continue;
       }
       if (i != p) {
-        headers[p++] = std::move(kv);
+        headers[p] = std::move(kv);
       }
+      ++p;
     }
     headers.resize(p);
   }
index 92531f8..82547c2 100644 (file)
 
 namespace shrpx {
 
-RNode::RNode() : s(nullptr), len(0), index(-1) {}
+RNode::RNode() : s(nullptr), len(0), index(-1), wildcard_index(-1) {}
 
-RNode::RNode(const char *s, size_t len, size_t index)
-    : s(s), len(len), index(index) {}
+RNode::RNode(const char *s, size_t len, ssize_t index, ssize_t wildcard_index)
+    : s(s), len(len), index(index), wildcard_index(wildcard_index) {}
 
 Router::Router() : balloc_(1024, 1024), root_{} {}
 
@@ -64,21 +64,30 @@ void add_next_node(RNode *node, std::unique_ptr<RNode> new_node) {
 } // namespace
 
 void Router::add_node(RNode *node, const char *pattern, size_t patlen,
-                      size_t index) {
+                      ssize_t index, ssize_t wildcard_index) {
   auto pat = make_string_ref(balloc_, StringRef{pattern, patlen});
-  auto new_node = make_unique<RNode>(pat.c_str(), pat.size(), index);
+  auto new_node =
+      make_unique<RNode>(pat.c_str(), pat.size(), index, wildcard_index);
   add_next_node(node, std::move(new_node));
 }
 
-size_t Router::add_route(const StringRef &pattern, size_t index) {
+size_t Router::add_route(const StringRef &pattern, size_t idx, bool wildcard) {
+  ssize_t index = -1, wildcard_index = -1;
+  if (wildcard) {
+    wildcard_index = idx;
+  } else {
+    index = idx;
+  }
+
   auto node = &root_;
   size_t i = 0;
 
   for (;;) {
     auto next_node = find_next_node(node, pattern[i]);
     if (next_node == nullptr) {
-      add_node(node, pattern.c_str() + i, pattern.size() - i, index);
-      return index;
+      add_node(node, pattern.c_str() + i, pattern.size() - i, index,
+               wildcard_index);
+      return idx;
     }
 
     node = next_node;
@@ -93,12 +102,22 @@ size_t Router::add_route(const StringRef &pattern, size_t index) {
       // The common prefix was matched
       if (slen == node->len) {
         // Complete match
-        if (node->index != -1) {
-          // Return the existing index for duplicates.
-          return node->index;
+        if (index != -1) {
+          if (node->index != -1) {
+            // Return the existing index for duplicates.
+            return node->index;
+          }
+          node->index = index;
+          return idx;
         }
-        node->index = index;
-        return index;
+
+        assert(wildcard_index != -1);
+
+        if (node->wildcard_index != -1) {
+          return node->wildcard_index;
+        }
+        node->wildcard_index = wildcard_index;
+        return idx;
       }
 
       if (slen > node->len) {
@@ -112,27 +131,30 @@ size_t Router::add_route(const StringRef &pattern, size_t index) {
     if (node->len > j) {
       // node must be split into 2 nodes.  new_node is now the child
       // of node.
-      auto new_node =
-          make_unique<RNode>(&node->s[j], node->len - j, node->index);
+      auto new_node = make_unique<RNode>(&node->s[j], node->len - j,
+                                         node->index, node->wildcard_index);
       std::swap(node->next, new_node->next);
 
       node->len = j;
       node->index = -1;
+      node->wildcard_index = -1;
 
       add_next_node(node, std::move(new_node));
 
       if (slen == j) {
         node->index = index;
-        return index;
+        node->wildcard_index = wildcard_index;
+        return idx;
       }
     }
 
     i += j;
 
     assert(pattern.size() > i);
-    add_node(node, pattern.c_str() + i, pattern.size() - i, index);
+    add_node(node, pattern.c_str() + i, pattern.size() - i, index,
+             wildcard_index);
 
-    return index;
+    return idx;
   }
 }
 
@@ -169,8 +191,10 @@ const RNode *match_complete(size_t *offset, const RNode *node,
 } // namespace
 
 namespace {
-const RNode *match_partial(const RNode *node, size_t offset, const char *first,
-                           const char *last) {
+const RNode *match_partial(bool *pattern_is_wildcard, const RNode *node,
+                           size_t offset, const char *first, const char *last) {
+  *pattern_is_wildcard = false;
+
   if (first == last) {
     if (node->len == offset) {
       return node;
@@ -207,8 +231,12 @@ const RNode *match_partial(const RNode *node, size_t offset, const char *first,
       return nullptr;
     }
 
-    if (node->index != -1 && node->s[node->len - 1] == '/') {
+    if (node->wildcard_index != -1) {
+      found_node = node;
+      *pattern_is_wildcard = true;
+    } else if (node->index != -1 && node->s[node->len - 1] == '/') {
       found_node = node;
+      *pattern_is_wildcard = false;
     }
 
     assert(node->len == offset + n);
@@ -233,6 +261,7 @@ const RNode *match_partial(const RNode *node, size_t offset, const char *first,
       if (node->len == n) {
         // Complete match with this node
         if (node->index != -1) {
+          *pattern_is_wildcard = false;
           return node;
         }
 
@@ -246,16 +275,21 @@ const RNode *match_partial(const RNode *node, size_t offset, const char *first,
       // pattern is "/foo/" and path is "/foo", we consider they
       // match.
       if (node->index != -1 && n + 1 == node->len && node->s[n] == '/') {
+        *pattern_is_wildcard = false;
         return node;
       }
 
       return found_node;
     }
 
-    // This is the case when pattern which ends with "/" is included
-    // in query.
-    if (node->index != -1 && node->s[node->len - 1] == '/') {
+    if (node->wildcard_index != -1) {
       found_node = node;
+      *pattern_is_wildcard = true;
+    } else if (node->index != -1 && node->s[node->len - 1] == '/') {
+      // This is the case when pattern which ends with "/" is included
+      // in query.
+      found_node = node;
+      *pattern_is_wildcard = false;
     }
 
     assert(node->len == n);
@@ -272,12 +306,14 @@ ssize_t Router::match(const StringRef &host, const StringRef &path) const {
     return -1;
   }
 
-  node = match_partial(node, offset, std::begin(path), std::end(path));
+  bool pattern_is_wildcard;
+  node = match_partial(&pattern_is_wildcard, node, offset, std::begin(path),
+                       std::end(path));
   if (node == nullptr || node == &root_) {
     return -1;
   }
 
-  return node->index;
+  return pattern_is_wildcard ? node->wildcard_index : node->index;
 }
 
 ssize_t Router::match(const StringRef &s) const {
index 8762cb3..295db7e 100644 (file)
@@ -38,7 +38,7 @@ namespace shrpx {
 
 struct RNode {
   RNode();
-  RNode(const char *s, size_t len, size_t index);
+  RNode(const char *s, size_t len, ssize_t index, ssize_t wildcard_index);
   RNode(RNode &&) = default;
   RNode(const RNode &) = delete;
   RNode &operator=(RNode &&) = default;
@@ -54,6 +54,10 @@ struct RNode {
   // Index of pattern if match ends in this node.  Note that we don't
   // store duplicated pattern.
   ssize_t index;
+  // Index of wildcard pattern if query includes this node as prefix
+  // and it still has suffix to match.  Note that we don't store
+  // duplicated pattern.
+  ssize_t wildcard_index;
 };
 
 class Router {
@@ -66,8 +70,13 @@ public:
   Router &operator=(const Router &) = delete;
 
   // Adds route |pattern| with its |index|.  If same pattern has
-  // already been added, the existing index is returned.
-  size_t add_route(const StringRef &pattern, size_t index);
+  // already been added, the existing index is returned.  If
+  // |wildcard| is true, |pattern| is considered as wildcard pattern,
+  // and all paths which have the |pattern| as prefix and are strictly
+  // longer than |pattern| match.  The wildcard pattern only works
+  // with match(const StringRef&, const StringRef&).
+  size_t add_route(const StringRef &pattern, size_t index,
+                   bool wildcard = false);
   // Returns the matched index of pattern.  -1 if there is no match.
   ssize_t match(const StringRef &host, const StringRef &path) const;
   // Returns the matched index of pattern |s|.  -1 if there is no
@@ -84,7 +93,8 @@ public:
   ssize_t match_prefix(size_t *nread, const RNode **last_node,
                        const StringRef &s) const;
 
-  void add_node(RNode *node, const char *pattern, size_t patlen, size_t index);
+  void add_node(RNode *node, const char *pattern, size_t patlen, ssize_t index,
+                ssize_t wildcard_index);
 
   void dump() const;
 
index 0248181..9a93e71 100644 (file)
@@ -33,6 +33,7 @@ namespace shrpx {
 struct Pattern {
   StringRef pattern;
   size_t idx;
+  bool wildcard;
 };
 
 void test_shrpx_router_match(void) {
@@ -88,6 +89,50 @@ void test_shrpx_router_match(void) {
   CU_ASSERT(5 == idx);
 }
 
+void test_shrpx_router_match_wildcard(void) {
+  constexpr auto patterns = std::array<Pattern, 6>{{
+      {StringRef::from_lit("nghttp2.org/"), 0},
+      {StringRef::from_lit("nghttp2.org/"), 1, true},
+      {StringRef::from_lit("nghttp2.org/alpha/"), 2},
+      {StringRef::from_lit("nghttp2.org/alpha/"), 3, true},
+      {StringRef::from_lit("nghttp2.org/bravo"), 4},
+      {StringRef::from_lit("nghttp2.org/bravo"), 5, true},
+  }};
+
+  Router router;
+
+  for (auto &p : patterns) {
+    router.add_route(p.pattern, p.idx, p.wildcard);
+  }
+
+  CU_ASSERT(0 == router.match(StringRef::from_lit("nghttp2.org"),
+                              StringRef::from_lit("/")));
+
+  CU_ASSERT(1 == router.match(StringRef::from_lit("nghttp2.org"),
+                              StringRef::from_lit("/a")));
+
+  CU_ASSERT(1 == router.match(StringRef::from_lit("nghttp2.org"),
+                              StringRef::from_lit("/charlie")));
+
+  CU_ASSERT(2 == router.match(StringRef::from_lit("nghttp2.org"),
+                              StringRef::from_lit("/alpha")));
+
+  CU_ASSERT(2 == router.match(StringRef::from_lit("nghttp2.org"),
+                              StringRef::from_lit("/alpha/")));
+
+  CU_ASSERT(3 == router.match(StringRef::from_lit("nghttp2.org"),
+                              StringRef::from_lit("/alpha/b")));
+
+  CU_ASSERT(4 == router.match(StringRef::from_lit("nghttp2.org"),
+                              StringRef::from_lit("/bravo")));
+
+  CU_ASSERT(5 == router.match(StringRef::from_lit("nghttp2.org"),
+                              StringRef::from_lit("/bravocharlie")));
+
+  CU_ASSERT(5 == router.match(StringRef::from_lit("nghttp2.org"),
+                              StringRef::from_lit("/bravo/")));
+}
+
 void test_shrpx_router_match_prefix(void) {
   auto patterns = std::vector<Pattern>{
       {StringRef::from_lit("gro.2ptthgn."), 0},
index 9f4ba66..03b49d2 100644 (file)
@@ -32,6 +32,7 @@
 namespace shrpx {
 
 void test_shrpx_router_match(void);
+void test_shrpx_router_match_wildcard(void);
 void test_shrpx_router_match_prefix(void);
 
 } // namespace shrpx
index fcce2dc..5e16bf3 100644 (file)
@@ -94,7 +94,7 @@ int shrpx_signal_set(sigset_t *set) {
 
 namespace {
 template <typename Signals>
-void signal_set_handler(void (*handler)(int), Signals &&sigs) {
+int signal_set_handler(void (*handler)(int), Signals &&sigs) {
   struct sigaction act {};
   act.sa_handler = handler;
   sigemptyset(&act.sa_mask);
@@ -102,11 +102,10 @@ void signal_set_handler(void (*handler)(int), Signals &&sigs) {
   for (auto sig : sigs) {
     rv = sigaction(sig, &act, nullptr);
     if (rv != 0) {
-      auto error = errno;
-      LOG(WARN) << "sigaction() with signal " << sig
-                << " failed: errno=" << error;
+      return -1;
     }
   }
+  return 0;
 }
 } // namespace
 
@@ -120,20 +119,20 @@ constexpr auto worker_proc_ign_signals =
                         GRACEFUL_SHUTDOWN_SIGNAL, RELOAD_SIGNAL, SIGPIPE}};
 } // namespace
 
-void shrpx_signal_set_master_proc_ign_handler() {
-  signal_set_handler(SIG_IGN, master_proc_ign_signals);
+int shrpx_signal_set_master_proc_ign_handler() {
+  return signal_set_handler(SIG_IGN, master_proc_ign_signals);
 }
 
-void shrpx_signal_unset_master_proc_ign_handler() {
-  signal_set_handler(SIG_DFL, master_proc_ign_signals);
+int shrpx_signal_unset_master_proc_ign_handler() {
+  return signal_set_handler(SIG_DFL, master_proc_ign_signals);
 }
 
-void shrpx_signal_set_worker_proc_ign_handler() {
-  signal_set_handler(SIG_IGN, worker_proc_ign_signals);
+int shrpx_signal_set_worker_proc_ign_handler() {
+  return signal_set_handler(SIG_IGN, worker_proc_ign_signals);
 }
 
-void shrpx_signal_unset_worker_proc_ign_handler() {
-  signal_set_handler(SIG_DFL, worker_proc_ign_signals);
+int shrpx_signal_unset_worker_proc_ign_handler() {
+  return signal_set_handler(SIG_DFL, worker_proc_ign_signals);
 }
 
 } // namespace shrpx
index c1c61b3..8f668ae 100644 (file)
@@ -49,11 +49,11 @@ int shrpx_signal_unblock_all();
 // -1.  The errno will indicate the error.
 int shrpx_signal_set(sigset_t *set);
 
-void shrpx_signal_set_master_proc_ign_handler();
-void shrpx_signal_unset_master_proc_ign_handler();
+int shrpx_signal_set_master_proc_ign_handler();
+int shrpx_signal_unset_master_proc_ign_handler();
 
-void shrpx_signal_set_worker_proc_ign_handler();
-void shrpx_signal_unset_worker_proc_ign_handler();
+int shrpx_signal_set_worker_proc_ign_handler();
+int shrpx_signal_unset_worker_proc_ign_handler();
 
 } // namespace shrpx
 
diff --git a/src/shrpx_spdy_upstream.cc b/src/shrpx_spdy_upstream.cc
deleted file mode 100644 (file)
index 562df51..0000000
+++ /dev/null
@@ -1,1427 +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 <netinet/tcp.h>
-#include <assert.h>
-#include <cerrno>
-#include <sstream>
-
-#include <nghttp2/nghttp2.h>
-
-#include "shrpx_client_handler.h"
-#include "shrpx_downstream.h"
-#include "shrpx_downstream_connection.h"
-#include "shrpx_config.h"
-#include "shrpx_http.h"
-#ifdef HAVE_MRUBY
-#include "shrpx_mruby.h"
-#endif // HAVE_MRUBY
-#include "shrpx_worker.h"
-#include "shrpx_http2_session.h"
-#include "shrpx_log.h"
-#include "http2.h"
-#include "util.h"
-#include "template.h"
-
-using namespace nghttp2;
-
-namespace shrpx {
-
-namespace {
-constexpr size_t MAX_BUFFER_SIZE = 32_k;
-} // namespace
-
-namespace {
-int32_t get_connection_window_size() {
-  return std::max(get_config()->http2.upstream.connection_window_size,
-                  static_cast<int32_t>(64_k));
-}
-} // namespace
-
-namespace {
-int32_t get_window_size() {
-  auto n = get_config()->http2.upstream.window_size;
-
-  // 65535 is the default window size of HTTP/2.  OTOH, the default
-  // window size of SPDY is 65536.  The configuration defaults to
-  // HTTP/2, so if we have 65535, we use 65536 for SPDY.
-  if (n == 65535) {
-    return 64_k;
-  }
-
-  return n;
-}
-} // namespace
-
-namespace {
-ssize_t send_callback(spdylay_session *session, const uint8_t *data, size_t len,
-                      int flags, void *user_data) {
-  auto upstream = static_cast<SpdyUpstream *>(user_data);
-  auto wb = upstream->get_response_buf();
-
-  if (wb->rleft() >= MAX_BUFFER_SIZE) {
-    return SPDYLAY_ERR_WOULDBLOCK;
-  }
-
-  wb->append(data, len);
-
-  return len;
-}
-} // namespace
-
-namespace {
-ssize_t recv_callback(spdylay_session *session, uint8_t *buf, size_t len,
-                      int flags, void *user_data) {
-  auto upstream = static_cast<SpdyUpstream *>(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<SpdyUpstream *>(user_data);
-  if (LOG_ENABLED(INFO)) {
-    ULOG(INFO, upstream) << "Stream stream_id=" << stream_id
-                         << " is being closed";
-  }
-  auto downstream = static_cast<Downstream *>(
-      spdylay_session_get_stream_user_data(session, stream_id));
-  if (!downstream) {
-    return;
-  }
-
-  auto &req = downstream->request();
-
-  upstream->consume(stream_id, req.unconsumed_body_length);
-
-  req.unconsumed_body_length = 0;
-
-  if (downstream->get_request_state() == Downstream::CONNECT_FAIL) {
-    upstream->remove_downstream(downstream);
-    // downstream was deleted
-
-    return;
-  }
-
-  if (downstream->can_detach_downstream_connection()) {
-    // Keep-alive
-    downstream->detach_downstream_connection();
-  }
-
-  downstream->set_request_state(Downstream::STREAM_CLOSED);
-
-  // 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.
-}
-} // namespace
-
-namespace {
-void on_ctrl_recv_callback(spdylay_session *session, spdylay_frame_type type,
-                           spdylay_frame *frame, void *user_data) {
-  auto upstream = static_cast<SpdyUpstream *>(user_data);
-  auto config = get_config();
-
-  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);
-
-    auto &req = downstream->request();
-
-    auto &balloc = downstream->get_block_allocator();
-
-    auto lgconf = log_config();
-    lgconf->update_tstamp(std::chrono::system_clock::now());
-    req.tstamp = lgconf->tstamp;
-
-    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;
-      // shut up scan-build
-      assert(nv[i + 1]);
-      header_buffer += strlen(nv[i]) + strlen(nv[i + 1]);
-    }
-
-    auto &httpconf = config->http;
-
-    // spdy does not define usage of trailer fields, and we ignores
-    // them.
-    if (header_buffer > httpconf.request_header_field_buffer ||
-        num_headers > httpconf.max_request_header_fields) {
-      upstream->rst_stream(downstream, SPDYLAY_INTERNAL_ERROR);
-      return;
-    }
-
-    for (size_t i = 0; nv[i]; i += 2) {
-      auto name = StringRef{nv[i]};
-      auto value = StringRef{nv[i + 1]};
-      auto token = http2::lookup_token(name.byte(), name.size());
-      req.fs.add_header_token(make_string_ref(balloc, StringRef{name}),
-                              make_string_ref(balloc, StringRef{value}), false,
-                              token);
-    }
-
-    if (req.fs.parse_content_length() != 0) {
-      if (upstream->error_reply(downstream, 400) != 0) {
-        ULOG(FATAL, upstream) << "error_reply failed";
-      }
-      return;
-    }
-
-    auto path = req.fs.header(http2::HD__PATH);
-    auto scheme = req.fs.header(http2::HD__SCHEME);
-    auto host = req.fs.header(http2::HD__HOST);
-    auto method = req.fs.header(http2::HD__METHOD);
-
-    if (!method) {
-      upstream->rst_stream(downstream, SPDYLAY_PROTOCOL_ERROR);
-      return;
-    }
-
-    auto method_token = http2::lookup_method_token(method->value);
-    if (method_token == -1) {
-      if (upstream->error_reply(downstream, 501) != 0) {
-        ULOG(FATAL, upstream) << "error_reply failed";
-      }
-      return;
-    }
-
-    auto is_connect = method_token == HTTP_CONNECT;
-    if (!path || !host || !http2::non_empty_value(host) ||
-        !http2::non_empty_value(path) ||
-        (!is_connect && (!scheme || !http2::non_empty_value(scheme)))) {
-      upstream->rst_stream(downstream, SPDYLAY_PROTOCOL_ERROR);
-      return;
-    }
-
-    if (std::find_if(std::begin(host->value), std::end(host->value),
-                     [](char c) { return c == '"' || c == '\\'; }) !=
-        std::end(host->value)) {
-      if (upstream->error_reply(downstream, 400) != 0) {
-        ULOG(FATAL, upstream) << "error_reply failed";
-      }
-      return;
-    }
-
-    if (scheme) {
-      for (auto c : scheme->value) {
-        if (!(util::is_alpha(c) || util::is_digit(c) || c == '+' || c == '-' ||
-              c == '.')) {
-          if (upstream->error_reply(downstream, 400) != 0) {
-            ULOG(FATAL, upstream) << "error_reply failed";
-          }
-          return;
-        }
-      }
-    }
-
-    // For other than CONNECT method, path must start with "/", except
-    // for OPTIONS method, which can take "*" as path.
-    if (!is_connect && path->value[0] != '/' &&
-        (method_token != HTTP_OPTIONS || path->value != "*")) {
-      upstream->rst_stream(downstream, SPDYLAY_PROTOCOL_ERROR);
-      return;
-    }
-
-    req.method = method_token;
-    if (is_connect) {
-      req.authority = path->value;
-    } else {
-      req.scheme = scheme->value;
-      req.authority = host->value;
-
-      auto handler = upstream->get_client_handler();
-      auto faddr = handler->get_upstream_addr();
-
-      if (config->http2_proxy && !faddr->alt_mode) {
-        req.path = path->value;
-      } else if (method_token == HTTP_OPTIONS &&
-                 path->value == StringRef::from_lit("*")) {
-        // Server-wide OPTIONS request.  Path is empty.
-      } else {
-        req.path = http2::rewrite_clean_path(balloc, path->value);
-      }
-    }
-
-    if (!(frame->syn_stream.hd.flags & SPDYLAY_CTRL_FLAG_FIN)) {
-      req.http2_expect_body = true;
-    } else if (req.fs.content_length == -1) {
-      req.fs.content_length = 0;
-    }
-
-    downstream->inspect_http2_request();
-
-    downstream->set_request_state(Downstream::HEADER_COMPLETE);
-
-#ifdef HAVE_MRUBY
-    auto handler = upstream->get_client_handler();
-    auto worker = handler->get_worker();
-    auto mruby_ctx = worker->get_mruby_context();
-
-    if (mruby_ctx->run_on_request_proc(downstream) != 0) {
-      if (upstream->error_reply(downstream, 500) != 0) {
-        ULOG(FATAL, upstream) << "error_reply failed";
-        return;
-      }
-      return;
-    }
-#endif // HAVE_MRUBY
-
-    if (frame->syn_stream.hd.flags & SPDYLAY_CTRL_FLAG_FIN) {
-      if (!downstream->validate_request_recv_body_length()) {
-        upstream->rst_stream(downstream, SPDYLAY_PROTOCOL_ERROR);
-        return;
-      }
-
-      downstream->disable_upstream_rtimer();
-      downstream->set_request_state(Downstream::MSG_COMPLETE);
-    }
-
-    if (downstream->get_response_state() == Downstream::MSG_COMPLETE) {
-      return;
-    }
-
-    upstream->start_downstream(downstream);
-
-    break;
-  }
-  default:
-    break;
-  }
-}
-} // namespace
-
-void SpdyUpstream::start_downstream(Downstream *downstream) {
-  if (downstream_queue_.can_activate(downstream->request().authority)) {
-    initiate_downstream(downstream);
-    return;
-  }
-
-  downstream_queue_.mark_blocked(downstream);
-}
-
-void SpdyUpstream::initiate_downstream(Downstream *downstream) {
-  int rv;
-
-  auto dconn = handler_->get_downstream_connection(rv, downstream);
-
-  if (!dconn ||
-      (rv = downstream->attach_downstream_connection(std::move(dconn))) != 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);
-
-  auto &req = downstream->request();
-  if (!req.http2_expect_body) {
-    if (downstream->end_upload_data() != 0) {
-      if (downstream->get_response_state() != Downstream::MSG_COMPLETE) {
-        rst_stream(downstream, SPDYLAY_INTERNAL_ERROR);
-      }
-    }
-  }
-}
-
-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<SpdyUpstream *>(user_data);
-  auto downstream = static_cast<Downstream *>(
-      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) {
-    if (downstream->get_response_state() != Downstream::MSG_COMPLETE) {
-      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, get_connection_window_size())) {
-    if (LOG_ENABLED(INFO)) {
-      ULOG(INFO, upstream) << "Flow control error on connection: "
-                           << "recv_window_size="
-                           << spdylay_session_get_recv_data_length(session)
-                           << ", window_size=" << get_connection_window_size();
-    }
-    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, get_window_size())) {
-    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=" << get_window_size();
-    }
-    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<SpdyUpstream *>(user_data);
-  auto downstream = static_cast<Downstream *>(
-      spdylay_session_get_stream_user_data(session, stream_id));
-
-  if (downstream && (flags & SPDYLAY_DATA_FLAG_FIN)) {
-    if (!downstream->validate_request_recv_body_length()) {
-      upstream->rst_stream(downstream, SPDYLAY_PROTOCOL_ERROR);
-      return;
-    }
-
-    downstream->disable_upstream_rtimer();
-    if (downstream->end_upload_data() != 0) {
-      if (downstream->get_response_state() != Downstream::MSG_COMPLETE) {
-        upstream->rst_stream(downstream, SPDYLAY_INTERNAL_ERROR);
-      }
-    }
-    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<SpdyUpstream *>(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<Downstream *>(
-        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<SpdyUpstream *>(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<SpdyUpstream *>(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
-
-namespace {
-size_t downstream_queue_size(Worker *worker) {
-  auto &downstreamconf = *worker->get_downstream_config();
-
-  if (get_config()->http2_proxy) {
-    return downstreamconf.connections_per_host;
-  }
-
-  return downstreamconf.connections_per_frontend;
-}
-} // namespace
-
-SpdyUpstream::SpdyUpstream(uint16_t version, ClientHandler *handler)
-    : wb_(handler->get_worker()->get_mcpool()),
-      downstream_queue_(downstream_queue_size(handler->get_worker()),
-                        !get_config()->http2_proxy),
-      handler_(handler),
-      session_(nullptr) {
-  spdylay_session_callbacks 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 = 64_k;
-  rv = spdylay_session_set_option(session_,
-                                  SPDYLAY_OPT_MAX_RECV_CTRL_FRAME_BUFFER,
-                                  &max_buffer, sizeof(max_buffer));
-  assert(rv == 0);
-
-  auto config = get_config();
-  auto &http2conf = config->http2;
-
-  auto faddr = handler_->get_upstream_addr();
-
-  // We use automatic WINDOW_UPDATE for API endpoints.  Since SPDY is
-  // going to be deprecated in the future, and the default stream
-  // window is large enough for API request body (64KiB), we don't
-  // expand window size depending on the options.
-  int32_t initial_window_size;
-  if (version >= SPDYLAY_PROTO_SPDY3 && !faddr->alt_mode) {
-    int val = 1;
-    flow_control_ = true;
-    initial_window_size = get_window_size();
-    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<spdylay_settings_entry, 2> entry;
-  size_t num_entry = 1;
-  entry[0].settings_id = SPDYLAY_SETTINGS_MAX_CONCURRENT_STREAMS;
-  entry[0].value = http2conf.upstream.max_concurrent_streams;
-  entry[0].flags = SPDYLAY_ID_FLAG_SETTINGS_NONE;
-
-  if (flow_control_) {
-    ++num_entry;
-    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(), num_entry);
-  assert(rv == 0);
-
-  auto connection_window_size = get_connection_window_size();
-
-  if (flow_control_ && version >= SPDYLAY_PROTO_SPDY3_1 &&
-      connection_window_size > static_cast<int32_t>(64_k)) {
-    int32_t delta = connection_window_size - SPDYLAY_INITIAL_WINDOW_SIZE;
-    rv = spdylay_submit_window_update(session_, 0, delta);
-    assert(rv == 0);
-  }
-
-  handler_->reset_upstream_read_timeout(
-      config->conn.upstream.timeout.http2_read);
-
-  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;
-
-  if (wb_.rleft() >= MAX_BUFFER_SIZE) {
-    return 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 && 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_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 == SHRPX_ERR_DCONN_CANCELED) {
-      downstream->pop_downstream_connection();
-      handler_->signal_write();
-      return 0;
-    }
-    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);
-    }
-    if (downstream->can_detach_downstream_connection()) {
-      // 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 rv;
-  }
-  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();
-  }
-
-  // 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";
-    }
-  }
-
-  // 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<Downstream *>(source->ptr);
-  auto upstream = static_cast<SpdyUpstream *>(downstream->get_upstream());
-  auto body = downstream->get_response_buf();
-  assert(body);
-
-  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->response_sent_body_length += nread;
-  }
-
-  return nread;
-}
-} // namespace
-
-int SpdyUpstream::send_reply(Downstream *downstream, const uint8_t *body,
-                             size_t bodylen) {
-  int rv;
-
-  spdylay_data_provider data_prd, *data_prd_ptr = nullptr;
-  if (bodylen) {
-    data_prd.source.ptr = downstream;
-    data_prd.read_callback = spdy_data_read_callback;
-    data_prd_ptr = &data_prd;
-  }
-
-  const auto &resp = downstream->response();
-  auto &balloc = downstream->get_block_allocator();
-
-  auto status_line = http2::stringify_status(balloc, resp.http_status);
-
-  const auto &headers = resp.fs.headers();
-
-  auto config = get_config();
-  auto &httpconf = config->http;
-
-  auto nva = std::vector<const char *>();
-  // 6 for :status, :version and server.  1 for last terminal nullptr.
-  nva.reserve(6 + headers.size() * 2 +
-              httpconf.add_response_headers.size() * 2 + 1);
-
-  nva.push_back(":status");
-  nva.push_back(status_line.c_str());
-  nva.push_back(":version");
-  nva.push_back("HTTP/1.1");
-
-  for (auto &kv : headers) {
-    if (kv.name.empty() || kv.name[0] == ':') {
-      continue;
-    }
-    switch (kv.token) {
-    case http2::HD_CONNECTION:
-    case http2::HD_KEEP_ALIVE:
-    case http2::HD_PROXY_CONNECTION:
-    case http2::HD_TRANSFER_ENCODING:
-      continue;
-    }
-    nva.push_back(kv.name.c_str());
-    nva.push_back(kv.value.c_str());
-  }
-
-  if (!resp.fs.header(http2::HD_SERVER)) {
-    nva.push_back("server");
-    nva.push_back(config->http.server_name.c_str());
-  }
-
-  for (auto &p : httpconf.add_response_headers) {
-    nva.push_back(p.name.c_str());
-    nva.push_back(p.value.c_str());
-  }
-
-  nva.push_back(nullptr);
-
-  rv = spdylay_submit_response(session_, downstream->get_stream_id(),
-                               nva.data(), data_prd_ptr);
-  if (rv < SPDYLAY_ERR_FATAL) {
-    ULOG(FATAL, this) << "spdylay_submit_response() failed: "
-                      << spdylay_strerror(rv);
-    return -1;
-  }
-
-  auto buf = downstream->get_response_buf();
-
-  buf->append(body, bodylen);
-
-  downstream->set_response_state(Downstream::MSG_COMPLETE);
-
-  if (data_prd_ptr) {
-    downstream->reset_upstream_wtimer();
-  }
-
-  return 0;
-}
-
-int SpdyUpstream::error_reply(Downstream *downstream,
-                              unsigned int status_code) {
-  int rv;
-  auto &resp = downstream->response();
-  auto &balloc = downstream->get_block_allocator();
-
-  auto html = http::create_error_html(balloc, status_code);
-  resp.http_status = status_code;
-  auto body = downstream->get_response_buf();
-  body->append(html);
-  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;
-
-  auto lgconf = log_config();
-  lgconf->update_tstamp(std::chrono::system_clock::now());
-
-  auto content_length = util::make_string_ref_uint(balloc, html.size());
-  auto status_line = http2::stringify_status(balloc, status_code);
-
-  const char *nv[] = {":status",        status_line.c_str(),
-                      ":version",       "http/1.1",
-                      "content-type",   "text/html; charset=UTF-8",
-                      "server",         get_config()->http.server_name.c_str(),
-                      "content-length", content_length.c_str(),
-                      "date",           lgconf->tstamp->time_http.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;
-  }
-
-  downstream->reset_upstream_wtimer();
-
-  return 0;
-}
-
-Downstream *SpdyUpstream::add_pending_downstream(int32_t stream_id) {
-  auto downstream =
-      make_unique<Downstream>(this, handler_->get_mcpool(), stream_id);
-  spdylay_session_set_stream_user_data(session_, stream_id, downstream.get());
-  auto res = downstream.get();
-
-  downstream_queue_.add_pending(std::move(downstream));
-
-  handler_->stop_read_timer();
-
-  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);
-  }
-
-  if (downstream_queue_.get_downstreams() == nullptr) {
-    handler_->repeat_read_timer();
-  }
-}
-
-// 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) {
-  auto &resp = downstream->response();
-
-  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.
-    resp.fs.clear_headers();
-
-    return 0;
-  }
-
-  const auto &req = downstream->request();
-  auto &balloc = downstream->get_block_allocator();
-
-#ifdef HAVE_MRUBY
-  auto worker = handler_->get_worker();
-  auto mruby_ctx = worker->get_mruby_context();
-
-  if (mruby_ctx->run_on_response_proc(downstream) != 0) {
-    if (error_reply(downstream, 500) != 0) {
-      return -1;
-    }
-    // Returning -1 will signal deletion of dconn.
-    return -1;
-  }
-
-  if (downstream->get_response_state() == Downstream::MSG_COMPLETE) {
-    return -1;
-  }
-#endif // HAVE_MRUBY
-
-  if (LOG_ENABLED(INFO)) {
-    DLOG(INFO, downstream) << "HTTP response header completed";
-  }
-
-  auto config = get_config();
-  auto &httpconf = config->http;
-
-  if (!config->http2_proxy && !httpconf.no_location_rewrite) {
-    downstream->rewrite_location_response_header(req.scheme);
-  }
-
-  // 8 means server, :status, :version and possible via header field.
-  auto nv =
-      make_unique<const char *[]>(resp.fs.headers().size() * 2 + 8 +
-                                  httpconf.add_response_headers.size() * 2 + 1);
-
-  size_t hdidx = 0;
-  std::string via_value;
-  auto status_line = http2::stringify_status(balloc, resp.http_status);
-
-  nv[hdidx++] = ":status";
-  nv[hdidx++] = status_line.c_str();
-  nv[hdidx++] = ":version";
-  nv[hdidx++] = "HTTP/1.1";
-  for (auto &hd : resp.fs.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 && !httpconf.no_server_rewrite) {
-    nv[hdidx++] = "server";
-    nv[hdidx++] = httpconf.server_name.c_str();
-  } else {
-    auto server = resp.fs.header(http2::HD_SERVER);
-    if (server) {
-      nv[hdidx++] = "server";
-      nv[hdidx++] = server->value.c_str();
-    }
-  }
-
-  auto via = resp.fs.header(http2::HD_VIA);
-  if (httpconf.no_via) {
-    if (via) {
-      nv[hdidx++] = "via";
-      nv[hdidx++] = via->value.c_str();
-    }
-  } else {
-    if (via) {
-      via_value = via->value.str();
-      via_value += ", ";
-    }
-    std::array<char, 16> viabuf;
-    auto end = http::create_via_header_value(std::begin(viabuf),
-                                             resp.http_major, resp.http_minor);
-    via_value.append(std::begin(viabuf), end);
-    nv[hdidx++] = "via";
-    nv[hdidx++] = via_value.c_str();
-  }
-
-  for (auto &p : httpconf.add_response_headers) {
-    nv[hdidx++] = p.name.c_str();
-    nv[hdidx++] = p.value.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;
-  }
-
-  downstream->reset_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(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";
-  }
-
-  auto &resp = downstream->response();
-
-  if (!downstream->validate_response_recv_body_length()) {
-    rst_stream(downstream, SPDYLAY_PROTOCOL_ERROR);
-    resp.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()) {
-    if (consume(downstream->get_stream_id(), consumed) != 0) {
-      return -1;
-    }
-
-    auto &req = downstream->request();
-
-    req.consume(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::on_downstream_abort_request_with_https_redirect(
-    Downstream *downstream) {
-  // This should not be called since SPDY is only available with TLS.
-  assert(0);
-  return 0;
-}
-
-int SpdyUpstream::consume(int32_t stream_id, size_t len) {
-  int rv;
-
-  if (!get_flow_control()) {
-    return 0;
-  }
-
-  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);
-
-  handler_->signal_write();
-
-  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(Downstream *downstream, bool no_retry) {
-  int rv;
-
-  if (downstream->get_dispatch_state() != Downstream::DISPATCH_ACTIVE) {
-    // This is error condition when we failed push_request_headers()
-    // in initiate_downstream().  Otherwise, we have
-    // Downstream::DISPATCH_ACTIVE state, or we did not set
-    // DownstreamConnection.
-    downstream->pop_downstream_connection();
-
-    handler_->signal_write();
-
-    return 0;
-  }
-
-  if (!downstream->request_submission_ready()) {
-    if (downstream->get_response_state() == Downstream::MSG_COMPLETE) {
-      // We have got all response body already.  Send it off.
-      downstream->pop_downstream_connection();
-      return 0;
-    }
-    rst_stream(downstream, SPDYLAY_INTERNAL_ERROR);
-    downstream->pop_downstream_connection();
-
-    handler_->signal_write();
-
-    return 0;
-  }
-
-  downstream->pop_downstream_connection();
-
-  downstream->add_retry();
-
-  std::unique_ptr<DownstreamConnection> dconn;
-
-  if (no_retry || downstream->no_more_retry()) {
-    goto fail;
-  }
-
-  // downstream connection is clean; we can retry with new
-  // downstream connection.
-
-  dconn = handler_->get_downstream_connection(rv, downstream);
-  if (!dconn) {
-    goto fail;
-  }
-
-  rv = downstream->attach_downstream_connection(std::move(dconn));
-  if (rv != 0) {
-    goto fail;
-  }
-
-  rv = downstream->push_request_headers();
-  if (rv != 0) {
-    goto fail;
-  }
-
-  return 0;
-
-fail:
-  if (on_downstream_abort_request(downstream, 503) != 0) {
-    rst_stream(downstream, SPDYLAY_INTERNAL_ERROR);
-  }
-  downstream->pop_downstream_connection();
-
-  handler_->signal_write();
-
-  return 0;
-}
-
-int SpdyUpstream::initiate_push(Downstream *downstream, const StringRef &uri) {
-  return 0;
-}
-
-int SpdyUpstream::response_riovec(struct iovec *iov, int iovcnt) const {
-  if (iovcnt == 0 || wb_.rleft() == 0) {
-    return 0;
-  }
-
-  return wb_.riovec(iov, iovcnt);
-}
-
-void SpdyUpstream::response_drain(size_t n) { wb_.drain(n); }
-
-bool SpdyUpstream::response_empty() const { return wb_.rleft() == 0; }
-
-DefaultMemchunks *SpdyUpstream::get_response_buf() { return &wb_; }
-
-Downstream *
-SpdyUpstream::on_downstream_push_promise(Downstream *downstream,
-                                         int32_t promised_stream_id) {
-  return nullptr;
-}
-
-int SpdyUpstream::on_downstream_push_promise_complete(
-    Downstream *downstream, Downstream *promised_downstream) {
-  return -1;
-}
-
-bool SpdyUpstream::push_enabled() const { return false; }
-
-void SpdyUpstream::cancel_premature_downstream(
-    Downstream *promised_downstream) {}
-
-} // namespace shrpx
diff --git a/src/shrpx_spdy_upstream.h b/src/shrpx_spdy_upstream.h
deleted file mode 100644 (file)
index 826a887..0000000
+++ /dev/null
@@ -1,113 +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 <memory>
-
-#include <ev.h>
-
-#include <spdylay/spdylay.h>
-
-#include "shrpx_upstream.h"
-#include "shrpx_downstream_queue.h"
-#include "memchunk.h"
-#include "buffer.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 int
-  on_downstream_abort_request_with_https_redirect(Downstream *downstream);
-  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);
-  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(Downstream *downstream, bool no_retry);
-
-  virtual int send_reply(Downstream *downstream, const uint8_t *body,
-                         size_t bodylen);
-  virtual int initiate_push(Downstream *downstream, const StringRef &uri);
-  virtual int response_riovec(struct iovec *iov, int iovcnt) const;
-  virtual void response_drain(size_t n);
-  virtual bool response_empty() const;
-
-  virtual Downstream *on_downstream_push_promise(Downstream *downstream,
-                                                 int32_t promised_stream_id);
-  virtual int
-  on_downstream_push_promise_complete(Downstream *downstream,
-                                      Downstream *promised_downstream);
-  virtual bool push_enabled() const;
-  virtual void cancel_premature_downstream(Downstream *promised_downstream);
-
-  bool get_flow_control() const;
-
-  int consume(int32_t stream_id, size_t len);
-
-  void start_downstream(Downstream *downstream);
-  void initiate_downstream(Downstream *downstream);
-
-  DefaultMemchunks *get_response_buf();
-
-private:
-  DefaultMemchunks wb_;
-  DownstreamQueue downstream_queue_;
-  ClientHandler *handler_;
-  spdylay_session *session_;
-  bool flow_control_;
-};
-
-} // namespace shrpx
-
-#endif // SHRPX_SPDY_UPSTREAM_H
similarity index 77%
rename from src/shrpx_ssl.cc
rename to src/shrpx_tls.cc
index 56280fa..87ca928 100644 (file)
@@ -22,7 +22,7 @@
  * OF 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"
+#include "shrpx_tls.h"
 
 #ifdef HAVE_SYS_SOCKET_H
 #include <sys/socket.h>
 #include <openssl/x509v3.h>
 #include <openssl/rand.h>
 #include <openssl/dh.h>
+#ifndef OPENSSL_NO_OCSP
+#include <openssl/ocsp.h>
+#endif // OPENSSL_NO_OCSP
 
 #include <nghttp2/nghttp2.h>
 
-#ifdef HAVE_SPDYLAY
-#include <spdylay/spdylay.h>
-#endif // HAVE_SPDYLAY
-
 #include "shrpx_log.h"
 #include "shrpx_client_handler.h"
 #include "shrpx_config.h"
 #include "shrpx_memcached_dispatcher.h"
 #include "shrpx_connection_handler.h"
 #include "util.h"
-#include "ssl.h"
+#include "tls.h"
 #include "template.h"
 #include "ssl_compat.h"
+#include "timegm.h"
 
 using namespace nghttp2;
 
 namespace shrpx {
 
-namespace ssl {
+namespace tls {
 
 #if !OPENSSL_1_1_API
 namespace {
@@ -95,6 +95,12 @@ 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);
+    if (err == X509_V_ERR_CERT_HAS_EXPIRED && depth == 0 &&
+        get_config()->tls.client_verify.tolerate_expired) {
+      LOG(INFO) << "The client certificate has expired, but is accepted by "
+                   "configuration";
+      return 1;
+    }
     LOG(ERROR) << "client certificate verify error:num=" << err << ":"
                << X509_verify_cert_error_string(err) << ":depth=" << depth;
   }
@@ -146,6 +152,8 @@ int ssl_pem_passwd_cb(char *buf, int size, int rwflag, void *user_data) {
 } // namespace
 
 namespace {
+// *al is set to SSL_AD_UNRECOGNIZED_NAME by openssl, so we don't have
+// to set it explicitly.
 int servername_callback(SSL *ssl, int *al, void *arg) {
   auto conn = static_cast<Connection *>(SSL_get_app_data(ssl));
   auto handler = static_cast<ClientHandler *>(conn->data);
@@ -153,13 +161,13 @@ int servername_callback(SSL *ssl, int *al, void *arg) {
 
   auto rawhost = SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name);
   if (rawhost == nullptr) {
-    return SSL_TLSEXT_ERR_OK;
+    return SSL_TLSEXT_ERR_NOACK;
   }
 
   auto len = strlen(rawhost);
   // NI_MAXHOST includes terminal NULL.
   if (len == 0 || len + 1 > NI_MAXHOST) {
-    return SSL_TLSEXT_ERR_OK;
+    return SSL_TLSEXT_ERR_NOACK;
   }
 
   std::array<uint8_t, NI_MAXHOST> buf;
@@ -170,18 +178,15 @@ int servername_callback(SSL *ssl, int *al, void *arg) {
 
   auto hostname = StringRef{std::begin(buf), end_buf};
 
-  handler->set_tls_sni(hostname);
-
   auto cert_tree = worker->get_cert_lookup_tree();
-  if (!cert_tree) {
-    return SSL_TLSEXT_ERR_OK;
-  }
 
   auto idx = cert_tree->lookup(hostname);
   if (idx == -1) {
-    return SSL_TLSEXT_ERR_OK;
+    return SSL_TLSEXT_ERR_NOACK;
   }
 
+  handler->set_tls_sni(hostname);
+
   auto conn_handler = worker->get_connection_handler();
 
   const auto &ssl_ctx_list = conn_handler->get_indexed_ssl_ctx(idx);
@@ -189,19 +194,43 @@ int servername_callback(SSL *ssl, int *al, void *arg) {
 
 #if !defined(OPENSSL_IS_BORINGSSL) && !defined(LIBRESSL_VERSION_NUMBER) &&     \
     OPENSSL_VERSION_NUMBER >= 0x10002000L
-  // boringssl removed SSL_get_sigalgs.
-  auto num_sigalg =
-      SSL_get_sigalgs(ssl, 0, nullptr, nullptr, nullptr, nullptr, nullptr);
+  auto num_shared_curves = SSL_get_shared_curve(ssl, -1);
+
+  for (auto i = 0; i < num_shared_curves; ++i) {
+    auto shared_curve = SSL_get_shared_curve(ssl, i);
 
-  for (auto i = 0; i < num_sigalg; ++i) {
-    int sigalg;
-    SSL_get_sigalgs(ssl, i, nullptr, nullptr, &sigalg, nullptr, nullptr);
     for (auto ssl_ctx : ssl_ctx_list) {
       auto cert = SSL_CTX_get0_certificate(ssl_ctx);
-      // X509_get_signature_nid is available since OpenSSL 1.0.2.
-      auto cert_sigalg = X509_get_signature_nid(cert);
 
-      if (sigalg == cert_sigalg) {
+#if OPENSSL_1_1_API
+      auto pubkey = X509_get0_pubkey(cert);
+#else  // !OPENSSL_1_1_API
+      auto pubkey = X509_get_pubkey(cert);
+#endif // !OPENSSL_1_1_API
+
+      if (EVP_PKEY_base_id(pubkey) != EVP_PKEY_EC) {
+        continue;
+      }
+
+#if OPENSSL_1_1_API
+      auto eckey = EVP_PKEY_get0_EC_KEY(pubkey);
+#else  // !OPENSSL_1_1_API
+      auto eckey = EVP_PKEY_get1_EC_KEY(pubkey);
+#endif // !OPENSSL_1_1_API
+
+      if (eckey == nullptr) {
+        continue;
+      }
+
+      auto ecgroup = EC_KEY_get0_group(eckey);
+      auto cert_curve = EC_GROUP_get_curve_name(ecgroup);
+
+#if !OPENSSL_1_1_API
+      EC_KEY_free(eckey);
+      EVP_PKEY_free(pubkey);
+#endif // !OPENSSL_1_1_API
+
+      if (shared_curve == cert_curve) {
         SSL_set_SSL_CTX(ssl, ssl_ctx);
         return SSL_TLSEXT_ERR_OK;
       }
@@ -262,6 +291,20 @@ constexpr auto MEMCACHED_SESSION_CACHE_KEY_PREFIX =
     StringRef::from_lit("nghttpx:tls-session-cache:");
 
 namespace {
+int tls_session_client_new_cb(SSL *ssl, SSL_SESSION *session) {
+  auto conn = static_cast<Connection *>(SSL_get_app_data(ssl));
+  if (conn->tls.client_session_cache == nullptr) {
+    return 0;
+  }
+
+  try_cache_tls_session(conn->tls.client_session_cache, session,
+                        ev_now(conn->loop));
+
+  return 0;
+}
+} // namespace
+
+namespace {
 int tls_session_new_cb(SSL *ssl, SSL_SESSION *session) {
   auto conn = static_cast<Connection *>(SSL_get_app_data(ssl));
   auto handler = static_cast<ClientHandler *>(conn->data);
@@ -269,6 +312,12 @@ int tls_session_new_cb(SSL *ssl, SSL_SESSION *session) {
   auto dispatcher = worker->get_session_cache_memcached_dispatcher();
   auto &balloc = handler->get_block_allocator();
 
+#ifdef TLS1_3_VERSION
+  if (SSL_version(ssl) == TLS1_3_VERSION) {
+    return 0;
+  }
+#endif // TLS1_3_VERSION
+
   const unsigned char *id;
   unsigned int idlen;
 
@@ -323,6 +372,10 @@ SSL_SESSION *tls_session_get_cb(SSL *ssl,
   auto dispatcher = worker->get_session_cache_memcached_dispatcher();
   auto &balloc = handler->get_block_allocator();
 
+  if (idlen == 0) {
+    return nullptr;
+  }
+
   if (conn->tls.cached_session) {
     if (LOG_ENABLED(INFO)) {
       LOG(INFO) << "Memcached: found cached session, id="
@@ -511,15 +564,32 @@ int alpn_select_proto_cb(SSL *ssl, const unsigned char **out,
 #endif // OPENSSL_VERSION_NUMBER >= 0x10002000L
 
 #if !LIBRESSL_IN_USE && OPENSSL_VERSION_NUMBER >= 0x10002000L
-namespace {
-// https://tools.ietf.org/html/rfc6962#section-6
-constexpr unsigned int TLS_EXT_SIGNED_CERTIFICATE_TIMESTAMP = 18;
-} // namespace
+
+#ifndef TLSEXT_TYPE_signed_certificate_timestamp
+#define TLSEXT_TYPE_signed_certificate_timestamp 18
+#endif // !TLSEXT_TYPE_signed_certificate_timestamp
 
 namespace {
-int sct_add_cb(SSL *ssl, unsigned int ext_type, const unsigned char **out,
-               size_t *outlen, int *al, void *add_arg) {
-  assert(ext_type == TLS_EXT_SIGNED_CERTIFICATE_TIMESTAMP);
+int sct_add_cb(SSL *ssl, unsigned int ext_type, unsigned int context,
+               const unsigned char **out, size_t *outlen, X509 *x,
+               size_t chainidx, int *al, void *add_arg) {
+  assert(ext_type == TLSEXT_TYPE_signed_certificate_timestamp);
+
+  auto conn = static_cast<Connection *>(SSL_get_app_data(ssl));
+  if (!conn->tls.sct_requested) {
+    return 0;
+  }
+
+  if (LOG_ENABLED(INFO)) {
+    LOG(INFO) << "sct_add_cb is called, chainidx=" << chainidx << ", x=" << x
+              << ", context=" << std::hex << context;
+  }
+
+  // We only have SCTs for leaf certificate.
+  if (chainidx != 0) {
+    return 0;
+  }
+
   auto ssl_ctx = SSL_get_SSL_CTX(ssl);
   auto tls_ctx_data =
       static_cast<TLSContextData *>(SSL_CTX_get_app_data(ssl_ctx));
@@ -532,22 +602,57 @@ int sct_add_cb(SSL *ssl, unsigned int ext_type, const unsigned char **out,
 } // namespace
 
 namespace {
-void sct_free_cb(SSL *ssl, unsigned int ext_type, const unsigned char *out,
-                 void *add_arg) {
-  assert(ext_type == TLS_EXT_SIGNED_CERTIFICATE_TIMESTAMP);
+void sct_free_cb(SSL *ssl, unsigned int ext_type, unsigned int context,
+                 const unsigned char *out, void *add_arg) {
+  assert(ext_type == TLSEXT_TYPE_signed_certificate_timestamp);
 }
 } // namespace
 
 namespace {
-int sct_parse_cb(SSL *ssl, unsigned int ext_type, const unsigned char *in,
-                 size_t inlen, int *al, void *parse_arg) {
-  assert(ext_type == TLS_EXT_SIGNED_CERTIFICATE_TIMESTAMP);
+int sct_parse_cb(SSL *ssl, unsigned int ext_type, unsigned int context,
+                 const unsigned char *in, size_t inlen, X509 *x,
+                 size_t chainidx, int *al, void *parse_arg) {
+  assert(ext_type == TLSEXT_TYPE_signed_certificate_timestamp);
   // client SHOULD send 0 length extension_data, but it is still
   // SHOULD, and not MUST.
 
+  // For TLSv1.3 Certificate message, sct_add_cb is called even if
+  // client has not sent signed_certificate_timestamp extension in its
+  // ClientHello.  Explicitly remember that client has included it
+  // here.
+  auto conn = static_cast<Connection *>(SSL_get_app_data(ssl));
+  conn->tls.sct_requested = true;
+
   return 1;
 }
 } // namespace
+
+#if !OPENSSL_1_1_1_API
+
+namespace {
+int legacy_sct_add_cb(SSL *ssl, unsigned int ext_type,
+                      const unsigned char **out, size_t *outlen, int *al,
+                      void *add_arg) {
+  return sct_add_cb(ssl, ext_type, 0, out, outlen, nullptr, 0, al, add_arg);
+}
+} // namespace
+
+namespace {
+void legacy_sct_free_cb(SSL *ssl, unsigned int ext_type,
+                        const unsigned char *out, void *add_arg) {
+  sct_free_cb(ssl, ext_type, 0, out, add_arg);
+}
+} // namespace
+
+namespace {
+int legacy_sct_parse_cb(SSL *ssl, unsigned int ext_type,
+                        const unsigned char *in, size_t inlen, int *al,
+                        void *parse_arg) {
+  return sct_parse_cb(ssl, ext_type, 0, in, inlen, nullptr, 0, al, parse_arg);
+}
+} // namespace
+
+#endif // !OPENSSL_1_1_1_API
 #endif // !LIBRESSL_IN_USE && OPENSSL_VERSION_NUMBER >= 0x10002000L
 
 #if !LIBRESSL_IN_USE
@@ -645,7 +750,7 @@ SSL_CTX *create_ssl_context(const char *private_key_file, const char *cert_file,
                             ,
                             neverbleed_t *nb
 #endif // HAVE_NEVERBLEED
-                            ) {
+) {
   auto ssl_ctx = SSL_CTX_new(SSLv23_server_method());
   if (!ssl_ctx) {
     LOG(FATAL) << ERR_error_string(ERR_get_error(), nullptr);
@@ -663,7 +768,7 @@ SSL_CTX *create_ssl_context(const char *private_key_file, const char *cert_file,
 
   SSL_CTX_set_options(ssl_ctx, ssl_opts | tlsconf.tls_proto_mask);
 
-  if (nghttp2::ssl::ssl_ctx_set_proto_versions(
+  if (nghttp2::tls::ssl_ctx_set_proto_versions(
           ssl_ctx, tlsconf.min_proto_version, tlsconf.max_proto_version) != 0) {
     LOG(FATAL) << "Could not set TLS protocol version";
     DIE();
@@ -731,8 +836,23 @@ SSL_CTX *create_ssl_context(const char *private_key_file, const char *cert_file,
     BIO_free(bio);
   }
 
-  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 (!tlsconf.cacert.empty()) {
+    if (SSL_CTX_load_verify_locations(ssl_ctx, tlsconf.cacert.c_str(),
+                                      nullptr) != 1) {
+      LOG(FATAL) << "Could not load trusted ca certificates from "
+                 << tlsconf.cacert << ": "
+                 << ERR_error_string(ERR_get_error(), nullptr);
+      DIE();
+    }
+  }
+
   if (!tlsconf.private_key_passwd.empty()) {
     SSL_CTX_set_default_passwd_cb(ssl_ctx, ssl_pem_passwd_cb);
     SSL_CTX_set_default_passwd_cb_userdata(ssl_ctx, config);
@@ -786,8 +906,9 @@ SSL_CTX *create_ssl_context(const char *private_key_file, const char *cert_file,
       }
       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,
+    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);
@@ -797,6 +918,10 @@ SSL_CTX *create_ssl_context(const char *private_key_file, const char *cert_file,
 #endif // OPENSSL_IS_BORINGSSL
   SSL_CTX_set_info_callback(ssl_ctx, info_callback);
 
+#ifdef OPENSSL_IS_BORINGSSL
+  SSL_CTX_set_early_data_enabled(ssl_ctx, 1);
+#endif // OPENSSL_IS_BORINGSSL
+
   // NPN advertisement
   SSL_CTX_set_next_protos_advertised_cb(ssl_ctx, next_proto_cb, nullptr);
 #if OPENSSL_VERSION_NUMBER >= 0x10002000L
@@ -805,17 +930,35 @@ SSL_CTX *create_ssl_context(const char *private_key_file, const char *cert_file,
 #endif // OPENSSL_VERSION_NUMBER >= 0x10002000L
 
 #if !LIBRESSL_IN_USE && OPENSSL_VERSION_NUMBER >= 0x10002000L
-  // SSL_extension_supported(TLS_EXT_SIGNED_CERTIFICATE_TIMESTAMP)
+  // SSL_extension_supported(TLSEXT_TYPE_signed_certificate_timestamp)
   // returns 1, which means OpenSSL internally handles it.  But
   // OpenSSL handles signed_certificate_timestamp extension specially,
   // and it lets custom handler to process the extension.
-  if (!sct_data.empty() &&
-      SSL_CTX_add_server_custom_ext(
-          ssl_ctx, TLS_EXT_SIGNED_CERTIFICATE_TIMESTAMP, sct_add_cb,
-          sct_free_cb, nullptr, sct_parse_cb, nullptr) != 1) {
-    LOG(FATAL) << "SSL_CTX_add_server_custom_ext failed: "
-               << ERR_error_string(ERR_get_error(), nullptr);
-    DIE();
+  if (!sct_data.empty()) {
+#if OPENSSL_1_1_1_API
+    // It is not entirely clear to me that SSL_EXT_CLIENT_HELLO is
+    // required here.  sct_parse_cb is called without
+    // SSL_EXT_CLIENT_HELLO being set.  But the passed context value
+    // is SSL_EXT_CLIENT_HELLO.
+    if (SSL_CTX_add_custom_ext(
+            ssl_ctx, TLSEXT_TYPE_signed_certificate_timestamp,
+            SSL_EXT_CLIENT_HELLO | SSL_EXT_TLS1_2_SERVER_HELLO |
+                SSL_EXT_TLS1_3_CERTIFICATE | SSL_EXT_IGNORE_ON_RESUMPTION,
+            sct_add_cb, sct_free_cb, nullptr, sct_parse_cb, nullptr) != 1) {
+      LOG(FATAL) << "SSL_CTX_add_custom_ext failed: "
+                 << ERR_error_string(ERR_get_error(), nullptr);
+      DIE();
+    }
+#else  // !OPENSSL_1_1_1_API
+    if (SSL_CTX_add_server_custom_ext(
+            ssl_ctx, TLSEXT_TYPE_signed_certificate_timestamp,
+            legacy_sct_add_cb, legacy_sct_free_cb, nullptr, legacy_sct_parse_cb,
+            nullptr) != 1) {
+      LOG(FATAL) << "SSL_CTX_add_server_custom_ext failed: "
+                 << ERR_error_string(ERR_get_error(), nullptr);
+      DIE();
+    }
+#endif // !OPENSSL_1_1_1_API
   }
 #endif // !LIBRESSL_IN_USE && OPENSSL_VERSION_NUMBER >= 0x10002000L
 
@@ -903,7 +1046,11 @@ SSL_CTX *create_ssl_client_context(
 
   SSL_CTX_set_options(ssl_ctx, ssl_opts | tlsconf.tls_proto_mask);
 
-  if (nghttp2::ssl::ssl_ctx_set_proto_versions(
+  SSL_CTX_set_session_cache_mode(ssl_ctx, SSL_SESS_CACHE_CLIENT |
+                                              SSL_SESS_CACHE_NO_INTERNAL_STORE);
+  SSL_CTX_sess_set_new_cb(ssl_ctx, tls_session_client_new_cb);
+
+  if (nghttp2::tls::ssl_ctx_set_proto_versions(
           ssl_ctx, tlsconf.min_proto_version, tlsconf.max_proto_version) != 0) {
     LOG(FATAL) << "Could not set TLS protocol version";
     DIE();
@@ -915,7 +1062,6 @@ SSL_CTX *create_ssl_client_context(
     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) {
@@ -932,6 +1078,10 @@ SSL_CTX *create_ssl_client_context(
     }
   }
 
+  if (!tlsconf.insecure) {
+    SSL_CTX_set_verify(ssl_ctx, SSL_VERIFY_PEER, nullptr);
+  }
+
   if (!cert_file.empty()) {
     if (SSL_CTX_use_certificate_chain_file(ssl_ctx, cert_file.c_str()) != 1) {
 
@@ -1254,12 +1404,6 @@ int check_cert(SSL *ssl, const Address *addr, const StringRef &host) {
     return 0;
   }
   auto cert_deleter = defer(X509_free, cert);
-  auto 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;
-  }
 
   if (verify_hostname(cert, host, addr) != 0) {
     LOG(ERROR) << "Certificate verification failed: hostname does not match";
@@ -1416,8 +1560,6 @@ int cert_lookup_tree_add_ssl_ctx(
 #endif // defined(LIBRESSL_VERSION_NUMBER) || OPENSSL_VERSION_NUMBER <
        // 0x10002000L
 
-  auto idx = indexed_ssl_ctx.size();
-
   auto altnames = static_cast<GENERAL_NAMES *>(
       X509_get_ext_d2i(cert, NID_subject_alt_name, nullptr, nullptr));
   if (altnames) {
@@ -1460,15 +1602,16 @@ int cert_lookup_tree_add_ssl_ctx(
       auto end_buf = std::copy_n(name, len, std::begin(buf));
       util::inp_strlower(std::begin(buf), end_buf);
 
-      auto nidx = lt->add_cert(StringRef{std::begin(buf), end_buf}, idx);
-      if (nidx == -1) {
+      auto idx = lt->add_cert(StringRef{std::begin(buf), end_buf},
+                              indexed_ssl_ctx.size());
+      if (idx == -1) {
         continue;
       }
-      idx = nidx;
-      if (idx < indexed_ssl_ctx.size()) {
+
+      if (static_cast<size_t>(idx) < indexed_ssl_ctx.size()) {
         indexed_ssl_ctx[idx].push_back(ssl_ctx);
       } else {
-        assert(idx == indexed_ssl_ctx.size());
+        assert(static_cast<size_t>(idx) == indexed_ssl_ctx.size());
         indexed_ssl_ctx.emplace_back(std::vector<SSL_CTX *>{ssl_ctx});
       }
     }
@@ -1500,15 +1643,16 @@ int cert_lookup_tree_add_ssl_ctx(
 
   util::inp_strlower(std::begin(buf), end_buf);
 
-  auto nidx = lt->add_cert(StringRef{std::begin(buf), end_buf}, idx);
-  if (nidx == -1) {
+  auto idx =
+      lt->add_cert(StringRef{std::begin(buf), end_buf}, indexed_ssl_ctx.size());
+  if (idx == -1) {
     return 0;
   }
-  idx = nidx;
-  if (idx < indexed_ssl_ctx.size()) {
+
+  if (static_cast<size_t>(idx) < indexed_ssl_ctx.size()) {
     indexed_ssl_ctx[idx].push_back(ssl_ctx);
   } else {
-    assert(idx == indexed_ssl_ctx.size());
+    assert(static_cast<size_t>(idx) == indexed_ssl_ctx.size());
     indexed_ssl_ctx.emplace_back(std::vector<SSL_CTX *>{ssl_ctx});
   }
 
@@ -1559,7 +1703,7 @@ setup_server_ssl_context(std::vector<SSL_CTX *> &all_ssl_ctx,
                          ,
                          neverbleed_t *nb
 #endif // HAVE_NEVERBLEED
-                         ) {
+) {
   auto config = get_config();
 
   if (!upstream_tls_enabled(config->conn)) {
@@ -1568,45 +1712,35 @@ setup_server_ssl_context(std::vector<SSL_CTX *> &all_ssl_ctx,
 
   auto &tlsconf = config->tls;
 
-  auto ssl_ctx =
-      ssl::create_ssl_context(tlsconf.private_key_file.c_str(),
-                              tlsconf.cert_file.c_str(), tlsconf.sct_data
+  auto ssl_ctx = create_ssl_context(tlsconf.private_key_file.c_str(),
+                                    tlsconf.cert_file.c_str(), tlsconf.sct_data
 #ifdef HAVE_NEVERBLEED
-                              ,
-                              nb
+                                    ,
+                                    nb
 #endif // HAVE_NEVERBLEED
-                              );
+  );
 
   all_ssl_ctx.push_back(ssl_ctx);
 
-  if (tlsconf.subcerts.empty()) {
-    return ssl_ctx;
-  }
+  assert(cert_tree);
 
-  if (!cert_tree) {
-    LOG(WARN) << "We have multiple additional certificates (--subcert), but "
-                 "cert_tree is not given.  SNI may not work.";
-    return ssl_ctx;
-  }
-
-  if (ssl::cert_lookup_tree_add_ssl_ctx(cert_tree, indexed_ssl_ctx, ssl_ctx) ==
-      -1) {
+  if (cert_lookup_tree_add_ssl_ctx(cert_tree, indexed_ssl_ctx, ssl_ctx) == -1) {
     LOG(FATAL) << "Failed to add default certificate.";
     DIE();
   }
 
   for (auto &c : tlsconf.subcerts) {
-    auto ssl_ctx = ssl::create_ssl_context(c.private_key_file.c_str(),
-                                           c.cert_file.c_str(), c.sct_data
+    auto ssl_ctx = create_ssl_context(c.private_key_file.c_str(),
+                                      c.cert_file.c_str(), c.sct_data
 #ifdef HAVE_NEVERBLEED
-                                           ,
-                                           nb
+                                      ,
+                                      nb
 #endif // HAVE_NEVERBLEED
-                                           );
+    );
     all_ssl_ctx.push_back(ssl_ctx);
 
-    if (ssl::cert_lookup_tree_add_ssl_ctx(cert_tree, indexed_ssl_ctx,
-                                          ssl_ctx) == -1) {
+    if (cert_lookup_tree_add_ssl_ctx(cert_tree, indexed_ssl_ctx, ssl_ctx) ==
+        -1) {
       LOG(FATAL) << "Failed to add sub certificate.";
       DIE();
     }
@@ -1619,10 +1753,10 @@ SSL_CTX *setup_downstream_client_ssl_context(
 #ifdef HAVE_NEVERBLEED
     neverbleed_t *nb
 #endif // HAVE_NEVERBLEED
-    ) {
+) {
   auto &tlsconf = get_config()->tls;
 
-  return ssl::create_ssl_client_context(
+  return create_ssl_client_context(
 #ifdef HAVE_NEVERBLEED
       nb,
 #endif // HAVE_NEVERBLEED
@@ -1647,7 +1781,7 @@ void setup_downstream_http1_alpn(SSL *ssl) {
 
 std::unique_ptr<CertLookupTree> create_cert_lookup_tree() {
   auto config = get_config();
-  if (!upstream_tls_enabled(config->conn) || config->tls.subcerts.empty()) {
+  if (!upstream_tls_enabled(config->conn)) {
     return nullptr;
   }
   return make_unique<CertLookupTree>();
@@ -1664,24 +1798,22 @@ std::vector<uint8_t> serialize_ssl_session(SSL_SESSION *session) {
 }
 } // namespace
 
-void try_cache_tls_session(TLSSessionCache &cache, const Address &addr,
-                           SSL_SESSION *session, ev_tstamp t) {
-  if (cache.last_updated + 1_min > t) {
+void try_cache_tls_session(TLSSessionCache *cache, SSL_SESSION *session,
+                           ev_tstamp t) {
+  if (cache->last_updated + 1_min > t) {
     if (LOG_ENABLED(INFO)) {
-      LOG(INFO) << "Cache for addr=" << util::to_numeric_addr(&addr)
-                << " is still host.  Not updating.";
+      LOG(INFO) << "Client session cache entry is still fresh.";
     }
     return;
   }
 
   if (LOG_ENABLED(INFO)) {
-    LOG(INFO) << "Update cache entry for SSL_SESSION=" << session
-              << ", addr=" << util::to_numeric_addr(&addr)
-              << ", timestamp=" << std::fixed << std::setprecision(6) << t;
+    LOG(INFO) << "Update client cache entry "
+              << "timestamp = " << std::fixed << std::setprecision(6) << t;
   }
 
-  cache.session_data = serialize_ssl_session(session);
-  cache.last_updated = t;
+  cache->session_data = serialize_ssl_session(session);
+  cache->last_updated = t;
 }
 
 SSL_SESSION *reuse_tls_session(const TLSSessionCache &cache) {
@@ -1711,6 +1843,230 @@ int proto_version_from_string(const StringRef &v) {
   return -1;
 }
 
-} // namespace ssl
+int verify_ocsp_response(SSL_CTX *ssl_ctx, const uint8_t *ocsp_resp,
+                         size_t ocsp_resplen) {
+
+#if !defined(OPENSSL_NO_OCSP) && !defined(LIBRESSL_VERSION_NUMBER) &&          \
+    OPENSSL_VERSION_NUMBER >= 0x10002000L
+  int rv;
+
+  STACK_OF(X509) * chain_certs;
+  SSL_CTX_get0_chain_certs(ssl_ctx, &chain_certs);
+
+  auto resp = d2i_OCSP_RESPONSE(nullptr, &ocsp_resp, ocsp_resplen);
+  if (resp == nullptr) {
+    LOG(ERROR) << "d2i_OCSP_RESPONSE failed";
+    return -1;
+  }
+  auto resp_deleter = defer(OCSP_RESPONSE_free, resp);
+
+  ERR_clear_error();
+
+  auto bs = OCSP_response_get1_basic(resp);
+  if (bs == nullptr) {
+    LOG(ERROR) << "OCSP_response_get1_basic failed: "
+               << ERR_error_string(ERR_get_error(), nullptr);
+    return -1;
+  }
+  auto bs_deleter = defer(OCSP_BASICRESP_free, bs);
+
+  auto store = SSL_CTX_get_cert_store(ssl_ctx);
+
+  ERR_clear_error();
+
+  rv = OCSP_basic_verify(bs, chain_certs, store, 0);
+
+  if (rv != 1) {
+    LOG(ERROR) << "OCSP_basic_verify failed: "
+               << ERR_error_string(ERR_get_error(), nullptr);
+    return -1;
+  }
+
+  auto sresp = OCSP_resp_get0(bs, 0);
+  if (sresp == nullptr) {
+    LOG(ERROR) << "OCSP response verification failed: no single response";
+    return -1;
+  }
+
+#if OPENSSL_1_1_API
+  auto certid = OCSP_SINGLERESP_get0_id(sresp);
+#else  // !OPENSSL_1_1_API
+  auto certid = sresp->certId;
+#endif // !OPENSSL_1_1_API
+  assert(certid != nullptr);
+
+  ASN1_INTEGER *serial;
+  rv = OCSP_id_get0_info(nullptr, nullptr, nullptr, &serial,
+                         const_cast<OCSP_CERTID *>(certid));
+  if (rv != 1) {
+    LOG(ERROR) << "OCSP_id_get0_info failed";
+    return -1;
+  }
+
+  if (serial == nullptr) {
+    LOG(ERROR) << "OCSP response does not contain serial number";
+    return -1;
+  }
+
+  auto cert = SSL_CTX_get0_certificate(ssl_ctx);
+  auto cert_serial = X509_get_serialNumber(cert);
+
+  if (ASN1_INTEGER_cmp(cert_serial, serial)) {
+    LOG(ERROR) << "OCSP verification serial numbers do not match";
+    return -1;
+  }
+
+  if (LOG_ENABLED(INFO)) {
+    LOG(INFO) << "OCSP verification succeeded";
+  }
+#endif // !defined(OPENSSL_NO_OCSP) && !defined(LIBRESSL_VERSION_NUMBER)
+       // && OPENSSL_VERSION_NUMBER >= 0x10002000L
+
+  return 0;
+}
+
+ssize_t get_x509_fingerprint(uint8_t *dst, size_t dstlen, const X509 *x,
+                             const EVP_MD *md) {
+  unsigned int len = dstlen;
+  if (X509_digest(x, md, dst, &len) != 1) {
+    return -1;
+  }
+  return len;
+}
+
+namespace {
+StringRef get_x509_name(BlockAllocator &balloc, X509_NAME *nm) {
+  auto b = BIO_new(BIO_s_mem());
+  if (!b) {
+    return StringRef{};
+  }
+
+  auto b_deleter = defer(BIO_free, b);
+
+  // Not documented, but it seems that X509_NAME_print_ex returns the
+  // number of bytes written into b.
+  auto slen = X509_NAME_print_ex(b, nm, 0, XN_FLAG_RFC2253);
+  if (slen <= 0) {
+    return StringRef{};
+  }
+
+  auto iov = make_byte_ref(balloc, slen + 1);
+  BIO_read(b, iov.base, slen);
+  iov.base[slen] = '\0';
+  return StringRef{iov.base, static_cast<size_t>(slen)};
+}
+} // namespace
+
+StringRef get_x509_subject_name(BlockAllocator &balloc, X509 *x) {
+  return get_x509_name(balloc, X509_get_subject_name(x));
+}
+
+StringRef get_x509_issuer_name(BlockAllocator &balloc, X509 *x) {
+  return get_x509_name(balloc, X509_get_issuer_name(x));
+}
+
+#ifdef WORDS_BIGENDIAN
+#define bswap64(N) (N)
+#else /* !WORDS_BIGENDIAN */
+#define bswap64(N)                                                             \
+  ((uint64_t)(ntohl((uint32_t)(N))) << 32 | ntohl((uint32_t)((N) >> 32)))
+#endif /* !WORDS_BIGENDIAN */
+
+StringRef get_x509_serial(BlockAllocator &balloc, X509 *x) {
+#if OPENSSL_1_1_API
+  auto sn = X509_get0_serialNumber(x);
+  uint64_t r;
+  if (ASN1_INTEGER_get_uint64(&r, sn) != 1) {
+    return StringRef{};
+  }
+
+  r = bswap64(r);
+  return util::format_hex(
+      balloc, StringRef{reinterpret_cast<uint8_t *>(&r), sizeof(r)});
+#else  // !OPENSSL_1_1_API
+  auto sn = X509_get_serialNumber(x);
+  auto bn = BN_new();
+  auto bn_d = defer(BN_free, bn);
+  if (!ASN1_INTEGER_to_BN(sn, bn)) {
+    return StringRef{};
+  }
+
+  std::array<uint8_t, 8> b;
+  auto n = BN_bn2bin(bn, b.data());
+  assert(n == b.size());
+
+  return util::format_hex(balloc, StringRef{std::begin(b), std::end(b)});
+#endif // !OPENSSL_1_1_API
+}
+
+namespace {
+// Performs conversion from |at| to time_t.  The result is stored in
+// |t|.  This function returns 0 if it succeeds, or -1.
+int time_t_from_asn1_time(time_t &t, const ASN1_TIME *at) {
+  int rv;
+
+#if OPENSSL_1_1_1_API
+  struct tm tm;
+  rv = ASN1_TIME_to_tm(at, &tm);
+  if (rv != 1) {
+    return -1;
+  }
+
+  t = nghttp2_timegm(&tm);
+#else  // !OPENSSL_1_1_1_API
+  auto b = BIO_new(BIO_s_mem());
+  if (!b) {
+    return -1;
+  }
+
+  auto bio_deleter = defer(BIO_free, b);
+
+  rv = ASN1_TIME_print(b, at);
+  if (rv != 1) {
+    return -1;
+  }
+
+  unsigned char *s;
+  auto slen = BIO_get_mem_data(b, &s);
+  auto tt = util::parse_openssl_asn1_time_print(
+      StringRef{s, static_cast<size_t>(slen)});
+  if (tt == 0) {
+    return -1;
+  }
+
+  t = tt;
+#endif // !OPENSSL_1_1_1_API
+
+  return 0;
+}
+} // namespace
+
+int get_x509_not_before(time_t &t, X509 *x) {
+#if OPENSSL_1_1_API
+  auto at = X509_get0_notBefore(x);
+#else  // !OPENSSL_1_1_API
+  auto at = X509_get_notBefore(x);
+#endif // !OPENSSL_1_1_API
+  if (!at) {
+    return -1;
+  }
+
+  return time_t_from_asn1_time(t, at);
+}
+
+int get_x509_not_after(time_t &t, X509 *x) {
+#if OPENSSL_1_1_API
+  auto at = X509_get0_notAfter(x);
+#else  // !OPENSSL_1_1_API
+  auto at = X509_get_notAfter(x);
+#endif // !OPENSSL_1_1_API
+  if (!at) {
+    return -1;
+  }
+
+  return time_t_from_asn1_time(t, at);
+}
+
+} // namespace tls
 
 } // namespace shrpx
similarity index 84%
rename from src/shrpx_ssl.h
rename to src/shrpx_tls.h
index dcd15e8..f3ee63c 100644 (file)
@@ -22,8 +22,8 @@
  * OF 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
+#ifndef SHRPX_TLS_H
+#define SHRPX_TLS_H
 
 #include "shrpx.h"
 
@@ -51,7 +51,7 @@ class DownstreamConnectionPool;
 struct DownstreamAddr;
 struct UpstreamAddr;
 
-namespace ssl {
+namespace tls {
 
 struct TLSSessionCache {
   // ASN1 representation of SSL_SESSION object.  See
@@ -86,7 +86,7 @@ SSL_CTX *create_ssl_context(const char *private_key_file, const char *cert_file,
                             ,
                             neverbleed_t *nb
 #endif // HAVE_NEVERBLEED
-                            );
+);
 
 // Create client side SSL_CTX.  This does not configure ALPN settings.
 // |next_proto_select_cb| is for NPN.
@@ -215,14 +215,14 @@ setup_server_ssl_context(std::vector<SSL_CTX *> &all_ssl_ctx,
                          ,
                          neverbleed_t *nb
 #endif // HAVE_NEVERBLEED
-                         );
+);
 
 // Setups client side SSL_CTX.
 SSL_CTX *setup_downstream_client_ssl_context(
 #ifdef HAVE_NEVERBLEED
     neverbleed_t *nb
 #endif // HAVE_NEVERBLEED
-    );
+);
 
 // Sets ALPN settings in |SSL| suitable for HTTP/2 use.
 void setup_downstream_http2_alpn(SSL *ssl);
@@ -244,12 +244,12 @@ bool upstream_tls_enabled(const ConnectionConfig &connconf);
 // is based on RFC 6125.
 bool tls_hostname_match(const StringRef &pattern, const StringRef &hostname);
 
-// Caches |session| which is associated to remote address |addr|.
-// |session| is serialized into ASN1 representation, and stored.  |t|
-// is used as a time stamp.  Depending on the existing cache's time
-// stamp, |session| might not be cached.
-void try_cache_tls_session(TLSSessionCache &cache, const Address &addr,
-                           SSL_SESSION *session, ev_tstamp t);
+// Caches |session|.  |session| is serialized into ASN1
+// representation, and stored.  |t| is used as a time stamp.
+// Depending on the existing cache's time stamp, |session| might not
+// be cached.
+void try_cache_tls_session(TLSSessionCache *cache, SSL_SESSION *session,
+                           ev_tstamp t);
 
 // Returns cached session associated |addr|.  If no cache entry is
 // found associated to |addr|, nullptr will be returned.
@@ -264,8 +264,40 @@ X509 *load_certificate(const char *filename);
 // TLS version string.
 int proto_version_from_string(const StringRef &v);
 
-} // namespace ssl
+// Verifies OCSP response |ocsp_resp| of length |ocsp_resplen|.  This
+// function returns 0 if it succeeds, or -1.
+int verify_ocsp_response(SSL_CTX *ssl_ctx, const uint8_t *ocsp_resp,
+                         size_t ocsp_resplen);
+
+// Stores fingerprint of |x| in |dst| of length |dstlen|.  |md|
+// specifies hash function to use, and |dstlen| must be large enough
+// to include hash value (e.g., 32 bytes for SHA-256).  This function
+// returns the number of bytes written in |dst|, or -1.
+ssize_t get_x509_fingerprint(uint8_t *dst, size_t dstlen, const X509 *x,
+                             const EVP_MD *md);
+
+// Returns subject name of |x|.  If this function fails to get subject
+// name, it returns an empty string.
+StringRef get_x509_subject_name(BlockAllocator &balloc, X509 *x);
+
+// Returns issuer name of |x|.  If this function fails to get issuer
+// name, it returns an empty string.
+StringRef get_x509_issuer_name(BlockAllocator &balloc, X509 *x);
+
+// Returns serial number of |x|.  If this function fails to get serial
+// number, it returns an empty string.  number
+StringRef get_x509_serial(BlockAllocator &balloc, X509 *x);
+
+// Fills NotBefore of |x| in |t|.  This function returns 0 if it
+// succeeds, or -1.
+int get_x509_not_before(time_t &t, X509 *x);
+
+// Fills NotAfter of |x| in |t|.  This function returns 0 if it
+// succeeds, or -1.
+int get_x509_not_after(time_t &t, X509 *x);
+
+} // namespace tls
 
 } // namespace shrpx
 
-#endif // SHRPX_SSL_H
+#endif // SHRPX_TLS_H
similarity index 86%
rename from src/shrpx_ssl_test.cc
rename to src/shrpx_tls_test.cc
index 551b13d..b227dda 100644 (file)
  * OF 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 "shrpx_tls_test.h"
 
 #include <CUnit/CUnit.h>
 
-#include "shrpx_ssl.h"
+#include "shrpx_tls.h"
 #include "shrpx_log.h"
 #include "util.h"
 #include "template.h"
@@ -35,8 +35,8 @@ using namespace nghttp2;
 
 namespace shrpx {
 
-void test_shrpx_ssl_create_lookup_tree(void) {
-  auto tree = make_unique<ssl::CertLookupTree>();
+void test_shrpx_tls_create_lookup_tree(void) {
+  auto tree = make_unique<tls::CertLookupTree>();
 
   constexpr StringRef hostnames[] = {
       StringRef::from_lit("example.com"),             // 0
@@ -70,7 +70,7 @@ void test_shrpx_ssl_create_lookup_tree(void) {
   CU_ASSERT(-1 == tree->lookup(StringRef{}));
   CU_ASSERT(5 == tree->lookup(hostnames[5]));
   CU_ASSERT(6 == tree->lookup(hostnames[6]));
-  constexpr char h6[] = "pdylay.sourceforge.net";
+  static constexpr char h6[] = "pdylay.sourceforge.net";
   for (int i = 0; i < 7; ++i) {
     CU_ASSERT(-1 == tree->lookup(StringRef{h6 + i, str_size(h6) - i}));
   }
@@ -85,7 +85,7 @@ void test_shrpx_ssl_create_lookup_tree(void) {
   };
   num = array_size(names);
 
-  tree = make_unique<ssl::CertLookupTree>();
+  tree = make_unique<tls::CertLookupTree>();
   for (size_t idx = 0; idx < num; ++idx) {
     tree->add_cert(names[idx], idx);
   }
@@ -116,56 +116,58 @@ void test_shrpx_ssl_create_lookup_tree(void) {
 //     -config=ca-config.json -profile=server test.example.com.csr |
 //     cfssljson -bare test.example.com
 //
-void test_shrpx_ssl_cert_lookup_tree_add_ssl_ctx(void) {
+void test_shrpx_tls_cert_lookup_tree_add_ssl_ctx(void) {
   int rv;
 
-  constexpr char nghttp2_certfile[] = NGHTTP2_SRC_DIR "/test.nghttp2.org.pem";
+  static constexpr char nghttp2_certfile[] =
+      NGHTTP2_SRC_DIR "/test.nghttp2.org.pem";
   auto nghttp2_ssl_ctx = SSL_CTX_new(SSLv23_server_method());
   auto nghttp2_ssl_ctx_del = defer(SSL_CTX_free, nghttp2_ssl_ctx);
-  auto nghttp2_tls_ctx_data = make_unique<ssl::TLSContextData>();
+  auto nghttp2_tls_ctx_data = make_unique<tls::TLSContextData>();
   nghttp2_tls_ctx_data->cert_file = nghttp2_certfile;
   SSL_CTX_set_app_data(nghttp2_ssl_ctx, nghttp2_tls_ctx_data.get());
   rv = SSL_CTX_use_certificate_chain_file(nghttp2_ssl_ctx, nghttp2_certfile);
 
   CU_ASSERT(1 == rv);
 
-  constexpr char examples_certfile[] = NGHTTP2_SRC_DIR "/test.example.com.pem";
+  static constexpr char examples_certfile[] =
+      NGHTTP2_SRC_DIR "/test.example.com.pem";
   auto examples_ssl_ctx = SSL_CTX_new(SSLv23_server_method());
   auto examples_ssl_ctx_del = defer(SSL_CTX_free, examples_ssl_ctx);
-  auto examples_tls_ctx_data = make_unique<ssl::TLSContextData>();
+  auto examples_tls_ctx_data = make_unique<tls::TLSContextData>();
   examples_tls_ctx_data->cert_file = examples_certfile;
   SSL_CTX_set_app_data(examples_ssl_ctx, examples_tls_ctx_data.get());
   rv = SSL_CTX_use_certificate_chain_file(examples_ssl_ctx, examples_certfile);
 
   CU_ASSERT(1 == rv);
 
-  ssl::CertLookupTree tree;
+  tls::CertLookupTree tree;
   std::vector<std::vector<SSL_CTX *>> indexed_ssl_ctx;
 
-  rv = ssl::cert_lookup_tree_add_ssl_ctx(&tree, indexed_ssl_ctx,
+  rv = tls::cert_lookup_tree_add_ssl_ctx(&tree, indexed_ssl_ctx,
                                          nghttp2_ssl_ctx);
 
   CU_ASSERT(0 == rv);
 
-  rv = ssl::cert_lookup_tree_add_ssl_ctx(&tree, indexed_ssl_ctx,
+  rv = tls::cert_lookup_tree_add_ssl_ctx(&tree, indexed_ssl_ctx,
                                          examples_ssl_ctx);
 
   CU_ASSERT(0 == rv);
 
   CU_ASSERT(-1 == tree.lookup(StringRef::from_lit("not-used.nghttp2.org")));
   CU_ASSERT(0 == tree.lookup(StringRef::from_lit("test.nghttp2.org")));
-  CU_ASSERT(0 == tree.lookup(StringRef::from_lit("w.test.nghttp2.org")));
-  CU_ASSERT(0 == tree.lookup(StringRef::from_lit("www.test.nghttp2.org")));
-  CU_ASSERT(1 == tree.lookup(StringRef::from_lit("test.example.com")));
+  CU_ASSERT(1 == tree.lookup(StringRef::from_lit("w.test.nghttp2.org")));
+  CU_ASSERT(2 == tree.lookup(StringRef::from_lit("www.test.nghttp2.org")));
+  CU_ASSERT(3 == tree.lookup(StringRef::from_lit("test.example.com")));
 }
 
 template <size_t N, size_t M>
 bool tls_hostname_match_wrapper(const char (&pattern)[N],
                                 const char (&hostname)[M]) {
-  return ssl::tls_hostname_match(StringRef{pattern, N}, StringRef{hostname, M});
+  return tls::tls_hostname_match(StringRef{pattern, N}, StringRef{hostname, M});
 }
 
-void test_shrpx_ssl_tls_hostname_match(void) {
+void test_shrpx_tls_tls_hostname_match(void) {
   CU_ASSERT(tls_hostname_match_wrapper("example.com", "example.com"));
   CU_ASSERT(tls_hostname_match_wrapper("example.com", "EXAMPLE.com"));
 
similarity index 84%
rename from src/shrpx_ssl_test.h
rename to src/shrpx_tls_test.h
index b9caaee..40360db 100644 (file)
@@ -22,8 +22,8 @@
  * OF 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
+#ifndef SHRPX_TLS_TEST_H
+#define SHRPX_TLS_TEST_H
 
 #ifdef HAVE_CONFIG_H
 #include <config.h>
 
 namespace shrpx {
 
-void test_shrpx_ssl_create_lookup_tree(void);
-void test_shrpx_ssl_cert_lookup_tree_add_ssl_ctx(void);
-void test_shrpx_ssl_tls_hostname_match(void);
+void test_shrpx_tls_create_lookup_tree(void);
+void test_shrpx_tls_cert_lookup_tree_add_ssl_ctx(void);
+void test_shrpx_tls_tls_hostname_match(void);
 
 } // namespace shrpx
 
-#endif // SHRPX_SSL_TEST_H
+#endif // SHRPX_TLS_TEST_H
index 22d24f2..5801943 100644 (file)
@@ -30,7 +30,7 @@
 
 #include <memory>
 
-#include "shrpx_ssl.h"
+#include "shrpx_tls.h"
 #include "shrpx_log.h"
 #include "shrpx_client_handler.h"
 #include "shrpx_http2_session.h"
@@ -68,58 +68,55 @@ void proc_wev_cb(struct ev_loop *loop, ev_timer *w, int revents) {
 }
 } // namespace
 
-namespace {
-bool match_shared_downstream_addr(
-    const std::shared_ptr<SharedDownstreamAddr> &lhs,
-    const std::shared_ptr<SharedDownstreamAddr> &rhs) {
-  if (lhs->addrs.size() != rhs->addrs.size()) {
-    return false;
-  }
+// DownstreamKey is used to index SharedDownstreamAddr in order to
+// find the same configuration.
+using DownstreamKey = std::tuple<
+    std::vector<std::tuple<StringRef, StringRef, size_t, size_t, shrpx_proto,
+                           uint16_t, bool, bool, bool, bool>>,
+    bool, int, StringRef, StringRef, int>;
 
-  if (lhs->affinity != rhs->affinity ||
-      lhs->redirect_if_not_tls != rhs->redirect_if_not_tls) {
-    return false;
+namespace {
+DownstreamKey create_downstream_key(
+    const std::shared_ptr<SharedDownstreamAddr> &shared_addr) {
+  DownstreamKey dkey;
+
+  auto &addrs = std::get<0>(dkey);
+  addrs.resize(shared_addr->addrs.size());
+  auto p = std::begin(addrs);
+  for (auto &a : shared_addr->addrs) {
+    std::get<0>(*p) = a.host;
+    std::get<1>(*p) = a.sni;
+    std::get<2>(*p) = a.fall;
+    std::get<3>(*p) = a.rise;
+    std::get<4>(*p) = a.proto;
+    std::get<5>(*p) = a.port;
+    std::get<6>(*p) = a.host_unix;
+    std::get<7>(*p) = a.tls;
+    std::get<8>(*p) = a.dns;
+    std::get<9>(*p) = a.upgrade_scheme;
+    ++p;
   }
+  std::sort(std::begin(addrs), std::end(addrs));
 
-  auto used = std::vector<bool>(lhs->addrs.size());
-
-  for (auto &a : lhs->addrs) {
-    size_t i;
-    for (i = 0; i < rhs->addrs.size(); ++i) {
-      if (used[i]) {
-        continue;
-      }
-
-      auto &b = rhs->addrs[i];
-      if (a.host == b.host && a.port == b.port && a.host_unix == b.host_unix &&
-          a.proto == b.proto && a.tls == b.tls && a.sni == b.sni &&
-          a.fall == b.fall && a.rise == b.rise && a.dns == b.dns) {
-        break;
-      }
-    }
-
-    if (i == rhs->addrs.size()) {
-      return false;
-    }
+  std::get<1>(dkey) = shared_addr->redirect_if_not_tls;
 
-    used[i] = true;
-  }
+  auto &affinity = shared_addr->affinity;
+  std::get<2>(dkey) = affinity.type;
+  std::get<3>(dkey) = affinity.cookie.name;
+  std::get<4>(dkey) = affinity.cookie.path;
+  std::get<5>(dkey) = affinity.cookie.secure;
 
-  return true;
+  return dkey;
 }
 } // namespace
 
-namespace {
-std::random_device rd;
-} // namespace
-
 Worker::Worker(struct ev_loop *loop, SSL_CTX *sv_ssl_ctx, SSL_CTX *cl_ssl_ctx,
                SSL_CTX *tls_session_cache_memcached_ssl_ctx,
-               ssl::CertLookupTree *cert_tree,
+               tls::CertLookupTree *cert_tree,
                const std::shared_ptr<TicketKeys> &ticket_keys,
                ConnectionHandler *conn_handler,
                std::shared_ptr<DownstreamConfig> downstreamconf)
-    : randgen_(rd()),
+    : randgen_(util::make_mt19937()),
       worker_stat_{},
       dns_tracker_(loop),
       loop_(loop),
@@ -160,7 +157,7 @@ void Worker::replace_downstream_config(
 
     auto &shared_addr = g->shared_addr;
 
-    if (shared_addr->affinity == AFFINITY_NONE) {
+    if (shared_addr->affinity.type == AFFINITY_NONE) {
       shared_addr->dconn_pool.remove_all();
       continue;
     }
@@ -179,6 +176,8 @@ void Worker::replace_downstream_config(
   downstream_addr_groups_ =
       std::vector<std::shared_ptr<DownstreamAddrGroup>>(groups.size());
 
+  std::map<DownstreamKey, size_t> addr_groups_indexer;
+
   for (size_t i = 0; i < groups.size(); ++i) {
     auto &src = groups[i];
     auto &dst = downstream_addr_groups_[i];
@@ -190,7 +189,16 @@ void Worker::replace_downstream_config(
     auto shared_addr = std::make_shared<SharedDownstreamAddr>();
 
     shared_addr->addrs.resize(src.addrs.size());
-    shared_addr->affinity = src.affinity;
+    shared_addr->affinity.type = src.affinity.type;
+    if (src.affinity.type == AFFINITY_COOKIE) {
+      shared_addr->affinity.cookie.name =
+          make_string_ref(shared_addr->balloc, src.affinity.cookie.name);
+      if (!src.affinity.cookie.path.empty()) {
+        shared_addr->affinity.cookie.path =
+            make_string_ref(shared_addr->balloc, src.affinity.cookie.path);
+      }
+      shared_addr->affinity.cookie.secure = src.affinity.cookie.secure;
+    }
     shared_addr->affinity_hash = src.affinity_hash;
     shared_addr->redirect_if_not_tls = src.redirect_if_not_tls;
 
@@ -213,6 +221,7 @@ void Worker::replace_downstream_config(
       dst_addr.fall = src_addr.fall;
       dst_addr.rise = src_addr.rise;
       dst_addr.dns = src_addr.dns;
+      dst_addr.upgrade_scheme = src_addr.upgrade_scheme;
 
       auto shared_addr_ptr = shared_addr.get();
 
@@ -256,14 +265,11 @@ void Worker::replace_downstream_config(
 
     // share the connection if patterns have the same set of backend
     // addresses.
-    auto end = std::begin(downstream_addr_groups_) + i;
-    auto it = std::find_if(
-        std::begin(downstream_addr_groups_), end,
-        [&shared_addr](const std::shared_ptr<DownstreamAddrGroup> &group) {
-          return match_shared_downstream_addr(group->shared_addr, shared_addr);
-        });
-
-    if (it == end) {
+
+    auto dkey = create_downstream_key(shared_addr);
+    auto it = addr_groups_indexer.find(dkey);
+
+    if (it == std::end(addr_groups_indexer)) {
       if (LOG_ENABLED(INFO)) {
         LOG(INFO) << "number of http/1.1 backend: " << num_http1
                   << ", number of h2 backend: " << num_http2;
@@ -272,19 +278,22 @@ void Worker::replace_downstream_config(
       shared_addr->http1_pri.weight = num_http1;
       shared_addr->http2_pri.weight = num_http2;
 
-      if (shared_addr->affinity != AFFINITY_NONE) {
+      if (shared_addr->affinity.type != AFFINITY_NONE) {
         for (auto &addr : shared_addr->addrs) {
           addr.dconn_pool = make_unique<DownstreamConnectionPool>();
         }
       }
 
       dst->shared_addr = shared_addr;
+
+      addr_groups_indexer.emplace(std::move(dkey), i);
     } else {
+      auto &g = *(std::begin(downstream_addr_groups_) + (*it).second);
       if (LOG_ENABLED(INFO)) {
         LOG(INFO) << dst->pattern << " shares the same backend group with "
-                  << (*it)->pattern;
+                  << g->pattern;
       }
-      dst->shared_addr = (*it)->shared_addr;
+      dst->shared_addr = g->shared_addr;
     }
   }
 }
@@ -372,7 +381,7 @@ void Worker::process_events() {
     }
 
     auto client_handler =
-        ssl::accept_connection(this, wev.client_fd, &wev.client_addr.sa,
+        tls::accept_connection(this, wev.client_fd, &wev.client_addr.sa,
                                wev.client_addrlen, wev.faddr);
     if (!client_handler) {
       if (LOG_ENABLED(INFO)) {
@@ -420,7 +429,7 @@ void Worker::process_events() {
   }
 }
 
-ssl::CertLookupTree *Worker::get_cert_lookup_tree() const { return cert_tree_; }
+tls::CertLookupTree *Worker::get_cert_lookup_tree() const { return cert_tree_; }
 
 std::shared_ptr<TicketKeys> Worker::get_ticket_keys() {
 #ifdef HAVE_ATOMIC_STD_SHARED_PTR
@@ -509,18 +518,6 @@ size_t match_downstream_addr_group_host(
   const auto &rev_wildcard_router = routerconf.rev_wildcard_router;
   const auto &wildcard_patterns = routerconf.wildcard_patterns;
 
-  if (path.empty() || path[0] != '/') {
-    auto group = router.match(host, StringRef::from_lit("/"));
-    if (group != -1) {
-      if (LOG_ENABLED(INFO)) {
-        LOG(INFO) << "Found pattern with query " << host
-                  << ", matched pattern=" << groups[group]->pattern;
-      }
-      return group;
-    }
-    return catch_all;
-  }
-
   if (LOG_ENABLED(INFO)) {
     LOG(INFO) << "Perform mapping selection, using host=" << host
               << ", path=" << path;
@@ -606,6 +603,10 @@ size_t match_downstream_addr_group(
   auto query = std::find(std::begin(raw_path), fragment, '?');
   auto path = StringRef{std::begin(raw_path), query};
 
+  if (path.empty() || path[0] != '/') {
+    path = StringRef::from_lit("/");
+  }
+
   if (hostport.empty()) {
     return match_downstream_addr_group_host(routerconf, hostport, path, groups,
                                             catch_all, balloc);
index 9b77eb5..a7a7773 100644 (file)
@@ -45,7 +45,7 @@
 #include "shrpx_config.h"
 #include "shrpx_downstream_connection_pool.h"
 #include "memchunk.h"
-#include "shrpx_ssl.h"
+#include "shrpx_tls.h"
 #include "shrpx_live_check.h"
 #include "shrpx_connect_blocker.h"
 #include "shrpx_dns_tracker.h"
@@ -69,9 +69,9 @@ class MRubyContext;
 } // namespace mruby
 #endif // HAVE_MRUBY
 
-namespace ssl {
+namespace tls {
 class CertLookupTree;
-} // namespace ssl
+} // namespace tls
 
 struct DownstreamAddr {
   Address addr;
@@ -95,7 +95,7 @@ struct DownstreamAddr {
   size_t fall;
   size_t rise;
   // Client side TLS session cache
-  ssl::TLSSessionCache tls_session_cache;
+  tls::TLSSessionCache tls_session_cache;
   // Http2Session object created for this address.  This list chains
   // all Http2Session objects that is not in group scope
   // http2_avail_freelist, and is not reached in maximum concurrency.
@@ -115,6 +115,10 @@ struct DownstreamAddr {
   bool tls;
   // true if dynamic DNS is enabled
   bool dns;
+  // true if :scheme pseudo header field should be upgraded to secure
+  // variant (e.g., "https") when forwarding request to a backend
+  // connected by TLS connection.
+  bool upgrade_scheme;
 };
 
 // Simplified weighted fair queuing.  Actually we don't use queue here
@@ -133,10 +137,10 @@ struct WeightedPri {
 struct SharedDownstreamAddr {
   SharedDownstreamAddr()
       : balloc(1024, 1024),
+        affinity{AFFINITY_NONE},
         next{0},
         http1_pri{},
         http2_pri{},
-        affinity{AFFINITY_NONE},
         redirect_if_not_tls{false} {}
 
   SharedDownstreamAddr(const SharedDownstreamAddr &) = delete;
@@ -161,6 +165,8 @@ struct SharedDownstreamAddr {
   // wise.
   DList<Http2Session> http2_avail_freelist;
   DownstreamConnectionPool dconn_pool;
+  // Configuration for session affinity
+  AffinityConfig affinity;
   // Next http/1.1 downstream address index in addrs.
   size_t next;
   // http1_pri and http2_pri are used to which protocols are used
@@ -171,7 +177,6 @@ struct SharedDownstreamAddr {
   WeightedPri http1_pri;
   WeightedPri http2_pri;
   // Session affinity
-  shrpx_session_affinity affinity;
   // true if this group requires that client connection must be TLS,
   // and the request must be redirected to https URI.
   bool redirect_if_not_tls;
@@ -220,7 +225,7 @@ class Worker {
 public:
   Worker(struct ev_loop *loop, SSL_CTX *sv_ssl_ctx, SSL_CTX *cl_ssl_ctx,
          SSL_CTX *tls_session_cache_memcached_ssl_ctx,
-         ssl::CertLookupTree *cert_tree,
+         tls::CertLookupTree *cert_tree,
          const std::shared_ptr<TicketKeys> &ticket_keys,
          ConnectionHandler *conn_handler,
          std::shared_ptr<DownstreamConfig> downstreamconf);
@@ -230,7 +235,7 @@ public:
   void process_events();
   void send(const WorkerEvent &event);
 
-  ssl::CertLookupTree *get_cert_lookup_tree() const;
+  tls::CertLookupTree *get_cert_lookup_tree() const;
 
   // These 2 functions make a lock m_ to get/set ticket keys
   // atomically.
@@ -297,7 +302,7 @@ private:
   // get_config()->tls_ctx_per_worker == true.
   SSL_CTX *sv_ssl_ctx_;
   SSL_CTX *cl_ssl_ctx_;
-  ssl::CertLookupTree *cert_tree_;
+  tls::CertLookupTree *cert_tree_;
   ConnectionHandler *conn_handler_;
 
 #ifndef HAVE_ATOMIC_STD_SHARED_PTR
index 51e493a..647c167 100644 (file)
@@ -51,7 +51,7 @@
 #include "shrpx_memcached_dispatcher.h"
 #include "shrpx_memcached_request.h"
 #include "shrpx_process.h"
-#include "shrpx_ssl.h"
+#include "shrpx_tls.h"
 #include "shrpx_log.h"
 #include "util.h"
 #include "app_helper.h"
@@ -67,7 +67,7 @@ void drop_privileges(
 #ifdef HAVE_NEVERBLEED
     neverbleed_t *nb
 #endif // HAVE_NEVERBLEED
-    ) {
+) {
   std::array<char, STRERROR_BUFSIZE> errbuf;
   auto config = get_config();
 
@@ -114,12 +114,10 @@ void graceful_shutdown(ConnectionHandler *conn_handler) {
 
   conn_handler->set_graceful_shutdown(true);
 
-  conn_handler->disable_acceptor();
-
-  // After disabling accepting new connection, disptach incoming
-  // connection in backlog.
-
+  // TODO What happens for the connections not established in the
+  // kernel?
   conn_handler->accept_pending_connection();
+  conn_handler->delete_acceptor();
 
   conn_handler->graceful_shutdown_worker();
 
@@ -275,8 +273,7 @@ void memcached_get_ticket_key_cb(struct ev_loop *loop, ev_timer *w,
   auto req = make_unique<MemcachedRequest>();
   req->key = "nghttpx:tls-ticket-key";
   req->op = MEMCACHED_OP_GET;
-  req->cb = [conn_handler, dispatcher, w](MemcachedRequest *req,
-                                          MemcachedResult res) {
+  req->cb = [conn_handler, w](MemcachedRequest *req, MemcachedResult res) {
     switch (res.status_code) {
     case MEMCACHED_ERR_NO_ERROR:
       break;
@@ -392,10 +389,6 @@ void nb_child_cb(struct ev_loop *loop, ev_child *w, int revents) {
 } // namespace
 #endif // HAVE_NEVERBLEED
 
-namespace {
-std::random_device rd;
-} // namespace
-
 int worker_process_event_loop(WorkerProcessConfig *wpconf) {
   int rv;
   std::array<char, STRERROR_BUFSIZE> errbuf;
@@ -416,7 +409,7 @@ int worker_process_event_loop(WorkerProcessConfig *wpconf) {
 
   auto loop = EV_DEFAULT;
 
-  auto gen = std::mt19937(rd());
+  auto gen = util::make_mt19937();
 
   ConnectionHandler conn_handler(loop, gen);
 
@@ -452,7 +445,7 @@ int worker_process_event_loop(WorkerProcessConfig *wpconf) {
   MemchunkPool mcpool;
 
   ev_timer renew_ticket_key_timer;
-  if (ssl::upstream_tls_enabled(config->conn)) {
+  if (tls::upstream_tls_enabled(config->conn)) {
     auto &ticketconf = config->tls.ticket;
     auto &memcachedconf = ticketconf.memcached;
 
@@ -544,14 +537,19 @@ int worker_process_event_loop(WorkerProcessConfig *wpconf) {
 #ifdef HAVE_NEVERBLEED
       nb
 #endif // HAVE_NEVERBLEED
-      );
+  );
 
   ev_io ipcev;
   ev_io_init(&ipcev, ipc_readcb, wpconf->ipc_fd, EV_READ);
   ipcev.data = &conn_handler;
   ev_io_start(loop, &ipcev);
 
-  if (ssl::upstream_tls_enabled(config->conn) && !config->tls.ocsp.disabled) {
+  if (tls::upstream_tls_enabled(config->conn) && !config->tls.ocsp.disabled) {
+    if (config->tls.ocsp.startup) {
+      conn_handler.set_enable_acceptor_on_ocsp_completion(true);
+      conn_handler.disable_acceptor();
+    }
+
     conn_handler.proceed_next_cert_ocsp();
   }
 
index ee6919f..d907b32 100644 (file)
@@ -197,6 +197,10 @@ void test_shrpx_worker_match_downstream_addr_group(void) {
   g2->pattern = ImmutableString::from_lit(".nghttp2.org");
   groups.push_back(std::move(g2));
 
+  auto g3 = std::make_shared<DownstreamAddrGroup>();
+  g3->pattern = ImmutableString::from_lit(".local");
+  groups.push_back(std::move(g3));
+
   wp.emplace_back(StringRef::from_lit("git.nghttp2.org"));
   wcrouter.add_route(StringRef::from_lit("gro.2ptthgn.tig"), 0);
   wp.back().router.add_route(StringRef::from_lit("/echo/"), 10);
@@ -206,6 +210,10 @@ void test_shrpx_worker_match_downstream_addr_group(void) {
   wp.back().router.add_route(StringRef::from_lit("/echo/"), 11);
   wp.back().router.add_route(StringRef::from_lit("/echo/foxtrot"), 12);
 
+  wp.emplace_back(StringRef::from_lit(".local"));
+  wcrouter.add_route(StringRef::from_lit("lacol."), 2);
+  wp.back().router.add_route(StringRef::from_lit("/"), 13);
+
   CU_ASSERT(11 == match_downstream_addr_group(
                       routerconf, StringRef::from_lit("git.nghttp2.org"),
                       StringRef::from_lit("/echo"), groups, 255, balloc));
@@ -230,6 +238,10 @@ void test_shrpx_worker_match_downstream_addr_group(void) {
   CU_ASSERT(0 == match_downstream_addr_group(
                      routerconf, StringRef::from_lit("nghttp2.org"),
                      StringRef::from_lit("/echo"), groups, 255, balloc));
+
+  CU_ASSERT(13 == match_downstream_addr_group(
+                      routerconf, StringRef::from_lit("test.local"),
+                      StringRef{}, groups, 255, balloc));
 }
 
 } // namespace shrpx
index 4404594..22077b1 100644 (file)
@@ -35,4 +35,7 @@
 #define OPENSSL_1_1_API                                                        \
   (!LIBRESSL_IN_USE && OPENSSL_VERSION_NUMBER >= 0x1010000fL)
 
+#define OPENSSL_1_1_1_API                                                      \
+  (!LIBRESSL_IN_USE && OPENSSL_VERSION_NUMBER >= 0x10101000L)
+
 #endif // OPENSSL_COMPAT_H
similarity index 88%
rename from src/ssl.cc
rename to src/tls.cc
index 5425cb8..6920fe4 100644 (file)
@@ -22,7 +22,7 @@
  * OF 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 "tls.h"
 
 #include <cassert>
 #include <vector>
 
 namespace nghttp2 {
 
-namespace ssl {
+namespace tls {
 
 #if OPENSSL_1_1_API
 
 // CRYPTO_LOCK is deprecated as of OpenSSL 1.1.0
 LibsslGlobalLock::LibsslGlobalLock() {}
-LibsslGlobalLock::~LibsslGlobalLock() {}
 
 #else // !OPENSSL_1_1_API
 
 namespace {
-std::vector<std::mutex> ssl_global_locks;
+std::mutex *ssl_global_locks;
 } // namespace
 
 namespace {
@@ -61,11 +60,11 @@ void ssl_locking_cb(int mode, int type, const char *file, int line) {
 } // namespace
 
 LibsslGlobalLock::LibsslGlobalLock() {
-  if (!ssl_global_locks.empty()) {
+  if (ssl_global_locks) {
     std::cerr << "OpenSSL global lock has been already set" << std::endl;
     assert(0);
   }
-  ssl_global_locks = std::vector<std::mutex>(CRYPTO_num_locks());
+  ssl_global_locks = new std::mutex[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
@@ -73,8 +72,6 @@ LibsslGlobalLock::LibsslGlobalLock() {
   CRYPTO_set_locking_callback(ssl_locking_cb);
 }
 
-LibsslGlobalLock::~LibsslGlobalLock() { ssl_global_locks.clear(); }
-
 #endif // !OPENSSL_1_1_API
 
 const char *get_tls_protocol(SSL *ssl) {
@@ -152,29 +149,26 @@ bool check_http2_requirement(SSL *ssl) {
 }
 
 void libssl_init() {
-// OPENSSL_config() is not available in BoringSSL.  It is also
-// deprecated as of OpenSSL 1.1.0.
-#if !defined(OPENSSL_IS_BORINGSSL) && !OPENSSL_1_1_API
+#if OPENSSL_1_1_API
+// No explicit initialization is required.
+#elif defined(OPENSSL_IS_BORINGSSL)
+  CRYPTO_library_init();
+#else  // !OPENSSL_1_1_API && !defined(OPENSSL_IS_BORINGSSL)
   OPENSSL_config(nullptr);
-#endif // !defined(OPENSSL_IS_BORINGSSL) && !OPENSSL_1_1_API
-
   SSL_load_error_strings();
   SSL_library_init();
   OpenSSL_add_all_algorithms();
+#endif // !OPENSSL_1_1_API && !defined(OPENSSL_IS_BORINGSSL)
 }
 
 int ssl_ctx_set_proto_versions(SSL_CTX *ssl_ctx, int min, int max) {
-#if OPENSSL_1_1_API
+#if OPENSSL_1_1_API || defined(OPENSSL_IS_BORINGSSL)
   if (SSL_CTX_set_min_proto_version(ssl_ctx, min) != 1 ||
       SSL_CTX_set_max_proto_version(ssl_ctx, max) != 1) {
     return -1;
   }
   return 0;
-#elif defined(OPENSSL_IS_BORINGSSL)
-  SSL_CTX_set_min_version(ssl_ctx, min);
-  SSL_CTX_set_max_version(ssl_ctx, max);
-  return 0;
-#else  // !defined(OPENSSL_IS_BORINGSSL)
+#else  // !OPENSSL_1_1_API && !defined(OPENSSL_IS_BORINGSSL)
   long int opts = 0;
 
   // TODO We depends on the ordering of protocol version macro in
@@ -199,9 +193,9 @@ int ssl_ctx_set_proto_versions(SSL_CTX *ssl_ctx, int min, int max) {
   SSL_CTX_set_options(ssl_ctx, opts);
 
   return 0;
-#endif // !defined(OPENSSL_IS_BORINGSSL)
+#endif // !OPENSSL_1_1_API && !defined(OPENSSL_IS_BORINGSSL)
 }
 
-} // namespace ssl
+} // namespace tls
 
 } // namespace nghttp2
similarity index 79%
rename from src/ssl.h
rename to src/tls.h
index 23c71fa..aca3802 100644 (file)
--- a/src/ssl.h
+++ b/src/tls.h
@@ -22,8 +22,8 @@
  * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
  * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  */
-#ifndef SSL_H
-#define SSL_H
+#ifndef TLS_H
+#define TLS_H
 
 #include "nghttp2_config.h"
 
 
 namespace nghttp2 {
 
-namespace ssl {
+namespace tls {
 
 // 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;
 };
 
-// Recommended general purpose "Intermediate compatibility" cipher
-// suites by mozilla.
+// Recommended general purpose "Modern compatibility" cipher suites by
+// mozilla.
 //
 // https://wiki.mozilla.org/Security/Server_Side_TLS
 //
@@ -68,21 +67,15 @@ constexpr char DEFAULT_CIPHER_LIST[] =
 #ifdef TLS1_3_TXT_AES_128_CCM_8_SHA256
     TLS1_3_TXT_AES_128_CCM_8_SHA256 ":"
 #endif // TLS1_3_TXT_AES_128_CCM_8_SHA256
-    "ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-"
-    "AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-"
-    "SHA384:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-"
-    "AES256-GCM-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-"
-    "ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-"
-    "AES256-SHA384:ECDHE-ECDSA-AES256-SHA:ECDHE-RSA-AES256-SHA:DHE-RSA-AES128-"
-    "SHA256:DHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA256:DHE-RSA-AES256-SHA:ECDHE-"
-    "ECDSA-DES-CBC3-SHA:ECDHE-RSA-DES-CBC3-SHA:EDH-RSA-DES-CBC3-SHA:AES128-GCM-"
-    "SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-"
-    "SHA:DES-CBC3-SHA:!DSS";
+    "ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-"
+    "CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-"
+    "SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-"
+    "AES256-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256";
 
 constexpr auto NGHTTP2_TLS_MIN_VERSION = TLS1_VERSION;
 #ifdef TLS1_3_VERSION
 constexpr auto NGHTTP2_TLS_MAX_VERSION = TLS1_3_VERSION;
-#else // !TLS1_3_VERSION
+#else  // !TLS1_3_VERSION
 constexpr auto NGHTTP2_TLS_MAX_VERSION = TLS1_2_VERSION;
 #endif // !TLS1_3_VERSION
 
@@ -120,8 +113,8 @@ void libssl_init();
 // 0 if it succeeds, or -1.
 int ssl_ctx_set_proto_versions(SSL_CTX *ssl_ctx, int min, int max);
 
-} // namespace ssl
+} // namespace tls
 
 } // namespace nghttp2
 
-#endif // SSL_H
+#endif // TLS_H
index d7bc807..485163f 100644 (file)
 #ifdef HAVE_NETINET_IN_H
 #include <netinet/in.h>
 #endif // HAVE_NETINET_IN_H
+#ifdef _WIN32
+#include <ws2tcpip.h>
+#include <boost/date_time/posix_time/posix_time.hpp>
+#else // !_WIN32
 #include <netinet/tcp.h>
+#endif // !_WIN32
 #ifdef HAVE_ARPA_INET_H
 #include <arpa/inet.h>
 #endif // HAVE_ARPA_INET_H
@@ -65,6 +70,34 @@ namespace nghttp2 {
 
 namespace util {
 
+#ifndef _WIN32
+namespace {
+int nghttp2_inet_pton(int af, const char *src, void *dst) {
+  return inet_pton(af, src, dst);
+}
+} // namespace
+#else // _WIN32
+namespace {
+// inet_pton-wrapper for Windows
+int nghttp2_inet_pton(int af, const char *src, void *dst) {
+#if _WIN32_WINNT >= 0x0600
+  return InetPtonA(af, src, dst);
+#else
+  // the function takes a 'char*', so we need to make a copy
+  char addr[INET6_ADDRSTRLEN + 1];
+  strncpy(addr, src, sizeof(addr));
+  addr[sizeof(addr) - 1] = 0;
+
+  int size = sizeof(struct in6_addr);
+
+  if (WSAStringToAddress(addr, af, NULL, (LPSOCKADDR)dst, &size) == 0)
+    return 1;
+  return 0;
+#endif
+}
+} // namespace
+#endif // _WIN32
+
 const char UPPER_XDIGITS[] = "0123456789ABCDEF";
 
 bool in_rfc3986_unreserved_chars(const char c) {
@@ -164,7 +197,7 @@ uint32_t hex_to_uint(char c) {
   if (c <= 'z') {
     return c - 'a' + 10;
   }
-  return c;
+  return 256;
 }
 
 StringRef quote_string(BlockAllocator &balloc, const StringRef &target) {
@@ -352,13 +385,43 @@ char *iso8601_date(char *res, int64_t ms) {
   return p;
 }
 
+#ifdef _WIN32
+namespace bt = boost::posix_time;
+// one-time definition of the locale that is used to parse UTC strings
+// (note that the time_input_facet is ref-counted and deleted automatically)
+static const std::locale
+    ptime_locale(std::locale::classic(),
+                 new bt::time_input_facet("%a, %d %b %Y %H:%M:%S GMT"));
+#endif //_WIN32
+
 time_t parse_http_date(const StringRef &s) {
+#ifdef _WIN32
+  // there is no strptime - use boost
+  std::stringstream sstr(s.str());
+  sstr.imbue(ptime_locale);
+  bt::ptime ltime;
+  sstr >> ltime;
+  if (!sstr)
+    return 0;
+
+  return boost::posix_time::to_time_t(ltime);
+#else  // !_WIN32
   tm tm{};
   char *r = strptime(s.c_str(), "%a, %d %b %Y %H:%M:%S GMT", &tm);
   if (r == 0) {
     return 0;
   }
   return nghttp2_timegm_without_yday(&tm);
+#endif // !_WIN32
+}
+
+time_t parse_openssl_asn1_time_print(const StringRef &s) {
+  tm tm{};
+  auto r = strptime(s.c_str(), "%b %d %H:%M:%S %Y GMT", &tm);
+  if (r == nullptr) {
+    return 0;
+  }
+  return nghttp2_timegm_without_yday(&tm);
 }
 
 char upcase(char c) {
@@ -369,10 +432,6 @@ char upcase(char c) {
   }
 }
 
-namespace {
-constexpr char LOWER_XDIGITS[] = "0123456789abcdef";
-} // namespace
-
 std::string format_hex(const unsigned char *s, size_t len) {
   std::string res;
   res.resize(len * 2);
@@ -611,7 +670,7 @@ bool numeric_host(const char *hostname, int family) {
   int rv;
   std::array<uint8_t, sizeof(struct in6_addr)> dst;
 
-  rv = inet_pton(family, hostname, dst.data());
+  rv = nghttp2_inet_pton(family, hostname, dst.data());
 
   return rv == 1;
 }
@@ -628,9 +687,11 @@ std::string numeric_name(const struct sockaddr *sa, socklen_t salen) {
 
 std::string to_numeric_addr(const Address *addr) {
   auto family = addr->su.storage.ss_family;
+#ifndef _WIN32
   if (family == AF_UNIX) {
     return addr->su.un.sun_path;
   }
+#endif // !_WIN32
 
   std::array<char, NI_MAXHOST> host;
   std::array<char, NI_MAXSERV> serv;
@@ -818,6 +879,10 @@ std::vector<std::string> parse_config_str_list(const StringRef &s, char delim) {
 }
 
 int make_socket_closeonexec(int fd) {
+#ifdef _WIN32
+  (void)fd;
+  return 0;
+#else  // !_WIN32
   int flags;
   int rv;
   while ((flags = fcntl(fd, F_GETFD)) == -1 && errno == EINTR)
@@ -825,15 +890,24 @@ int make_socket_closeonexec(int fd) {
   while ((rv = fcntl(fd, F_SETFD, flags | FD_CLOEXEC)) == -1 && errno == EINTR)
     ;
   return rv;
+#endif // !_WIN32
 }
 
 int make_socket_nonblocking(int fd) {
-  int flags;
   int rv;
+
+#ifdef _WIN32
+  u_long mode = 1;
+
+  rv = ioctlsocket(fd, FIONBIO, &mode);
+#else  // !_WIN32
+  int flags;
   while ((flags = fcntl(fd, F_GETFL, 0)) == -1 && errno == EINTR)
     ;
   while ((rv = fcntl(fd, F_SETFL, flags | O_NONBLOCK)) == -1 && errno == EINTR)
     ;
+#endif // !_WIN32
+
   return rv;
 }
 
@@ -874,7 +948,7 @@ int create_nonblock_socket(int family) {
 bool check_socket_connected(int fd) {
   int error;
   socklen_t len = sizeof(error);
-  if (getsockopt(fd, SOL_SOCKET, SO_ERROR, &error, &len) != 0) {
+  if (getsockopt(fd, SOL_SOCKET, SO_ERROR, (char *)&error, &len) != 0) {
     return false;
   }
 
@@ -884,7 +958,7 @@ bool check_socket_connected(int fd) {
 int get_socket_error(int fd) {
   int error;
   socklen_t len = sizeof(error);
-  if (getsockopt(fd, SOL_SOCKET, SO_ERROR, &error, &len) != 0) {
+  if (getsockopt(fd, SOL_SOCKET, SO_ERROR, (char *)&error, &len) != 0) {
     return -1;
   }
 
@@ -893,7 +967,7 @@ int get_socket_error(int fd) {
 
 bool ipv6_numeric_addr(const char *host) {
   uint8_t dst[16];
-  return inet_pton(AF_INET6, host, dst) == 1;
+  return nghttp2_inet_pton(AF_INET6, host, dst) == 1;
 }
 
 namespace {
@@ -1103,8 +1177,9 @@ std::string format_duration(double t) {
 }
 
 std::string dtos(double n) {
-  auto f = utos(static_cast<int64_t>(round(100. * n)) % 100);
-  return utos(static_cast<int64_t>(n)) + "." + (f.size() == 1 ? "0" : "") + f;
+  auto m = llround(100. * n);
+  auto f = utos(m % 100);
+  return utos(m / 100) + "." + (f.size() == 1 ? "0" : "") + f;
 }
 
 StringRef make_http_hostport(BlockAllocator &balloc, const StringRef &host,
@@ -1452,6 +1527,11 @@ StringRef extract_host(const StringRef &hostport) {
   return StringRef{std::begin(hostport), p};
 }
 
+std::mt19937 make_mt19937() {
+  std::random_device rd;
+  return std::mt19937(rd());
+}
+
 } // namespace util
 
 } // namespace nghttp2
index a3c7c38..cc85b2c 100644 (file)
@@ -96,8 +96,8 @@ bool in_token(char c);
 
 bool in_attr_char(char c);
 
-// Returns integer corresponding to hex notation |c|.  It is undefined
-// if is_hex_digit(c) is false.
+// Returns integer corresponding to hex notation |c|.  If
+// is_hex_digit(c) is false, it returns 256.
 uint32_t hex_to_uint(char c);
 
 std::string percent_encode(const unsigned char *target, size_t len);
@@ -152,6 +152,19 @@ template <size_t N> std::string format_hex(const std::array<uint8_t, N> &s) {
 
 StringRef format_hex(BlockAllocator &balloc, const StringRef &s);
 
+static constexpr char LOWER_XDIGITS[] = "0123456789abcdef";
+
+template <typename OutputIt>
+OutputIt format_hex(OutputIt it, const StringRef &s) {
+  for (auto cc : s) {
+    uint8_t c = cc;
+    *it++ = LOWER_XDIGITS[c >> 4];
+    *it++ = LOWER_XDIGITS[c & 0xf];
+  }
+
+  return it;
+}
+
 // decode_hex decodes hex string |s|, returns the decoded byte string.
 // This function assumes |s| is hex string, that is is_hex_string(s)
 // == true.
@@ -183,6 +196,11 @@ char *iso8601_date(char *res, int64_t ms);
 
 time_t parse_http_date(const StringRef &s);
 
+// Parses time formatted as "MMM DD HH:MM:SS YYYY [GMT]" (e.g., Feb 3
+// 00:55:52 2015 GMT), which is specifically used by OpenSSL
+// ASN1_TIME_print().
+time_t parse_openssl_asn1_time_print(const StringRef &s);
+
 char upcase(char c);
 
 inline char lowcase(char c) {
@@ -716,7 +734,7 @@ template <typename OutputIt, typename Generator>
 OutputIt random_alpha_digit(OutputIt first, OutputIt last, Generator &gen) {
   // If we use uint8_t instead char, gcc 6.2.0 complains by shouting
   // char-array initialized from wide string.
-  constexpr char s[] =
+  static constexpr char s[] =
       "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
   std::uniform_int_distribution<> dis(0, 26 * 2 + 10 - 1);
   for (; first != last; ++first) {
@@ -744,6 +762,9 @@ int sha256(uint8_t *buf, const StringRef &s);
 // NULL-terminated.
 StringRef extract_host(const StringRef &hostport);
 
+// Returns new std::mt19937 object.
+std::mt19937 make_mt19937();
+
 } // namespace util
 
 } // namespace nghttp2
index d21fe7f..d375b43 100644 (file)
@@ -473,6 +473,7 @@ void test_util_localtime_date(void) {
 
   if (tz) {
     setenv("TZ", tz, 1);
+    free(tz);
   } else {
     unsetenv("TZ");
   }
index 8fbbb0a..9c6e655 100644 (file)
@@ -1,7 +1,7 @@
-# Makefile.in generated by automake 1.15 from Makefile.am.
+# Makefile.in generated by automake 1.15.1 from Makefile.am.
 # @configure_input@
 
-# Copyright (C) 1994-2014 Free Software Foundation, Inc.
+# Copyright (C) 1994-2017 Free Software Foundation, Inc.
 
 # This Makefile.in is free software; the Free Software Foundation
 # gives unlimited permission to copy and/or distribute it,
@@ -552,8 +552,6 @@ LIBMRUBY_CFLAGS = @LIBMRUBY_CFLAGS@
 LIBMRUBY_LIBS = @LIBMRUBY_LIBS@
 LIBOBJS = @LIBOBJS@
 LIBS = @LIBS@
-LIBSPDYLAY_CFLAGS = @LIBSPDYLAY_CFLAGS@
-LIBSPDYLAY_LIBS = @LIBSPDYLAY_LIBS@
 LIBTOOL = @LIBTOOL@
 LIBXML2_CFLAGS = @LIBXML2_CFLAGS@
 LIBXML2_LIBS = @LIBXML2_LIBS@
index f273403..4d7376e 100644 (file)
@@ -37,7 +37,7 @@ static int init_suite1(void) { return 0; }
 
 static int clean_suite1(void) { return 0; }
 
-int main(int argc _U_, char *argv[] _U_) {
+int main() {
   CU_pSuite pSuite = NULL;
   unsigned int num_tests_failed;
 
index 42ccb9d..a7ac2f0 100644 (file)
@@ -59,29 +59,39 @@ static void data_feed_init(data_feed *df, nghttp2_bufs *bufs) {
   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_) {
+static ssize_t null_send_callback(nghttp2_session *session, const uint8_t *data,
+                                  size_t len, int flags, void *user_data) {
+  (void)session;
+  (void)data;
+  (void)flags;
+  (void)user_data;
+
   return (ssize_t)len;
 }
 
-static ssize_t data_feed_recv_callback(nghttp2_session *session _U_,
-                                       uint8_t *data, size_t len, int flags _U_,
-                                       void *user_data) {
+static ssize_t data_feed_recv_callback(nghttp2_session *session, uint8_t *data,
+                                       size_t len, int flags, void *user_data) {
   data_feed *df = ((my_user_data *)user_data)->df;
   size_t avail = (size_t)(df->datalimit - df->datamark);
   size_t wlen = nghttp2_min(avail, len);
+  (void)session;
+  (void)flags;
+
   memcpy(data, df->datamark, wlen);
   df->datamark += wlen;
   return (ssize_t)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) {
+    nghttp2_session *session, int32_t stream_id, uint8_t *buf, size_t len,
+    uint32_t *data_flags, nghttp2_data_source *source, void *user_data) {
   my_user_data *ud = (my_user_data *)user_data;
   size_t wlen;
+  (void)session;
+  (void)stream_id;
+  (void)buf;
+  (void)source;
+
   if (len < ud->data_source_length) {
     wlen = len;
   } else {
@@ -175,16 +185,6 @@ static void run_nghttp2_session_send(void) {
   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;
@@ -266,8 +266,12 @@ static void run_nghttp2_session_recv(void) {
   nghttp2_hd_deflater deflater;
   nghttp2_frame frame;
   nghttp2_bufs bufs;
-  nghttp2_nv nv[] = {MAKE_NV(":authority", "example.org"),
-                     MAKE_NV(":scheme", "https")};
+  nghttp2_nv nv[] = {
+      MAKE_NV(":method", "GET"),
+      MAKE_NV(":scheme", "https"),
+      MAKE_NV(":authority", "example.org"),
+      MAKE_NV(":path", "/"),
+  };
   nghttp2_settings_entry iv[2];
   my_user_data ud;
   data_feed df;
@@ -286,22 +290,51 @@ static void run_nghttp2_session_recv(void) {
   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());
+
+  /* Client preface */
+  nghttp2_bufs_add(&bufs, NGHTTP2_CLIENT_MAGIC, NGHTTP2_CLIENT_MAGIC_LEN);
+  data_feed_init(&df, &bufs);
+  nghttp2_bufs_reset(&bufs);
   nghttp2_failmalloc_unpause();
 
-  /* HEADERS */
+  rv = nghttp2_session_recv(session);
+  if (rv != 0) {
+    goto fail;
+  }
+
+  nghttp2_failmalloc_pause();
+  /* SETTINGS */
+  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());
+  data_feed_init(&df, &bufs);
+  nghttp2_bufs_reset(&bufs);
+  nghttp2_failmalloc_unpause();
+
+  rv = nghttp2_session_recv(session);
+  if (rv != 0) {
+    goto fail;
+  }
+
   nghttp2_failmalloc_pause();
+  /* HEADERS */
+  nvlen = ARRLEN(nv);
+  nghttp2_nv_array_copy(&nva, nv, nvlen, nghttp2_mem_fm());
   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);
@@ -338,26 +371,6 @@ static void run_nghttp2_session_recv(void) {
     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);
index b700b89..a922da6 100644 (file)
@@ -47,7 +47,7 @@ static int init_suite1(void) { return 0; }
 
 static int clean_suite1(void) { return 0; }
 
-int main(int argc _U_, char *argv[] _U_) {
+int main() {
   CU_pSuite pSuite = NULL;
   unsigned int num_tests_failed;
 
index 6873bff..f814c3d 100644 (file)
@@ -39,19 +39,29 @@ int nghttp2_nmalloc = 0;
     }                                                                          \
   } while (0)
 
-static void *my_malloc(size_t size, void *mud _U_) {
+static void *my_malloc(size_t size, void *mud) {
+  (void)mud;
+
   CHECK_PREREQ;
   return malloc(size);
 }
 
-static void my_free(void *ptr, void *mud _U_) { free(ptr); }
+static void my_free(void *ptr, void *mud) {
+  (void)mud;
+
+  free(ptr);
+}
+
+static void *my_calloc(size_t nmemb, size_t size, void *mud) {
+  (void)mud;
 
-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_) {
+static void *my_realloc(void *ptr, size_t size, void *mud) {
+  (void)mud;
+
   CHECK_PREREQ;
   return realloc(ptr, size);
 }
index cf51904..e1064e0 100644 (file)
@@ -522,6 +522,7 @@ void test_nghttp2_nv_array_copy(void) {
   bignv.namelen = strlen("echo");
   bignv.valuelen = (1 << 14) - 1;
   bignv.value = mem->malloc(bignv.valuelen, NULL);
+  bignv.flags = NGHTTP2_NV_FLAG_NONE;
   memset(bignv.value, '0', bignv.valuelen);
 
   rv = nghttp2_nv_array_copy(&nva, NULL, 0, mem);
index 2f77a7a..0408bf5 100644 (file)
@@ -295,10 +295,11 @@ void test_nghttp2_hd_inflate_indname_inc(void) {
   assert_nv_equal(&nv, out.nva, 1, mem);
   CU_ASSERT(1 == inflater.ctx.hd_table.len);
   CU_ASSERT(62 == nghttp2_hd_inflate_get_num_table_entries(&inflater));
-  assert_nv_equal(&nv, nghttp2_hd_inflate_get_table_entry(
-                           &inflater, NGHTTP2_STATIC_TABLE_LENGTH +
-                                          inflater.ctx.hd_table.len),
-                  1, mem);
+  assert_nv_equal(
+      &nv,
+      nghttp2_hd_inflate_get_table_entry(
+          &inflater, NGHTTP2_STATIC_TABLE_LENGTH + inflater.ctx.hd_table.len),
+      1, mem);
 
   nva_out_reset(&out, mem);
   nghttp2_bufs_free(&bufs);
@@ -424,10 +425,11 @@ void test_nghttp2_hd_inflate_newname_inc(void) {
   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, nghttp2_hd_inflate_get_table_entry(
-                           &inflater, NGHTTP2_STATIC_TABLE_LENGTH +
-                                          inflater.ctx.hd_table.len),
-                  1, mem);
+  assert_nv_equal(
+      &nv,
+      nghttp2_hd_inflate_get_table_entry(
+          &inflater, NGHTTP2_STATIC_TABLE_LENGTH + inflater.ctx.hd_table.len),
+      1, mem);
 
   nva_out_reset(&out, mem);
   nghttp2_bufs_free(&bufs);
index 699a53c..c5e9de3 100644 (file)
@@ -100,7 +100,12 @@ static void shuffle(int *a, int n) {
   }
 }
 
-static int eachfun(nghttp2_map_entry *entry _U_, void *ptr _U_) { return 0; }
+static int eachfun(nghttp2_map_entry *entry, void *ptr) {
+  (void)entry;
+  (void)ptr;
+
+  return 0;
+}
 
 #define NUM_ENT 6000
 static strentry arr[NUM_ENT];
index b0e694d..5fcb0ee 100644 (file)
@@ -127,8 +127,10 @@ static int node_less(const void *lhs, const void *rhs) {
   return ln->key < rn->key;
 }
 
-static int node_update(nghttp2_pq_entry *item, void *arg _U_) {
+static int node_update(nghttp2_pq_entry *item, void *arg) {
   node *nd = (node *)item;
+  (void)arg;
+
   if ((nd->key % 2) == 0) {
     nd->key *= -1;
     return 1;
index 928d131..783b0ed 100644 (file)
@@ -81,8 +81,10 @@ typedef struct {
 } my_user_data;
 
 static const nghttp2_nv reqnv[] = {
-    MAKE_NV(":method", "GET"), MAKE_NV(":path", "/"),
-    MAKE_NV(":scheme", "https"), MAKE_NV(":authority", "localhost"),
+    MAKE_NV(":method", "GET"),
+    MAKE_NV(":path", "/"),
+    MAKE_NV(":scheme", "https"),
+    MAKE_NV(":authority", "localhost"),
 };
 
 static const nghttp2_nv resnv[] = {
@@ -118,30 +120,45 @@ static void scripted_data_feed_init2(scripted_data_feed *df,
   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_) {
+static ssize_t null_send_callback(nghttp2_session *session, const uint8_t *data,
+                                  size_t len, int flags, void *user_data) {
+  (void)session;
+  (void)data;
+  (void)flags;
+  (void)user_data;
+
   return (ssize_t)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_) {
+static ssize_t fail_send_callback(nghttp2_session *session, const uint8_t *data,
+                                  size_t len, int flags, void *user_data) {
+  (void)session;
+  (void)data;
+  (void)len;
+  (void)flags;
+  (void)user_data;
+
   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) {
+static ssize_t fixed_bytes_send_callback(nghttp2_session *session,
+                                         const uint8_t *data, size_t len,
+                                         int flags, void *user_data) {
   size_t fixed_sendlen = ((my_user_data *)user_data)->fixed_sendlen;
+  (void)session;
+  (void)data;
+  (void)flags;
+
   return (ssize_t)(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) {
+static ssize_t scripted_recv_callback(nghttp2_session *session, uint8_t *data,
+                                      size_t len, int flags, 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];
+  (void)session;
+  (void)flags;
+
   memcpy(data, df->datamark, wlen);
   df->datamark += wlen;
   df->feedseq[df->seqidx] -= wlen;
@@ -151,33 +168,46 @@ static ssize_t scripted_recv_callback(nghttp2_session *session _U_,
   return (ssize_t)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_) {
+static ssize_t eof_recv_callback(nghttp2_session *session, uint8_t *data,
+                                 size_t len, int flags, void *user_data) {
+  (void)session;
+  (void)data;
+  (void)len;
+  (void)flags;
+  (void)user_data;
+
   return NGHTTP2_ERR_EOF;
 }
 
-static ssize_t accumulator_send_callback(nghttp2_session *session _U_,
+static ssize_t accumulator_send_callback(nghttp2_session *session,
                                          const uint8_t *buf, size_t len,
-                                         int flags _U_, void *user_data) {
+                                         int flags, void *user_data) {
   accumulator *acc = ((my_user_data *)user_data)->acc;
+  (void)session;
+  (void)flags;
+
   assert(acc->length + len < sizeof(acc->buf));
   memcpy(acc->buf + acc->length, buf, len);
   acc->length += len;
   return (ssize_t)len;
 }
 
-static int on_begin_frame_callback(nghttp2_session *session _U_,
-                                   const nghttp2_frame_hd *hd _U_,
+static int on_begin_frame_callback(nghttp2_session *session,
+                                   const nghttp2_frame_hd *hd,
                                    void *user_data) {
   my_user_data *ud = (my_user_data *)user_data;
+  (void)session;
+  (void)hd;
+
   ++ud->begin_frame_cb_called;
   return 0;
 }
 
-static int on_frame_recv_callback(nghttp2_session *session _U_,
+static int on_frame_recv_callback(nghttp2_session *session,
                                   const nghttp2_frame *frame, void *user_data) {
   my_user_data *ud = (my_user_data *)user_data;
+  (void)session;
+
   ++ud->frame_recv_cb_called;
   ud->recv_frame_type = frame->hd.type;
   ud->recv_frame_hd = frame->hd;
@@ -185,90 +215,129 @@ static int on_frame_recv_callback(nghttp2_session *session _U_,
   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) {
+static int on_invalid_frame_recv_callback(nghttp2_session *session,
+                                          const nghttp2_frame *frame,
+                                          int lib_error_code, void *user_data) {
   my_user_data *ud = (my_user_data *)user_data;
+  (void)session;
+  (void)frame;
+  (void)lib_error_code;
+
   ++ud->invalid_frame_recv_cb_called;
   return 0;
 }
 
-static int on_frame_send_callback(nghttp2_session *session _U_,
+static int on_frame_send_callback(nghttp2_session *session,
                                   const nghttp2_frame *frame, void *user_data) {
   my_user_data *ud = (my_user_data *)user_data;
+  (void)session;
+
   ++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_,
+static int on_frame_not_send_callback(nghttp2_session *session,
                                       const nghttp2_frame *frame, int lib_error,
                                       void *user_data) {
   my_user_data *ud = (my_user_data *)user_data;
+  (void)session;
+
   ++ud->frame_not_send_cb_called;
   ud->not_sent_frame_type = frame->hd.type;
   ud->not_sent_error = lib_error;
   return 0;
 }
 
-static int cancel_before_frame_send_callback(nghttp2_session *session _U_,
-                                             const nghttp2_frame *frame _U_,
+static int cancel_before_frame_send_callback(nghttp2_session *session,
+                                             const nghttp2_frame *frame,
                                              void *user_data) {
   my_user_data *ud = (my_user_data *)user_data;
+  (void)session;
+  (void)frame;
+
   ++ud->before_frame_send_cb_called;
   return NGHTTP2_ERR_CANCEL;
 }
 
-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) {
+static 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) {
   my_user_data *ud = (my_user_data *)user_data;
+  (void)session;
+  (void)flags;
+  (void)stream_id;
+  (void)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) {
+static int pause_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) {
   my_user_data *ud = (my_user_data *)user_data;
+  (void)session;
+  (void)flags;
+  (void)stream_id;
+  (void)data;
+  (void)len;
+
   ++ud->data_chunk_recv_cb_called;
   return NGHTTP2_ERR_PAUSE;
 }
 
-static ssize_t select_padding_callback(nghttp2_session *session _U_,
+static ssize_t select_padding_callback(nghttp2_session *session,
                                        const nghttp2_frame *frame,
                                        size_t max_payloadlen, void *user_data) {
   my_user_data *ud = (my_user_data *)user_data;
+  (void)session;
+
   return (ssize_t)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_) {
+    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) {
+  (void)session;
+  (void)frame_type;
+  (void)stream_id;
+  (void)session_remote_window_size;
+  (void)stream_remote_window_size;
+  (void)remote_max_frame_size;
+  (void)user_data;
+
   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_) {
+    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) {
+  (void)session;
+  (void)frame_type;
+  (void)stream_id;
+  (void)session_remote_window_size;
+  (void)stream_remote_window_size;
+  (void)remote_max_frame_size;
+  (void)user_data;
+
   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) {
+    nghttp2_session *session, int32_t stream_id, uint8_t *buf, size_t len,
+    uint32_t *data_flags, nghttp2_data_source *source, void *user_data) {
   my_user_data *ud = (my_user_data *)user_data;
   size_t wlen;
+  (void)session;
+  (void)stream_id;
+  (void)buf;
+  (void)source;
+
   if (len < ud->data_source_length) {
     wlen = len;
   } else {
@@ -282,35 +351,59 @@ static ssize_t fixed_length_data_source_read_callback(
 }
 
 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_) {
+    nghttp2_session *session, int32_t stream_id, uint8_t *buf, size_t len,
+    uint32_t *data_flags, nghttp2_data_source *source, void *user_data) {
+  (void)session;
+  (void)stream_id;
+  (void)buf;
+  (void)len;
+  (void)data_flags;
+  (void)source;
+  (void)user_data;
+
   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_) {
+static ssize_t fail_data_source_read_callback(nghttp2_session *session,
+                                              int32_t stream_id, uint8_t *buf,
+                                              size_t len, uint32_t *data_flags,
+                                              nghttp2_data_source *source,
+                                              void *user_data) {
+  (void)session;
+  (void)stream_id;
+  (void)buf;
+  (void)len;
+  (void)data_flags;
+  (void)source;
+  (void)user_data;
+
   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_) {
+    nghttp2_session *session, int32_t stream_id, uint8_t *buf, size_t len,
+    uint32_t *data_flags, nghttp2_data_source *source, void *user_data) {
+  (void)session;
+  (void)stream_id;
+  (void)buf;
+  (void)len;
+  (void)source;
+  (void)user_data;
+
   *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) {
+    nghttp2_session *session, int32_t stream_id, uint8_t *buf, size_t len,
+    uint32_t *data_flags, nghttp2_data_source *source, void *user_data) {
   my_user_data *ud = (my_user_data *)user_data;
   size_t wlen;
+  (void)session;
+  (void)stream_id;
+  (void)buf;
+  (void)source;
+
   if (len < ud->data_source_length) {
     wlen = len;
   } else {
@@ -327,11 +420,12 @@ static ssize_t no_copy_data_source_read_callback(
   return (ssize_t)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) {
+static int send_data_callback(nghttp2_session *session, nghttp2_frame *frame,
+                              const uint8_t *framehd, size_t length,
+                              nghttp2_data_source *source, void *user_data) {
   accumulator *acc = ((my_user_data *)user_data)->acc;
+  (void)session;
+  (void)source;
 
   memcpy(acc->buf + acc->length, framehd, NGHTTP2_FRAME_HDLEN);
   acc->length += NGHTTP2_FRAME_HDLEN;
@@ -349,10 +443,13 @@ static int send_data_callback(nghttp2_session *session _U_,
   return 0;
 }
 
-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) {
+static ssize_t block_count_send_callback(nghttp2_session *session,
+                                         const uint8_t *data, size_t len,
+                                         int flags, void *user_data) {
   my_user_data *ud = (my_user_data *)user_data;
+  (void)session;
+  (void)data;
+  (void)flags;
 
   if (ud->block_count == 0) {
     return NGHTTP2_ERR_WOULDBLOCK;
@@ -362,12 +459,14 @@ static ssize_t block_count_send_callback(nghttp2_session *session _U_,
   return (ssize_t)len;
 }
 
-static int on_header_callback(nghttp2_session *session _U_,
+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) {
+                              size_t valuelen, uint8_t flags, void *user_data) {
   my_user_data *ud = (my_user_data *)user_data;
+  (void)session;
+  (void)flags;
+
   ++ud->header_cb_called;
   ud->nv.name = (uint8_t *)name;
   ud->nv.namelen = namelen;
@@ -397,12 +496,15 @@ static int temporal_failure_on_header_callback(
   return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
 }
 
-static int on_invalid_header_callback(nghttp2_session *session _U_,
+static int on_invalid_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) {
+                                      uint8_t flags, void *user_data) {
   my_user_data *ud = (my_user_data *)user_data;
+  (void)session;
+  (void)flags;
+
   ++ud->invalid_header_cb_called;
   ud->nv.name = (uint8_t *)name;
   ud->nv.namelen = namelen;
@@ -435,10 +537,13 @@ static int reset_on_invalid_header_callback(nghttp2_session *session,
   return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
 }
 
-static int on_begin_headers_callback(nghttp2_session *session _U_,
-                                     const nghttp2_frame *frame _U_,
+static int on_begin_headers_callback(nghttp2_session *session,
+                                     const nghttp2_frame *frame,
                                      void *user_data) {
   my_user_data *ud = (my_user_data *)user_data;
+  (void)session;
+  (void)frame;
+
   ++ud->begin_headers_cb_called;
   return 0;
 }
@@ -449,71 +554,99 @@ static int temporal_failure_on_begin_headers_callback(
   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_) {
+static ssize_t defer_data_source_read_callback(nghttp2_session *session,
+                                               int32_t stream_id, uint8_t *buf,
+                                               size_t len, uint32_t *data_flags,
+                                               nghttp2_data_source *source,
+                                               void *user_data) {
+  (void)session;
+  (void)stream_id;
+  (void)buf;
+  (void)len;
+  (void)data_flags;
+  (void)source;
+  (void)user_data;
+
   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_,
+static int on_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;
+  (void)session;
+  (void)stream_id;
+  (void)error_code;
+
   ++my_data->stream_close_cb_called;
   my_data->stream_close_error_code = error_code;
 
   return 0;
 }
 
-static ssize_t pack_extension_callback(nghttp2_session *session _U_,
-                                       uint8_t *buf, size_t len _U_,
-                                       const nghttp2_frame *frame,
-                                       void *user_data _U_) {
+static ssize_t pack_extension_callback(nghttp2_session *session, uint8_t *buf,
+                                       size_t len, const nghttp2_frame *frame,
+                                       void *user_data) {
   nghttp2_buf *p = frame->ext.payload;
+  (void)session;
+  (void)len;
+  (void)user_data;
 
   memcpy(buf, p->pos, nghttp2_buf_len(p));
 
   return (ssize_t)nghttp2_buf_len(p);
 }
 
-static int on_extension_chunk_recv_callback(nghttp2_session *session _U_,
-                                            const nghttp2_frame_hd *hd _U_,
+static int on_extension_chunk_recv_callback(nghttp2_session *session,
+                                            const nghttp2_frame_hd *hd,
                                             const uint8_t *data, size_t len,
                                             void *user_data) {
   my_user_data *my_data = (my_user_data *)user_data;
   nghttp2_buf *buf = &my_data->scratchbuf;
+  (void)session;
+  (void)hd;
 
   buf->last = nghttp2_cpymem(buf->last, data, len);
 
   return 0;
 }
 
-static int cancel_on_extension_chunk_recv_callback(
-    nghttp2_session *session _U_, const nghttp2_frame_hd *hd _U_,
-    const uint8_t *data _U_, size_t len _U_, void *user_data _U_) {
+static int cancel_on_extension_chunk_recv_callback(nghttp2_session *session,
+                                                   const nghttp2_frame_hd *hd,
+                                                   const uint8_t *data,
+                                                   size_t len,
+                                                   void *user_data) {
+  (void)session;
+  (void)hd;
+  (void)data;
+  (void)len;
+  (void)user_data;
+
   return NGHTTP2_ERR_CANCEL;
 }
 
-static int unpack_extension_callback(nghttp2_session *session _U_,
-                                     void **payload,
-                                     const nghttp2_frame_hd *hd _U_,
+static int unpack_extension_callback(nghttp2_session *session, void **payload,
+                                     const nghttp2_frame_hd *hd,
                                      void *user_data) {
   my_user_data *my_data = (my_user_data *)user_data;
   nghttp2_buf *buf = &my_data->scratchbuf;
+  (void)session;
+  (void)hd;
 
   *payload = buf;
 
   return 0;
 }
 
-static int cancel_unpack_extension_callback(nghttp2_session *session _U_,
-                                            void **payload _U_,
-                                            const nghttp2_frame_hd *hd _U_,
-                                            void *user_data _U_) {
+static int cancel_unpack_extension_callback(nghttp2_session *session,
+                                            void **payload,
+                                            const nghttp2_frame_hd *hd,
+                                            void *user_data) {
+  (void)session;
+  (void)payload;
+  (void)hd;
+  (void)user_data;
+
   return NGHTTP2_ERR_CANCEL;
 }
 
@@ -807,7 +940,7 @@ void test_nghttp2_session_recv_data(void) {
   stream = open_sent_stream2(session, 1, NGHTTP2_STREAM_CLOSING);
 
   /* Set initial window size 16383 to check stream flow control,
-     isolating it from the conneciton flow control */
+     isolating it from the connection flow control */
   stream->local_window_size = 16383;
 
   ud.data_chunk_recv_cb_called = 0;
@@ -1444,8 +1577,9 @@ void test_nghttp2_session_recv_headers_with_priority(void) {
 
 static int response_on_begin_frame_callback(nghttp2_session *session,
                                             const nghttp2_frame_hd *hd,
-                                            void *user_data _U_) {
+                                            void *user_data) {
   int rv;
+  (void)user_data;
 
   if (hd->type != NGHTTP2_HEADERS) {
     return 0;
@@ -2024,6 +2158,7 @@ void test_nghttp2_session_recv_altsvc(void) {
   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_option_new(&option);
   nghttp2_option_set_builtin_recv_extension_type(option, NGHTTP2_ALTSVC);
@@ -2071,6 +2206,72 @@ void test_nghttp2_session_recv_altsvc(void) {
 
   nghttp2_session_del(session);
 
+  /* zero-length value */
+  nghttp2_buf_reset(&buf);
+
+  nghttp2_session_client_new2(&session, &callbacks, &ud, option);
+
+  nghttp2_frame_hd_init(&hd, 2 + sizeof(origin) - 1, NGHTTP2_ALTSVC,
+                        NGHTTP2_FLAG_NONE, 0);
+  nghttp2_frame_pack_frame_hd(buf.last, &hd);
+  buf.last += NGHTTP2_FRAME_HDLEN;
+  nghttp2_put_uint16be(buf.last, sizeof(origin) - 1);
+  buf.last += 2;
+  buf.last = nghttp2_cpymem(buf.last, origin, sizeof(origin) - 1);
+
+  ud.invalid_frame_recv_cb_called = 0;
+  rv = nghttp2_session_mem_recv(session, buf.pos, nghttp2_buf_len(&buf));
+
+  CU_ASSERT((ssize_t)nghttp2_buf_len(&buf) == rv);
+  CU_ASSERT(1 == ud.invalid_frame_recv_cb_called);
+
+  nghttp2_session_del(session);
+
+  /* non-empty origin to a stream other than 0 */
+  nghttp2_buf_reset(&buf);
+
+  nghttp2_session_client_new2(&session, &callbacks, &ud, option);
+
+  open_sent_stream(session, 1);
+
+  nghttp2_frame_hd_init(&hd, 2 + sizeof(origin) - 1 + sizeof(field_value) - 1,
+                        NGHTTP2_ALTSVC, NGHTTP2_FLAG_NONE, 1);
+  nghttp2_frame_pack_frame_hd(buf.last, &hd);
+  buf.last += NGHTTP2_FRAME_HDLEN;
+  nghttp2_put_uint16be(buf.last, sizeof(origin) - 1);
+  buf.last += 2;
+  buf.last = nghttp2_cpymem(buf.last, origin, sizeof(origin) - 1);
+  buf.last = nghttp2_cpymem(buf.last, field_value, sizeof(field_value) - 1);
+
+  ud.invalid_frame_recv_cb_called = 0;
+  rv = nghttp2_session_mem_recv(session, buf.pos, nghttp2_buf_len(&buf));
+
+  CU_ASSERT((ssize_t)nghttp2_buf_len(&buf) == rv);
+  CU_ASSERT(1 == ud.invalid_frame_recv_cb_called);
+
+  nghttp2_session_del(session);
+
+  /* empty origin to stream 0 */
+  nghttp2_buf_reset(&buf);
+
+  nghttp2_session_client_new2(&session, &callbacks, &ud, option);
+
+  nghttp2_frame_hd_init(&hd, 2 + sizeof(field_value) - 1, NGHTTP2_ALTSVC,
+                        NGHTTP2_FLAG_NONE, 0);
+  nghttp2_frame_pack_frame_hd(buf.last, &hd);
+  buf.last += NGHTTP2_FRAME_HDLEN;
+  nghttp2_put_uint16be(buf.last, 0);
+  buf.last += 2;
+  buf.last = nghttp2_cpymem(buf.last, field_value, sizeof(field_value) - 1);
+
+  ud.invalid_frame_recv_cb_called = 0;
+  rv = nghttp2_session_mem_recv(session, buf.pos, nghttp2_buf_len(&buf));
+
+  CU_ASSERT((ssize_t)nghttp2_buf_len(&buf) == rv);
+  CU_ASSERT(1 == ud.invalid_frame_recv_cb_called);
+
+  nghttp2_session_del(session);
+
   /* send large frame (16KiB) */
   nghttp2_buf_reset(&buf);
 
@@ -2096,6 +2297,31 @@ void test_nghttp2_session_recv_altsvc(void) {
 
   nghttp2_session_del(session);
 
+  /* send too large frame */
+  nghttp2_buf_reset(&buf);
+
+  nghttp2_session_client_new2(&session, &callbacks, &ud, option);
+
+  session->local_settings.max_frame_size = NGHTTP2_MAX_FRAME_SIZE_MIN - 1;
+
+  nghttp2_frame_hd_init(&hd, NGHTTP2_MAX_FRAME_SIZE_MIN + 1, NGHTTP2_ALTSVC,
+                        NGHTTP2_FLAG_NONE, 0);
+  nghttp2_frame_pack_frame_hd(buf.last, &hd);
+  buf.last += NGHTTP2_FRAME_HDLEN;
+  nghttp2_put_uint16be(buf.last, sizeof(origin) - 1);
+  buf.last += 2;
+  buf.last = nghttp2_cpymem(buf.last, origin, sizeof(origin) - 1);
+  memset(buf.last, 0, nghttp2_buf_avail(&buf));
+  buf.last += nghttp2_buf_avail(&buf);
+
+  ud.frame_recv_cb_called = 0;
+  rv = nghttp2_session_mem_recv(session, buf.pos, nghttp2_buf_len(&buf));
+
+  CU_ASSERT((ssize_t)nghttp2_buf_len(&buf) == rv);
+  CU_ASSERT(0 == ud.frame_recv_cb_called);
+
+  nghttp2_session_del(session);
+
   /* received by server */
   nghttp2_buf_reset(&buf);
 
@@ -4380,19 +4606,25 @@ void test_nghttp2_submit_data_read_length_smallest(void) {
 }
 
 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_) {
+    nghttp2_session *session, int32_t stream_id, uint8_t *buf, size_t len,
+    uint32_t *data_flags, nghttp2_data_source *source, void *user_data) {
+  (void)session;
+  (void)stream_id;
+  (void)buf;
+  (void)source;
+  (void)user_data;
+
   *data_flags |= NGHTTP2_DATA_FLAG_EOF;
   return (ssize_t)nghttp2_min(len, 16);
 }
 
 static int submit_data_twice_on_frame_send_callback(nghttp2_session *session,
                                                     const nghttp2_frame *frame,
-                                                    void *user_data _U_) {
+                                                    void *user_data) {
   static int called = 0;
   int rv;
   nghttp2_data_provider data_prd;
+  (void)user_data;
 
   if (called == 0) {
     called = 1;
@@ -5671,9 +5903,8 @@ void test_nghttp2_submit_altsvc(void) {
 
   len = nghttp2_session_mem_send(session, &data);
 
-  CU_ASSERT(len ==
-            NGHTTP2_FRAME_HDLEN + 2 + sizeof(origin) - 1 + sizeof(field_value) -
-                1);
+  CU_ASSERT(len == NGHTTP2_FRAME_HDLEN + 2 + sizeof(origin) - 1 +
+                       sizeof(field_value) - 1);
 
   nghttp2_frame_unpack_frame_hd(&hd, data);
 
@@ -6279,8 +6510,8 @@ void test_nghttp2_session_flow_control_data_recv(void) {
 
   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));
+            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
@@ -6298,8 +6529,8 @@ void test_nghttp2_session_flow_control_data_recv(void) {
      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));
+            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);
@@ -9252,9 +9483,12 @@ void test_nghttp2_session_defer_then_close(void) {
 
 static int submit_response_on_stream_close(nghttp2_session *session,
                                            int32_t stream_id,
-                                           uint32_t error_code _U_,
-                                           void *user_data _U_) {
+                                           uint32_t error_code,
+                                           void *user_data) {
   nghttp2_data_provider data_prd;
+  (void)error_code;
+  (void)user_data;
+
   data_prd.read_callback = temporal_failure_data_source_read_callback;
 
   // Attempt to submit response or data to the stream being closed
@@ -10896,9 +11130,12 @@ void test_nghttp2_http_ignore_regular_header(void) {
   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));
+  /* Without on_invalid_frame_recv_callback, bad header causes stream
+     reset */
+  item = nghttp2_session_get_next_ob_item(session);
+
+  CU_ASSERT(NGHTTP2_RST_STREAM == item->frame.hd.type);
+
   proclen += (size_t)rv;
 
   CU_ASSERT(nghttp2_buf_len(&bufs.head->buf) == proclen);
index 7e8d1b4..b8869f5 100644 (file)
@@ -53,16 +53,14 @@ int unpack_frame(nghttp2_frame *frame, const uint8_t *in, size_t len) {
   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);
+    rv = nghttp2_frame_unpack_headers_payload(&frame->headers,
+                                              payload + payloadoff);
     break;
   case NGHTTP2_PRIORITY:
-    nghttp2_frame_unpack_priority_payload(&frame->priority, payload,
-                                          payloadlen);
+    nghttp2_frame_unpack_priority_payload(&frame->priority, payload);
     break;
   case NGHTTP2_RST_STREAM:
-    nghttp2_frame_unpack_rst_stream_payload(&frame->rst_stream, payload,
-                                            payloadlen);
+    nghttp2_frame_unpack_rst_stream_payload(&frame->rst_stream, payload);
     break;
   case NGHTTP2_SETTINGS:
     rv = nghttp2_frame_unpack_settings_payload2(
@@ -70,18 +68,17 @@ int unpack_frame(nghttp2_frame *frame, const uint8_t *in, size_t len) {
     break;
   case NGHTTP2_PUSH_PROMISE:
     rv = nghttp2_frame_unpack_push_promise_payload(&frame->push_promise,
-                                                   payload, payloadlen);
+                                                   payload);
     break;
   case NGHTTP2_PING:
-    nghttp2_frame_unpack_ping_payload(&frame->ping, payload, payloadlen);
+    nghttp2_frame_unpack_ping_payload(&frame->ping, payload);
     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);
+    nghttp2_frame_unpack_window_update_payload(&frame->window_update, payload);
     break;
   case NGHTTP2_ALTSVC:
     assert(payloadlen > 2);
index e76fe7b..9bf74b1 100644 (file)
@@ -1,7 +1,7 @@
-# Makefile.in generated by automake 1.15 from Makefile.am.
+# Makefile.in generated by automake 1.15.1 from Makefile.am.
 # @configure_input@
 
-# Copyright (C) 1994-2014 Free Software Foundation, Inc.
+# Copyright (C) 1994-2017 Free Software Foundation, Inc.
 
 # This Makefile.in is free software; the Free Software Foundation
 # gives unlimited permission to copy and/or distribute it,
@@ -206,8 +206,6 @@ LIBMRUBY_CFLAGS = @LIBMRUBY_CFLAGS@
 LIBMRUBY_LIBS = @LIBMRUBY_LIBS@
 LIBOBJS = @LIBOBJS@
 LIBS = @LIBS@
-LIBSPDYLAY_CFLAGS = @LIBSPDYLAY_CFLAGS@
-LIBSPDYLAY_LIBS = @LIBSPDYLAY_LIBS@
 LIBTOOL = @LIBTOOL@
 LIBXML2_CFLAGS = @LIBXML2_CFLAGS@
 LIBXML2_LIBS = @LIBXML2_LIBS@
index 215cea8..04cdb42 100644 (file)
@@ -1,7 +1,7 @@
-# Makefile.in generated by automake 1.15 from Makefile.am.
+# Makefile.in generated by automake 1.15.1 from Makefile.am.
 # @configure_input@
 
-# Copyright (C) 1994-2014 Free Software Foundation, Inc.
+# Copyright (C) 1994-2017 Free Software Foundation, Inc.
 
 # This Makefile.in is free software; the Free Software Foundation
 # gives unlimited permission to copy and/or distribute it,
@@ -277,8 +277,6 @@ LIBMRUBY_CFLAGS = @LIBMRUBY_CFLAGS@
 LIBMRUBY_LIBS = @LIBMRUBY_LIBS@
 LIBOBJS = @LIBOBJS@
 LIBS = @LIBS@
-LIBSPDYLAY_CFLAGS = @LIBSPDYLAY_CFLAGS@
-LIBSPDYLAY_LIBS = @LIBSPDYLAY_LIBS@
 LIBTOOL = @LIBTOOL@
 LIBXML2_CFLAGS = @LIBXML2_CFLAGS@
 LIBXML2_LIBS = @LIBXML2_LIBS@
index 7ddd2e9..706e874 100644 (file)
@@ -4,7 +4,7 @@ MRuby::Build.new do |conf|
 
   # C++ project needs this.  Without this, mruby exception does not
   # properly destory C++ object allocated on stack.
-  conf.enable_cxx_abi
+  conf.enable_cxx_exception
 
   conf.build_dir = ENV['BUILD_DIR']
 
index 180fd75..e3b95d7 100644 (file)
@@ -34,3 +34,4 @@ Original Authors "mruby developers" are:
    Yuichi Osawa
    Terence Lee
    Zachary Scott
+   Tomasz Dąbrowski
diff --git a/third-party/mruby/ChangeLog b/third-party/mruby/ChangeLog
deleted file mode 100644 (file)
index 93a18f4..0000000
+++ /dev/null
@@ -1,15 +0,0 @@
-Thu Apl 19 17:25:18 2012  Yukihiro Matsumoto  <matz@ruby-lang.org>
-
-       * first release version 1.0.0 released.
-
-Local variables:
-add-log-time-format: (lambda ()
-  (let* ((time (current-time))
-        (system-time-locale "C")
-        (diff (+ (cadr time) 32400))
-        (lo (% diff 65536))
-        (hi (+ (car time) (/ diff 65536))))
-  (format-time-string "%a %b %e %H:%M:%S %Y" (list hi lo) t)))
-indent-tabs-mode: t
-tab-width: 8
-end:
index 1189801..d02b8fe 100644 (file)
@@ -1,4 +1,4 @@
-Copyright (c) 2015 mruby developers
+Copyright (c) 2017 mruby developers
 
 Permission is hereby granted, free of charge, to any person obtaining a
 copy of this software and associated documentation files (the "Software"),
index e57b79b..9d0c611 100644 (file)
@@ -17,7 +17,7 @@ of the Ministry of Economy, Trade and Industry of Japan.
 
 ## How to get mruby
 
-The stable version 1.2.0 of mruby can be downloaded via the following URL: [https://github.com/mruby/mruby/archive/1.2.0.zip](https://github.com/mruby/mruby/archive/1.2.0.zip)
+The stable version 1.3.0 of mruby can be downloaded via the following URL: [https://github.com/mruby/mruby/archive/1.3.0.zip](https://github.com/mruby/mruby/archive/1.3.0.zip)
 
 The latest development version of mruby can be downloaded via the following URL: [https://github.com/mruby/mruby/zipball/master](https://github.com/mruby/mruby/zipball/master)
 
@@ -26,13 +26,15 @@ following command:
 
     $ git clone https://github.com/mruby/mruby.git
 
+You can also install and compile mruby using [ruby-install](https://github.com/postmodern/ruby-install), [ruby-build](https://github.com/rbenv/ruby-build) or [rvm](https://github.com/rvm/rvm).
+
 ## mruby home-page
 
 The URL of the mruby home-page is: [http://www.mruby.org](http://www.mruby.org).
 
 ## Mailing list
 
-We don't have mailing list, but you can use [GitHub issues](https://github.com/mruby/mruby).
+We don't have mailing list, but you can use [GitHub issues](https://github.com/mruby/mruby).
 
 ## How to compile and install (mruby and gems)
 
@@ -52,7 +54,7 @@ Or
 
 mruby contains a package manager called *mrbgems*. To create extensions
 in C and/or Ruby you should create a *GEM*. For a documentation of how to
-use mrbgems consult the file [doc/mrbgems/README.md](doc/mrbgems/README.md). For example code of
+use mrbgems consult the file [doc/guides/mrbgems.md](doc/guides/mrbgems.md). For example code of
 how to use mrbgems look into the folder *examples/mrbgems/*.
 
 ## License
index 3021bcc..dd14c8b 100644 (file)
@@ -28,6 +28,8 @@ load "#{MRUBY_ROOT}/tasks/libmruby.rake"
 
 load "#{MRUBY_ROOT}/tasks/benchmark.rake"
 
+load "#{MRUBY_ROOT}/tasks/gitlab.rake"
+
 ##############################
 # generic build targets, rules
 task :default => :all
diff --git a/third-party/mruby/appveyor.yml b/third-party/mruby/appveyor.yml
new file mode 100644 (file)
index 0000000..c62b135
--- /dev/null
@@ -0,0 +1,27 @@
+version: "{build}"
+
+os: Visual Studio 2015
+
+clone_depth: 50
+
+
+environment:
+  matrix:
+    # Visual Studio 2015 64bit
+    - visualcpp: C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat
+      machine: amd64
+
+    # Visual Studio 2013 64bit
+    - visualcpp: C:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\vcvarsall.bat
+      machine: amd64
+
+
+init:
+  - call "%visualcpp%" %machine%
+  # For using bison.exe
+  - set PATH=%PATH%;C:\cygwin\bin;
+
+
+build_script:
+  - set MRUBY_CONFIG=appveyor_config.rb
+  - ruby .\minirake test
diff --git a/third-party/mruby/appveyor_config.rb b/third-party/mruby/appveyor_config.rb
new file mode 100644 (file)
index 0000000..57e1033
--- /dev/null
@@ -0,0 +1,50 @@
+MRuby::Build.new('debug') do |conf|
+  toolchain :visualcpp
+  enable_debug
+
+  # include all core GEMs
+  conf.gembox 'full-core'
+  conf.compilers.each do |c|
+    c.defines += %w(MRB_GC_STRESS MRB_GC_FIXED_ARENA)
+  end
+
+  build_mrbc_exec
+end
+
+MRuby::Build.new('full-debug') do |conf|
+  toolchain :visualcpp
+  enable_debug
+
+  # include all core GEMs
+  conf.gembox 'full-core'
+  conf.cc.defines = %w(MRB_ENABLE_DEBUG_HOOK)
+
+  conf.enable_test
+end
+
+MRuby::Build.new do |conf|
+  toolchain :visualcpp
+
+  # include all core GEMs
+  conf.gembox 'full-core'
+  conf.compilers.each do |c|
+    c.defines += %w(MRB_GC_FIXED_ARENA)
+  end
+  conf.enable_bintest
+  conf.enable_test
+end
+
+MRuby::Build.new('cxx_abi') do |conf|
+  toolchain :visualcpp
+
+  conf.gembox 'full-core'
+  conf.compilers.each do |c|
+    c.defines += %w(MRB_GC_FIXED_ARENA)
+  end
+  conf.enable_bintest
+  conf.enable_test
+
+  enable_cxx_abi
+
+  build_mrbc_exec
+end
index 6b54359..8293092 100644 (file)
@@ -16,8 +16,10 @@ MRuby::Build.new do |conf|
   #   g.cc.flags << '-g' # append cflags in this gem
   # end
   # conf.gem 'examples/mrbgems/c_and_ruby_extension_example'
-  # conf.gem :github => 'masuidrive/mrbgems-example', :checksum_hash => '76518e8aecd131d047378448ac8055fa29d974a9'
-  # conf.gem :git => 'git@github.com:masuidrive/mrbgems-example.git', :branch => 'master', :options => '-v'
+  # conf.gem :core => 'mruby-eval'
+  # conf.gem :mgem => 'mruby-io'
+  # conf.gem :github => 'iij/mruby-io'
+  # conf.gem :git => 'git@github.com:iij/mruby-io.git', :branch => 'master', :options => '-v'
 
   # include the default GEMs
   conf.gembox 'default'
@@ -122,6 +124,18 @@ MRuby::Build.new('test') do |conf|
   conf.gembox 'default'
 end
 
+MRuby::Build.new('bench') do |conf|
+  # Gets set by the VS command prompts.
+  if ENV['VisualStudioVersion'] || ENV['VSINSTALLDIR']
+    toolchain :visualcpp
+  else
+    toolchain :gcc
+    conf.cc.flags << '-O3'
+  end
+
+  conf.gembox 'default'
+end
+
 # Define cross build settings
 # MRuby::CrossBuild.new('32bit') do |conf|
 #   toolchain :gcc
index a1c8988..2aaf6f1 100644 (file)
@@ -67,7 +67,7 @@ toolchain :clang
 Toolchain configuration for Visual Studio on Windows. If you use the
 [Visual Studio Command Prompt](http://msdn.microsoft.com/en-us/library/ms229859\(v=vs.110\).aspx),
 you normally do not have to specify this manually, since it gets automatically detected by our build process.
-```
+```ruby
 toolchain :visualcpp
 ```
 
@@ -241,25 +241,32 @@ conf.enable_bintest
 
 ### C++ ABI
 
-mruby can use C++ exception to raise exception internally.
-It is called C++ ABI mode.
-By using C++ exception it can release C++ stack object correctly.
-Whenever you mix C++ code C++ ABI mode would be enabled automatically.
-If you need to enable C++ ABI mode explicitly add the following:
+By default, mruby uses setjmp/longjmp to implement its
+exceptions. But it doesn't release C++ stack object
+correctly. To support mrbgems written in C++, mruby can be
+configured to use C++ exception.
+
+There are two levels of C++ exception handling. The one is
+```enable_cxx_exception``` that enables C++ exception, but
+uses C ABI. The other is ```enable_cxx_abi``` where all
+files are compiled by C++ compiler.
+
+When you mix C++ code, C++ exception would be enabled automatically.
+If you need to enable C++ exception explicitly add the following:
 ```ruby
-conf.enable_cxx_abi
+conf.enable_cxx_exception
 ```
 
 #### C++ exception disabling.
 
-If you need to force C++ exception disable
-(For example using a compiler option to disable C++ exception)
-add following:
+If your compiler does not support C++ and you want to ensure
+you don't use mrbgem written in C++, you can explicitly disable
+C++ exception, add following:
 ```ruby
 conf.disable_cxx_exception
 ```
-
-Note that it must be called before ```enable_cxx_abi``` or ```gem``` method.
+and you will get an error when you try to use C++ gem.
+Note that it must be called before ```enable_cxx_exception``` or ```gem``` method.
 
 ### Debugging mode
 
index 366701c..72f2c2b 100644 (file)
@@ -38,7 +38,7 @@ To confirm mrdb was installed properly, run mrdb with the `--version` option:
 
 ```bash
 $ mrdb --version
-mruby 1.2.0 (2014-11-17)
+mruby 1.3.0 (2017-7-4)
 ```
 
 ## 2.2 Basic Operation
index f2a0448..d6659c8 100644 (file)
@@ -1,7 +1,9 @@
-# How to use `mrb_gc_arena_save()`/`mrb_gc_arena_restore()`
+# How to use `mrb_gc_arena_save()`/`mrb_gc_arena_restore()`/`mrb_gc_protect()`
 
-This is basically English translation of [Matz's blog post](http://www.rubyist.net/~matz/20130731.html) written in Japanese.
-Some parts are updated to reflect recent changes.
+_This is an English translation of [Matz's blog post][matz blog post]
+written in Japanese._
+_Some parts are updated to reflect recent changes._
+[matz blog post]: http://www.rubyist.net/~matz/20130731.html
 
 When you are extending mruby using C language, you may encounter
 mysterious "arena overflow error" or memory leak or very slow
@@ -10,7 +12,7 @@ implementing "conservative GC".
 
 GC (garbage collector) must ensure that object is "alive", in other
 words, that it is referenced by somewhere from program.  This can be
-determined by checking that that object can be directly or indirectly
+determined by checking if the object can be directly or indirectly
 referenced by root.  The local variables, global variables and
 constants etc are root.
 
@@ -22,7 +24,7 @@ by C variable is also "alive", but mruby GC cannot aware of this, so
 it might mistakenly recognize the objects referenced by only C
 variables as dead.
 
-It is a fatal bug for GC to collect live objects.
+This can be a fatal bug if the GC tries to collect a live object.
 
 In CRuby, we scan C stack area, and use C variable as root to check
 whether object is alive or not.  Of course, because we are accessing C
@@ -32,60 +34,64 @@ pointer, then assume it as a pointer.  We call it "conservative".
 
 By the way, CRuby's "conservative GC" has some problems.
 
-Its biggest problem is we have no way to access to the stack area in
+The biggest problem is we have no way to access to the stack area in
 portable way.  Therefore, we cannot use this method if we'd like to
 implement highly portable runtime, like mruby.
 
-So we came up an another plan to implement "conservative GC" in mruby.
+So we came up with an another plan to implement "conservative GC" in mruby.
 
-Again, the problem is that there is an object which was created in C
-function, and is not referenced by Ruby world, and cannot be treated
-as garbage.
+Again, the problem is when an object which was created in C function, becomes
+no longer referenced in the Ruby world, and cannot be treated as garbage.
 
 In mruby, we recognize all objects created in C function are alive.
-Then we have no problem such as recognizing live object as dead.
+Then we have no problem such as confusing a live object as dead.
 
 This means that because we cannot collect truly dead object, we may
-get a little bit less efficiency, but GC itself is highly portable.
+lose efficiency, but as a trade-off the GC itself is highly portable.
 We can say goodbye to the problem that GC deletes live objects due to
 optimization which sometimes occurs in CRuby.
 
 According to this idea, we have a table, called "GC arena", which
-remembers objects created in C function.  The arena is stack
-structure, when C function execution is returned to mruby VM, all
-objects registered in the arena are popped.
+remembers objects created in C function.
 
-This works very well, but GC arena causes another problem.  "arena
-overflow error" or memory leak.
+The arena is stack structure, when C function execution is returned to mruby
+VM, all objects registered in the arena are popped.
+
+This works very well, but can cause another problem: "arena overflow error" or
+memory leak.
 
 As of this writing, mruby automatically extend arena to remember
 objects (See `MRB_GC_FIXED_ARENA` and `MRB_GC_ARENA_SIZE` in
-doc/mrbconf/README.md).  If you keep creating objects in C functions,
-it increases memory usage, since GC never kick in.  This memory usage
-may look like memory leak, and also makes execution slower.
+doc/guides/mrbconf.md).
+
+If you create many objects in C functions, memory usage will increase, since
+GC never kick in.  This memory usage may look like memory leak, but will also
+make execution slower as more memory will need to be allocated.
 
 With the build time configuration, you can limit the maximum size of
 arena (e.g., 100).  Then if you create many objects, arena overflows,
-thus you will get "arena overflow error".
+thus you will get an "arena overflow error".
 
 To workaround these problems, we have `mrb_gc_arena_save()` and
 `mrb_gc_arena_restore()` functions.
 
 `int mrb_gc_arena_save(mrb)` returns the current position of the stack
 top of GC arena, and `void mrb_gc_arena_restore(mrb, idx)` sets the
-stack top position to back to given idx.  We uses them like so:
+stack top position to back to given `idx`.
+
+We can use them like this:
 
 ```c
 int arena_idx = mrb_gc_arena_save(mrb);
 
-...create objects...
+// ...create objects...
 mrb_gc_arena_restore(mrb, arena_idx);
 
 ```
 
-In mruby, C function call are surrounded by this save/restore, but we
+In mruby, C function calls are surrounded by this save/restore, but we
 can further optimize memory usage by surrounding save/restore, and can
-avoid arena overflow.
+avoid creating arena overflow bugs.
 
 Let's take a real example.  Here is the source code of `Array#inspect`:
 
@@ -134,38 +140,38 @@ inspect_ary(mrb_state *mrb, mrb_value ary, mrb_value list)
 }
 ```
 
-This is a real example, so a little bit complicated, so bear with me.
+This is a real example, so a little bit complicated, but bear with me.
 The essence of `Array#inspect` is that after stringifying each element
 of array using `inspect` method, we join them together so that we can
-get `inspect` representation of entire array.
+get `inspect` representation of the entire array.
 
-After the `inspect` representation of entire array is created, we no
-longer require the individual string representation.  That means that
-we don't have to register these temporal objects into GC arena.
+After the `inspect` representation is created, we no longer require the
+individual string representation.  This means that we don't have to register
+these temporal objects into GC arena.
 
-Therefore, in `ary_inspect()` function, we do:
+Therefore, in order to keep the arena size small; the `ary_inspect()` function
+will do the following:
 
 * save the position of the stack top using `mrb_gc_arena_save()`.
 * get `inspect` representation of each element.
 * append it to the constructing entire `inspect` representation of array.
 * restore stack top position using `mrb_gc_arena_restore()`.
 
-to keep the arena size small.
-
 Please note that the final `inspect` representation of entire array
 was created before the call of `mrb_gc_arena_restore()`.  Otherwise,
 required temporal object may be deleted by GC.
 
-We may have a usecase that after creating many temporal objects, we'd
+We may have a usecase where after creating many temporal objects, we'd
 like to keep some of them.  In this case, we cannot use the same idea
-in `ary_inspect()` like appending objects to existing one.  Instead,
-after `mrb_gc_arena_restore()`, we register back the objects we'd like
-to keep to the arena using `mrb_gc_protect(mrb, obj)`.  Use
-`mrb_gc_protect()` with caution because its usage could lead to arena
-overflow error.
-
-We also have to mention that when `mrb_funcall` is called in top
-level, its return value is also registered to GC arena, so calling
-them repeatedly eventually lead to arena overflow error.  Use
-`mrb_gc_arena_save()` and `mrb_gc_arena_restore()` or possible use of
+in `ary_inspect()` like appending objects to existing one.
+Instead, after `mrb_gc_arena_restore()`, we must re-register the objects we
+want to keep in the arena using `mrb_gc_protect(mrb, obj)`.
+Use `mrb_gc_protect()` with caution because it could also lead to an "arena
+overflow error".
+
+We must also mention that when `mrb_funcall` is called in top level, the return
+value is also registered to GC arena, so repeated use of `mrb_funcall` may
+eventually lead to an "arena overflow error".
+
+Use `mrb_gc_arena_save()` and `mrb_gc_arena_restore()` or possible use of
 `mrb_gc_protect()` to workaround this.
index fabd720..f957f8c 100644 (file)
@@ -122,20 +122,9 @@ largest value of required alignment.
 * If defined `Float` will be a mruby object with `RBasic`.
 
 ## Instance variable configuration.
-`MRB_USE_IV_SEGLIST`
-* If defined enable segmented list in instance variable table instead of khash.
-* Segmented list is a linked list of key and value segments.
-* It will linear search instead of hash search.
-
-`MRB_SEGMENT_SIZE`
+`MRB_IV_SEGMENT_SIZE`
 * Default value is `4`.
 * Specifies size of each segment in segment list.
-* Ignored when `MRB_USE_IV_SEGLIST` isn't defined.
-
-`MRB_IVHASH_INIT_SIZE`
-* Default value is `8`.
-* Specifies initial size for instance variable table.
-* Ignored when `MRB_USE_IV_SEGLIST` is defined.
 
 ## Other configuration.
 `MRB_UTF8_STRING`
index f75231f..258f405 100644 (file)
@@ -153,6 +153,9 @@ MRuby::Gem::Specification.new('c_and_ruby_extension_example') do |spec|
 
   # Use latest mruby-onig-regexp from github. (version requirements can be omitted)
   spec.add_dependency('mruby-onig-regexp', :github => 'mattn/mruby-onig-regexp')
+
+  # You can add extra mgems active only on test
+  spec.add_test_dependency('mruby-process', :github => 'iij/mruby-process')
 end
 ```
 
diff --git a/third-party/mruby/doc/limitations.md b/third-party/mruby/doc/limitations.md
new file mode 100644 (file)
index 0000000..db8db9a
--- /dev/null
@@ -0,0 +1,208 @@
+# Limitations and Differences
+
+The philosophy of mruby is to be a lightweight implementation of
+the Ruby ISO standard. These two objectives are partially contradicting.
+Ruby is an expressive language with complex implementation details which
+are difficult to implement in a lightweight manner. To cope with this,
+limitations to the "Ruby Compatibility" are defined.
+
+This document is collecting these limitations.
+
+## Integrity
+
+This document does not contain a complete list of limitations.
+Please help to improve it by submitting your findings.
+
+
+## ```1/2``` gives ```0.5```
+
+Since mruby does not have ```Bignum```, bigger integers are represented
+by ```Float``` numbers. To enhance interoperability between ```Fixnum```
+and ```Float```, mruby provides ```Float#upto``` and other iterating
+methods for the ```Float``` class.  As a side effect, ```1/2``` gives ```0.5```
+not ```0```.
+
+## ```Array``` passed to ```puts```
+
+Passing an Array to ```puts``` results in different output.
+
+```ruby
+puts [1,2,3]
+```
+
+#### Ruby [ruby 2.0.0p645 (2015-04-13 revision 50299)]
+
+```
+1
+2
+3
+```
+
+#### mruby [1.3.0 (2017-7-4)]
+
+```
+[1, 2, 3]
+```
+
+## ```Kernel.raise``` in rescue clause
+
+```Kernel.raise``` without arguments does not raise the current exception within
+a rescue clause.
+
+```ruby
+begin
+  1 / 0
+rescue
+  raise
+end
+```
+
+#### Ruby [ruby 2.0.0p645 (2015-04-13 revision 50299)]
+
+```ZeroDivisionError``` is raised.
+
+#### mruby [1.3.0 (2017-7-4)]
+
+No exception is raised.
+
+## Check of infinite recursion
+
+mruby does not check infinite recursion across C extensions.
+
+```ruby
+def test; eval 'test'; end; test
+```
+
+#### Ruby [ruby 2.0.0p645 (2015-04-13 revision 50299)]
+
+```SystemStackError``` is raised.
+
+#### mruby [1.3.0 (2017-7-4)]
+
+Segmentation fault.
+
+## Fiber execution can't cross C function boundary
+
+mruby's ```Fiber``` is implemented in a similar way to Lua's co-routine. This
+results in the consequence that you can't switch context within C functions.
+Only exception is ```mrb_fiber_yield``` at return.
+
+## ```Array``` does not support instance variables
+
+To reduce memory consumption ```Array``` does not support instance variables.
+
+```ruby
+class Liste < Array
+  def initialize(str = nil)
+    @feld = str
+  end
+end
+
+p Liste.new "foobar"
+```
+
+#### Ruby [ruby 2.0.0p645 (2015-04-13 revision 50299)]
+
+``` [] ```
+
+#### mruby [1.3.0 (2017-7-4)]
+
+```ArgumentError``` is raised.
+
+## Method visibility
+
+For simplicity reasons no method visibility (public/private/protected) is
+supported.
+
+```ruby
+class VisibleTest
+
+  def public_method; end
+
+  private
+  def private_method; end
+
+end
+
+p VisibleTest.new.respond_to?(:private_method, false)
+p VisibleTest.new.respond_to?(:private_method, true)
+```
+
+#### Ruby [ruby 2.0.0p645 (2015-04-13 revision 50299)]
+
+```
+false
+true
+```
+
+#### mruby [1.3.0 (2017-7-4)]
+
+```
+true
+true
+```
+
+## defined?
+
+The ```defined?``` keyword is considered too complex to be fully
+implemented. It is recommended to use ```const_defined?``` and
+other reflection methods instead.
+
+```ruby
+defined?(Foo)
+```
+
+#### Ruby [ruby 2.0.0p645 (2015-04-13 revision 50299)]
+
+```
+nil
+```
+
+#### mruby [1.3.0 (2017-7-4)]
+
+```NameError``` is raised.
+
+## ```alias``` on global variables
+
+Aliasing a global variable works in CRuby but is not part
+of the ISO standard.
+
+```ruby
+alias $a $__a__
+```
+
+#### Ruby [ruby 2.0.0p645 (2015-04-13 revision 50299)]
+
+``` nil ```
+
+#### mruby [1.3.0 (2017-7-4)]
+
+Syntax error
+
+## Operator modification
+
+An operator can't be overwritten by the user.
+
+```ruby
+class String
+  def +
+  end
+end
+
+'a' + 'b'
+```
+
+#### Ruby [ruby 2.0.0p645 (2015-04-13 revision 50299)]
+
+```ArgumentError``` is raised.
+The re-defined ```+``` operator does not accept any arguments.
+
+#### mruby [1.3.0 (2017-7-4)]
+
+``` 'ab' ```
+Behavior of the operator wasn't changed.
+
+## ```Kernel.binding``` missing
+
+```Kernel.binding``` is not implemented as it is not in the
+ISO standard.
index 57cf1a6..6e5a5b7 100644 (file)
@@ -11,6 +11,8 @@ MRuby::Gem::Specification.new('ruby_extension_example') do |spec|
   # Add libraries
   # spec.linker.libraries << 'external_lib'
 
+  spec.add_dependency('mruby-print', :core => 'mruby-print')
+
   # Default build files
   # spec.rbfiles = Dir.glob("#{dir}/mrblib/*.rb")
   # spec.objs = Dir.glob("#{dir}/src/*.{c,cpp,m,asm,S}").map { |f| objfile(f.relative_path_from(dir).pathmap("#{build_dir}/%X")) }
index 740dcfa..527aaa4 100644 (file)
@@ -46,7 +46,6 @@ MRuby::CrossBuild.new("ArduinoDue") do |conf|
 
     #configuration for low memory environment
     cc.defines << %w(MRB_HEAP_PAGE_SIZE=64)
-    cc.defines << %w(MRB_USE_IV_SEGLIST)
     cc.defines << %w(KHASH_DEFAULT_SIZE=8)
     cc.defines << %w(MRB_STR_BUF_MIN_SIZE=20)
     cc.defines << %w(MRB_GC_STRESS)
diff --git a/third-party/mruby/examples/targets/build_config_IntelEdison.rb b/third-party/mruby/examples/targets/build_config_IntelEdison.rb
new file mode 100644 (file)
index 0000000..8fa3aa0
--- /dev/null
@@ -0,0 +1,69 @@
+# Cross-compiling setup for Intel Edison (poky linux) platform
+# Get SDK from here: https://software.intel.com/en-us/iot/hardware/edison/downloads
+# REMEMBER to check and update the SDK root in the constant POKY_EDISON_PATH
+
+MRuby::Build.new do |conf|
+  toolchain :gcc
+  conf.gembox 'default'
+  conf.cc.defines = %w(ENABLE_READLINE)
+  conf.gembox 'default'
+
+  #lightweight regular expression
+  conf.gem :github => "pbosetti/mruby-hs-regexp", :branch => "master"
+
+end
+
+# Define cross build settings
+MRuby::CrossBuild.new('core2-32-poky-linux') do |conf|
+  toolchain :gcc
+
+  # Mac OS X
+  #
+  POKY_EDISON_PATH = '/opt/poky-edison/1.7.2'
+
+  POKY_EDISON_SYSROOT =  "#{POKY_EDISON_PATH}/sysroots/core2-32-poky-linux"
+  POKY_EDISON_X86_PATH = "#{POKY_EDISON_PATH}/sysroots/i386-pokysdk-darwin"
+  POKY_EDISON_BIN_PATH = "#{POKY_EDISON_X86_PATH}/usr/bin/i586-poky-linux"
+
+
+  conf.cc do |cc|
+    cc.command = "#{POKY_EDISON_BIN_PATH}/i586-poky-linux-gcc"
+    cc.include_paths << ["#{POKY_EDISON_SYSROOT}/usr/include", "#{POKY_EDISON_X86_PATH}/usr/include"]
+    cc.flags = %w(-m32 -march=core2 -mtune=core2 -msse3 -mfpmath=sse -mstackrealign -fno-omit-frame-pointer)
+    cc.flags << %w(-O2 -pipe -g -feliminate-unused-debug-types)
+    cc.flags << "--sysroot=#{POKY_EDISON_SYSROOT}"
+    cc.compile_options = "%{flags} -o %{outfile} -c %{infile}"
+    cc.defines = %w(ENABLE_READLINE)
+  end
+
+  conf.cxx do |cxx|
+    cxx.command = "#{POKY_EDISON_BIN_PATH}/i586-poky-linux-g++"
+    cxx.include_paths = conf.cc.include_paths.dup
+    cxx.include_paths << ["#{POKY_EDISON_SYSROOT}/usr/include/c++/4.9.1"]
+    cxx.flags = conf.cc.flags.dup
+    cxx.defines = conf.cc.defines.dup
+    cxx.compile_options = conf.cc.compile_options.dup
+  end
+
+  conf.archiver do |archiver|
+    archiver.command = "#{POKY_EDISON_BIN_PATH}/i586-poky-linux-ar"
+    archiver.archive_options = 'rcs %{outfile} %{objs}'
+  end
+
+  conf.linker do |linker|
+    linker.command = "#{POKY_EDISON_BIN_PATH}/i586-poky-linux-g++"
+    linker.flags = %w(-m32 -march=i586)
+    linker.flags << "--sysroot=#{POKY_EDISON_SYSROOT}"
+    linker.flags << %w(-O1)
+    linker.libraries = %w(m pthread)
+  end
+
+  #do not build executable test
+  conf.build_mrbtest_lib_only
+
+  conf.gembox 'default'
+
+  #lightweight regular expression
+  conf.gem :github => "pbosetti/mruby-hs-regexp", :branch => "master"
+
+end
diff --git a/third-party/mruby/examples/targets/build_config_RX630.rb b/third-party/mruby/examples/targets/build_config_RX630.rb
new file mode 100644 (file)
index 0000000..fd17eae
--- /dev/null
@@ -0,0 +1,81 @@
+MRuby::Build.new do |conf|
+
+  # Gets set by the VS command prompts.
+  if ENV['VisualStudioVersion'] || ENV['VSINSTALLDIR']
+    toolchain :visualcpp
+  else
+    toolchain :gcc
+  end
+
+  enable_debug
+
+  # include the default GEMs
+  conf.gembox 'default'
+
+end
+
+# Cross Compiling configuration for RX630
+# http://gadget.renesas.com/
+#
+# Requires gnurx_v14.03
+MRuby::CrossBuild.new("RX630") do |conf|
+  toolchain :gcc
+
+  # Linux
+  BIN_PATH = "/usr/share/gnurx_v14.03_elf-1/bin"
+
+  conf.cc do |cc|
+    cc.command = "#{BIN_PATH}/rx-elf-gcc"
+    cc.flags = "-Wall -g -O2 -flto -mcpu=rx600 -m64bit-doubles"
+    cc.compile_options = "%{flags} -o %{outfile} -c %{infile}"
+
+    #configuration for low memory environment
+    cc.defines << %w(MRB_USE_FLOAT)
+    cc.defines << %w(MRB_HEAP_PAGE_SIZE=64)
+    cc.defines << %w(KHASH_DEFAULT_SIZE=8)
+    cc.defines << %w(MRB_STR_BUF_MIN_SIZE=20)
+    cc.defines << %w(MRB_GC_STRESS)
+    cc.defines << %w(MRB_DISABLE_STDIO) #if you dont need stdio.
+    #cc.defines << %w(POOL_PAGE_SIZE=1000) #effective only for use with mruby-eval
+  end
+
+  conf.cxx do |cxx|
+    cxx.command = conf.cc.command.dup
+    cxx.include_paths = conf.cc.include_paths.dup
+    cxx.flags = conf.cc.flags.dup
+    cxx.defines = conf.cc.defines.dup
+    cxx.compile_options = conf.cc.compile_options.dup
+  end
+
+  conf.linker do |linker|
+    linker.command="#{BIN_PATH}/rx-elf-ld"
+  end
+
+  conf.archiver do |archiver|
+    archiver.command = "#{BIN_PATH}/rx-elf-ar"
+    archiver.archive_options = 'rcs %{outfile} %{objs}'
+  end
+
+  #no executables
+  conf.bins = []
+
+  #do not build executable test
+  conf.build_mrbtest_lib_only
+
+  #disable C++ exception
+  conf.disable_cxx_exception
+
+  #gems from core
+  conf.gem :core => "mruby-sprintf"
+  conf.gem :core => "mruby-print"
+  conf.gem :core => "mruby-math"
+  conf.gem :core => "mruby-enum-ext"
+  conf.gem :core => "mruby-numeric-ext"
+
+  #light-weight regular expression
+  #conf.gem :github => "masamitsu-murase/mruby-hs-regexp", :branch => "master"
+
+  #Arduino API
+  #conf.gem :github =>"kyab/mruby-arduino", :branch => "master"
+
+end
diff --git a/third-party/mruby/examples/targets/build_config_android_arm64-v8a.rb b/third-party/mruby/examples/targets/build_config_android_arm64-v8a.rb
new file mode 100644 (file)
index 0000000..6188c13
--- /dev/null
@@ -0,0 +1,26 @@
+MRuby::Build.new do |conf|
+
+  # Gets set by the VS command prompts.
+  if ENV['VisualStudioVersion'] || ENV['VSINSTALLDIR']
+    toolchain :visualcpp
+  else
+    toolchain :gcc
+  end
+
+  enable_debug
+
+  # include the default GEMs
+  conf.gembox 'default'
+end
+
+# Requires Android NDK r13 or later.
+MRuby::CrossBuild.new('android-arm64-v8a') do |conf|
+  params = { 
+    :arch => 'arm64-v8a', 
+    :platform => 'android-24',
+    :toolchain => :clang,
+  }
+  toolchain :android, params
+
+  conf.gembox 'default'
+end
diff --git a/third-party/mruby/examples/targets/build_config_android_armeabi.rb b/third-party/mruby/examples/targets/build_config_android_armeabi.rb
new file mode 100644 (file)
index 0000000..b7eb33a
--- /dev/null
@@ -0,0 +1,26 @@
+MRuby::Build.new do |conf|
+
+  # Gets set by the VS command prompts.
+  if ENV['VisualStudioVersion'] || ENV['VSINSTALLDIR']
+    toolchain :visualcpp
+  else
+    toolchain :gcc
+  end
+
+  enable_debug
+
+  # include the default GEMs
+  conf.gembox 'default'
+end
+
+# Requires Android NDK r13 or later.
+MRuby::CrossBuild.new('android-armeabi') do |conf|
+  params = { 
+    :arch => 'armeabi', 
+    :platform => 'android-24',
+    :toolchain => :clang,
+  }
+  toolchain :android, params
+
+  conf.gembox 'default'
+end
diff --git a/third-party/mruby/examples/targets/build_config_android_armeabi_v7a_neon_hard.rb b/third-party/mruby/examples/targets/build_config_android_armeabi_v7a_neon_hard.rb
new file mode 100644 (file)
index 0000000..3788bba
--- /dev/null
@@ -0,0 +1,28 @@
+MRuby::Build.new do |conf|
+
+  # Gets set by the VS command prompts.
+  if ENV['VisualStudioVersion'] || ENV['VSINSTALLDIR']
+    toolchain :visualcpp
+  else
+    toolchain :gcc
+  end
+
+  enable_debug
+
+  # include the default GEMs
+  conf.gembox 'default'
+end
+
+# Requires Android NDK r13 or later.
+MRuby::CrossBuild.new('android-armeabi-v7a-neon-hard') do |conf|
+  params = {
+    :arch => 'armeabi-v7a',
+    :mfpu => 'neon',
+    :mfloat_abi => 'hard',
+    :platform => 'android-24',
+    :toolchain => :clang,
+  }
+  toolchain :android, params
+
+  conf.gembox 'default'
+end
index c3a146e..951f714 100644 (file)
@@ -43,7 +43,6 @@ MRuby::CrossBuild.new("chipKITMax32") do |conf|
 
     #configuration for low memory environment
     cc.defines << %w(MRB_HEAP_PAGE_SIZE=64)
-    cc.defines << %w(MRB_USE_IV_SEGLIST)
     cc.defines << %w(KHASH_DEFAULT_SIZE=8)
     cc.defines << %w(MRB_STR_BUF_MIN_SIZE=20)
     cc.defines << %w(MRB_GC_STRESS)
index ab5dd1a..b246b97 100644 (file)
@@ -7,6 +7,23 @@
 #ifndef MRUBYCONF_H
 #define MRUBYCONF_H
 
+#include <limits.h>
+#include <stdint.h>
+
+/* architecture selection: */
+/* specify -DMRB_32BIT or -DMRB_64BIT to override */
+#if !defined(MRB_32BIT) && !defined(MRB_64BIT)
+#if UINT64_MAX == SIZE_MAX
+#define MRB_64BIT
+#else
+#define MRB_32BIT
+#endif
+#endif
+
+#if defined(MRB_32BIT) && defined(MRB_64BIT)
+#error Cannot build for 32 and 64 bit architecture at the same time
+#endif
+
 /* configuration options: */
 /* add -DMRB_USE_FLOAT to use float instead of double for floating point numbers */
 //#define MRB_USE_FLOAT
 /* number of object per heap page */
 //#define MRB_HEAP_PAGE_SIZE 1024
 
-/* use segmented list for IV table */
-//#define MRB_USE_IV_SEGLIST
-
-/* initial size for IV khash; ignored when MRB_USE_IV_SEGLIST is set */
-//#define MRB_IVHASH_INIT_SIZE 8
-
 /* if _etext and _edata available, mruby can reduce memory used by symbols */
 //#define MRB_USE_ETEXT_EDATA
 
index 49de55f..18f54fd 100644 (file)
@@ -1,7 +1,7 @@
 /*
 ** mruby - An embeddable Ruby implementation
 **
-** Copyright (c) mruby developers 2010-2015
+** Copyright (c) mruby developers 2010-2017
 **
 ** Permission is hereby granted, free of charge, to any person obtaining
 ** a copy of this software and associated documentation files (the
 #ifndef MRUBY_H
 #define MRUBY_H
 
+#ifdef __cplusplus
+#define __STDC_LIMIT_MACROS
+#define __STDC_CONSTANT_MACROS
+#define __STDC_FORMAT_MACROS
+#endif
+
 #include <stdint.h>
 #include <stddef.h>
 #include <limits.h>
 
+#ifdef __cplusplus
+#ifndef SIZE_MAX
+#ifdef __SIZE_MAX__
+#define SIZE_MAX __SIZE_MAX__
+#else
+#define SIZE_MAX std::numeric_limits<size_t>::max()
+#endif
+#endif
+#endif
+
+#ifdef MRB_DEBUG
+#include <assert.h>
+#define mrb_assert(p) assert(p)
+#define mrb_assert_int_fit(t1,n,t2,max) assert((n)>=0 && ((sizeof(n)<=sizeof(t2))||(n<=(t1)(max))))
+#else
+#define mrb_assert(p) ((void)0)
+#define mrb_assert_int_fit(t1,n,t2,max) ((void)0)
+#endif
+
+#if __STDC_VERSION__ >= 201112L
+#define mrb_static_assert(exp, str) _Static_assert(exp, str)
+#else
+#define mrb_static_assert(exp, str) mrb_assert(exp)
+#endif
+
 #include "mrbconf.h"
+
+#ifndef DBL_EPSILON
+#define DBL_EPSILON ((double)2.22044604925031308085e-16L)
+#endif
+#ifndef LDBL_EPSILON
+#define LDBL_EPSILON (1.08420217248550443401e-19L)
+#endif
+
+#ifdef MRB_USE_FLOAT
+#define MRB_FLOAT_EPSILON FLT_EPSILON
+#else
+#define MRB_FLOAT_EPSILON DBL_EPSILON
+#endif
+
 #include "mruby/common.h"
-#include "mruby/value.h"
-#include "mruby/gc.h"
-#include "mruby/version.h"
+#include <mruby/value.h>
+#include <mruby/gc.h>
+#include <mruby/version.h>
 
 /**
  * MRuby C API entry point
@@ -75,7 +120,7 @@ typedef struct {
   mrb_value *stackent;
   int nregs;
   int ridx;
-  int eidx;
+  int epos;
   struct REnv *env;
   mrb_code *pc;                 /* return address */
   mrb_code *err;                /* error position */
@@ -87,7 +132,7 @@ typedef struct {
 enum mrb_fiber_state {
   MRB_FIBER_CREATED = 0,
   MRB_FIBER_RUNNING,
-  MRB_FIBER_RESUMING,
+  MRB_FIBER_RESUMED,
   MRB_FIBER_SUSPENDED,
   MRB_FIBER_TRANSFERRED,
   MRB_FIBER_TERMINATED,
@@ -105,9 +150,10 @@ struct mrb_context {
   mrb_code **rescue;                      /* exception handler stack */
   int rsize;
   struct RProc **ensure;                  /* ensure handler stack */
-  int esize;
+  int esize, eidx;
 
   enum mrb_fiber_state status;
+  mrb_bool vmexec;
   struct RFiber *fib;
 };
 
@@ -115,17 +161,21 @@ struct mrb_jmpbuf;
 
 typedef void (*mrb_atexit_func)(struct mrb_state*);
 
+#define MRB_STATE_NO_REGEXP 1
+#define MRB_STATE_REGEXP    2
+
 typedef struct mrb_state {
   struct mrb_jmpbuf *jmp;
 
+  uint32_t flags;
   mrb_allocf allocf;                      /* memory allocation function */
   void *allocf_ud;                        /* auxiliary data of allocf */
 
   struct mrb_context *c;
   struct mrb_context *root_c;
+  struct iv_tbl *globals;                 /* global variable table */
 
   struct RObject *exc;                    /* exception */
-  struct iv_tbl *globals;                 /* global variable table */
 
   struct RObject *top_self;
   struct RClass *object_class;            /* Object class */
@@ -157,9 +207,17 @@ typedef struct mrb_state {
   void (*debug_op_hook)(struct mrb_state* mrb, struct mrb_irep *irep, mrb_code *pc, mrb_value *regs);
 #endif
 
+#ifdef MRB_BYTECODE_DECODE_OPTION
+  mrb_code (*bytecode_decoder)(struct mrb_state* mrb, mrb_code code);
+#endif
+
   struct RClass *eException_class;
   struct RClass *eStandardError_class;
   struct RObject *nomem_err;              /* pre-allocated NoMemoryError */
+  struct RObject *stack_err;              /* pre-allocated SysStackError */
+#ifdef MRB_GC_FIXED_ARENA
+  struct RObject *arena_err;              /* pre-allocated arena overfow error */
+#endif
 
   void *ud; /* auxiliary data */
 
@@ -181,12 +239,12 @@ typedef mrb_value (*mrb_func_t)(mrb_state *mrb, mrb_value);
  *
  *      !!!c
  *      void mrb_example_gem_init(mrb_state* mrb) {
- *              struct RClass *example_class;
- *              example_class = mrb_define_class(mrb, "Example_Class", mrb->object_class);
+ *          struct RClass *example_class;
+ *          example_class = mrb_define_class(mrb, "Example_Class", mrb->object_class);
  *      }
  *
  *      void mrb_example_gem_final(mrb_state* mrb) {
- *              //free(TheAnimals);
+ *          //free(TheAnimals);
  *      }
  *
  * @param [mrb_state *] mrb The current mruby state.
@@ -199,6 +257,7 @@ MRB_API struct RClass *mrb_define_class(mrb_state *mrb, const char *name, struct
 
 /**
  * Defines a new module.
+ *
  * @param [mrb_state *] mrb_state* The current mruby state.
  * @param [const char *] char* The name of the module.
  * @return [struct RClass *] Reference to the newly defined module.
@@ -208,11 +267,11 @@ MRB_API mrb_value mrb_singleton_class(mrb_state*, mrb_value);
 
 /**
  * Include a module in another class or module.
- * Equivalent to: 
+ * Equivalent to:
  *
- *   module B                                                                                                         
- *     include A                                                                                                      
- *   end 
+ *   module B
+ *     include A
+ *   end
  * @param [mrb_state *] mrb_state* The current mruby state.
  * @param [struct RClass *] RClass* A reference to module or a class.
  * @param [struct RClass *] RClass* A reference to the module to be included.
@@ -229,7 +288,7 @@ MRB_API void mrb_include_module(mrb_state*, struct RClass*, struct RClass*);
  * @param [mrb_state *] mrb_state* The current mruby state.
  * @param [struct RClass *] RClass* A reference to module or a class.
  * @param [struct RClass *] RClass* A reference to the module to be prepended.
- */ 
+ */
 MRB_API void mrb_prepend_module(mrb_state*, struct RClass*, struct RClass*);
 
 /**
@@ -263,29 +322,22 @@ MRB_API void mrb_define_method(mrb_state *mrb, struct RClass *cla, const char *n
  * Defines a class method.
  *
  * Example:
- *   # Ruby style
- *   class Foo
  *
- *     def Foo.bar
+ *     # Ruby style
+ *     class Foo
+ *       def Foo.bar
+ *       end
  *     end
- *
- *   end
- *   // C style
- *   mrb_value bar_method(mrb_state* mrb, mrb_value self){
- *
- *     return mrb_nil_value();
- *
- *   }
- *   void mrb_example_gem_init(mrb_state* mrb){
- *
- *     struct RClass *foo;
- *
- *     foo = mrb_define_class(mrb, "Foo", mrb->object_class);
- *
- *     mrb_define_class_method(mrb, foo, "bar", bar_method, MRB_ARGS_NONE());
- * 
- *   }
- * @param [mrb_state *] mrb_state* The MRuby state reference. 
+ *     // C style
+ *     mrb_value bar_method(mrb_state* mrb, mrb_value self){
+ *       return mrb_nil_value();
+ *     }
+ *     void mrb_example_gem_init(mrb_state* mrb){
+ *       struct RClass *foo;
+ *       foo = mrb_define_class(mrb, "Foo", mrb->object_class);
+ *       mrb_define_class_method(mrb, foo, "bar", bar_method, MRB_ARGS_NONE());
+ *     }
+ * @param [mrb_state *] mrb_state* The MRuby state reference.
  * @param [struct RClass *] RClass* The class where the class method will be defined.
  * @param [const char *] char* The name of the class method being defined.
  * @param [mrb_func_t] mrb_func_t The function pointer to the class method definition.
@@ -298,30 +350,25 @@ MRB_API void mrb_define_singleton_method(mrb_state*, struct RObject*, const char
  *  Defines a module fuction.
  *
  * Example:
- *   # Ruby style
- *   module Foo
- *                                                                                                     
- *     def Foo.bar                                                                                                    *     end
- *
- *   end                                                                                                             
- *   // C style                                                                                                      
- *   mrb_value bar_method(mrb_state* mrb, mrb_value self){ 
- *                                                          
- *     return mrb_nil_value();                                                                                        *               
- *   }                                                                                                               
- *   void mrb_example_gem_init(mrb_state* mrb){
- *                                                                      
- *     struct RClass *foo;
- *                                                                                           
- *     foo = mrb_define_module(mrb, "Foo");
- *                                                        
- *     mrb_define_module_function(mrb, foo, "bar", bar_method, MRB_ARGS_NONE());
- *
- *   }    
+ *
+ *        # Ruby style
+ *        module Foo
+ *          def Foo.bar
+ *          end
+ *        end
+ *        // C style
+ *        mrb_value bar_method(mrb_state* mrb, mrb_value self){
+ *          return mrb_nil_value();
+ *        }
+ *        void mrb_example_gem_init(mrb_state* mrb){
+ *          struct RClass *foo;
+ *          foo = mrb_define_module(mrb, "Foo");
+ *          mrb_define_module_function(mrb, foo, "bar", bar_method, MRB_ARGS_NONE());
+ *        }
  *  @param [mrb_state *] mrb_state* The MRuby state reference.
  *  @param [struct RClass *] RClass* The module where the module function will be defined.
  *  @param [const char *] char* The name of the module function being defined.
- *  @param [mrb_func_t] mrb_func_t The function pointer to the module function definition. 
+ *  @param [mrb_func_t] mrb_func_t The function pointer to the module function definition.
  *  @param [mrb_aspec] mrb_aspec The method parameters declaration.
  */
 MRB_API void mrb_define_module_function(mrb_state*, struct RClass*, const char*, mrb_func_t, mrb_aspec);
@@ -330,29 +377,23 @@ MRB_API void mrb_define_module_function(mrb_state*, struct RClass*, const char*,
  *  Defines a constant.
  *
  * Example:
- *    # Ruby style
  *
- *    class ExampleClass
- *    
- *    AGE = 22
- *
- *    end
- *
- *    // C style
- *    #include <stdio.h> 
- *    #include <mruby.h>
- *
- *    void
- *    mrb_example_gem_init(mrb_state* mrb){
- * 
- *      mrb_define_const(mrb, mrb->kernel_module, "AGE", mrb_fixnum_value(22));
- *
- *    }
- *
- *    mrb_value
- *    mrb_example_gem_final(mrb_state* mrb){
- *
- *    }
+ *          # Ruby style
+ *          class ExampleClass
+ *            AGE = 22
+ *          end
+ *          // C style
+ *          #include <stdio.h>
+ *          #include <mruby.h>
+ *
+ *          void
+ *          mrb_example_gem_init(mrb_state* mrb){
+ *            mrb_define_const(mrb, mrb->kernel_module, "AGE", mrb_fixnum_value(22));
+ *          }
+ *
+ *          mrb_value
+ *          mrb_example_gem_final(mrb_state* mrb){
+ *          }
  *  @param [mrb_state *] mrb_state* The MRuby state reference.
  *  @param [struct RClass *] RClass* A class or module the constant is defined in.
  *  @param [const char *] name The name of the constant being defined.
@@ -364,59 +405,46 @@ MRB_API void mrb_define_const(mrb_state*, struct RClass*, const char *name, mrb_
  * Undefines a method.
  *
  * Example:
- *   # Ruby style
  *
- *   class ExampleClassA
+ *     # Ruby style
  *
- *     def example_method
- *       "example"
+ *     class ExampleClassA
+ *       def example_method
+ *         "example"
+ *       end
  *     end
+ *     ExampleClassA.new.example_method # => example
  *
- *   end
- *
- *   ExampleClassA.new.example_method # => example
- *
- *   class ExampleClassB < ExampleClassA
- *
- *     undef_method :example_method
- *
- *   end
- *
- *   ExampleClassB.new.example_method # => undefined method 'example_method' for ExampleClassB (NoMethodError)
- *
- *   // C style
- *   #include <stdio.h>
- *   #include <mruby.h>
- *  
- *   mrb_value
- *   mrb_example_method(mrb_state *mrb){
- *
- *     return mrb_str_new_cstr(mrb, "example");
- *
- *   }
- *
- *   void
- *   mrb_example_gem_init(mrb_state* mrb){
- *     struct RClass *example_class_a;
- *     struct RClass *example_class_b;
- *     struct RClass *example_class_c;
- *
- *     example_class_a = mrb_define_class(mrb, "ExampleClassA", mrb->object_class);
- *
- *     mrb_define_method(mrb, example_class_a, "example_method", mrb_example_method, MRB_ARGS_NONE());
- *
- *     example_class_b = mrb_define_class(mrb, "ExampleClassB", example_class_a);
- *
- *     example_class_c = mrb_define_class(mrb, "ExampleClassC", example_class_b);
+ *     class ExampleClassB < ExampleClassA
+ *       undef_method :example_method
+ *     end
  *
- *     mrb_undef_method(mrb, example_class_c, "example_method");
+ *     ExampleClassB.new.example_method # => undefined method 'example_method' for ExampleClassB (NoMethodError)
  *
- *   }
+ *     // C style
+ *     #include <stdio.h>
+ *     #include <mruby.h>
  *
- *   mrb_example_gem_final(mrb_state* mrb){
+ *     mrb_value
+ *     mrb_example_method(mrb_state *mrb){
+ *       return mrb_str_new_lit(mrb, "example");
+ *     }
  *
- *   }
+ *     void
+ *     mrb_example_gem_init(mrb_state* mrb){
+ *       struct RClass *example_class_a;
+ *       struct RClass *example_class_b;
+ *       struct RClass *example_class_c;
+ *
+ *       example_class_a = mrb_define_class(mrb, "ExampleClassA", mrb->object_class);
+ *       mrb_define_method(mrb, example_class_a, "example_method", mrb_example_method, MRB_ARGS_NONE());
+ *       example_class_b = mrb_define_class(mrb, "ExampleClassB", example_class_a);
+ *       example_class_c = mrb_define_class(mrb, "ExampleClassC", example_class_b);
+ *       mrb_undef_method(mrb, example_class_c, "example_method");
+ *     }
  *
+ *     mrb_example_gem_final(mrb_state* mrb){
+ *     }
  * @param [mrb_state*] mrb_state* The mruby state reference.
  * @param [struct RClass*] RClass* A class the method will be undefined from.
  * @param [const char*] constchar* The name of the method to be undefined.
@@ -425,47 +453,37 @@ MRB_API void mrb_undef_method(mrb_state*, struct RClass*, const char*);
 
 /**
  * Undefine a class method.
- *
  * Example:
- *   # Ruby style
- *
- *   class ExampleClass
- *     def self.example_method
- *       "example"
- *     end
  *
- *   end
- *
- *   ExampleClass.example_method
- *   
- *   // C style
- *   #include <stdio.h>
- *   #include <mruby.h> 
- *
- *   mrb_value
- *   mrb_example_method(mrb_state *mrb){
- *
- *     return mrb_str_new_cstr(mrb, "example");
- *     
- *   }
- *
- *   void
- *   mrb_example_gem_init(mrb_state* mrb){
- *
- *     struct RClass *example_class;
- *     
- *     example_class = mrb_define_class(mrb, "ExampleClass", mrb->object_class);
+ *      # Ruby style
+ *      class ExampleClass
+ *        def self.example_method
+ *          "example"
+ *        end
+ *      end
  *
- *     mrb_define_class_method(mrb, example_class, "example_method", mrb_example_method, MRB_ARGS_NONE());
+ *     ExampleClass.example_method
  *
- *     mrb_undef_class_method(mrb, example_class, "example_method");
+ *     // C style
+ *     #include <stdio.h>
+ *     #include <mruby.h>
  *
- *   }
+ *     mrb_value
+ *     mrb_example_method(mrb_state *mrb){
+ *       return mrb_str_new_lit(mrb, "example");
+ *     }
  *
- *   void
- *   mrb_example_gem_final(mrb_state* mrb){
+ *     void
+ *     mrb_example_gem_init(mrb_state* mrb){
+ *       struct RClass *example_class;
+ *       example_class = mrb_define_class(mrb, "ExampleClass", mrb->object_class);
+ *       mrb_define_class_method(mrb, example_class, "example_method", mrb_example_method, MRB_ARGS_NONE());
+ *       mrb_undef_class_method(mrb, example_class, "example_method");
+ *      }
  *
- *   }
+ *      void
+ *      mrb_example_gem_final(mrb_state* mrb){
+ *      }
  * @param [mrb_state*] mrb_state* The mruby state reference.
  * @param [RClass*] RClass* A class the class method will be undefined from.
  * @param [constchar*] constchar* The name of the class method to be undefined.
@@ -477,24 +495,23 @@ MRB_API void mrb_undef_class_method(mrb_state*, struct RClass*, const char*);
  *
  * Example:
  *
- *   # Ruby style
- *   class ExampleClass
- *   end
- *
- *   p ExampleClass # => #<ExampleClass:0x9958588>
- *   // C style
- *   #include <stdio.h>
- *   #include <mruby.h>
- *
- *   void
- *   mrb_example_gem_init(mrb_state* mrb) {
- *     struct RClass *example_class;
- *     mrb_value obj;
+ *     # Ruby style
+ *     class ExampleClass
+ *     end
  *
- *     example_class = mrb_define_class(mrb, "ExampleClass", mrb->object_class); # => class ExampleClass; end
- *     obj = mrb_obj_new(mrb, example_class, 0, NULL); # => ExampleClass.new
- *     mrb_p(mrb, obj); // => Kernel#p
- *   }  
+ *     p ExampleClass # => #<ExampleClass:0x9958588>
+ *     // C style
+ *     #include <stdio.h>
+ *     #include <mruby.h>
+ *
+ *     void
+ *     mrb_example_gem_init(mrb_state* mrb) {
+ *       struct RClass *example_class;
+ *       mrb_value obj;
+ *       example_class = mrb_define_class(mrb, "ExampleClass", mrb->object_class); # => class ExampleClass; end
+ *       obj = mrb_obj_new(mrb, example_class, 0, NULL); # => ExampleClass.new
+ *       mrb_p(mrb, obj); // => Kernel#p
+ *      }
  * @param [mrb_state*] mrb The current mruby state.
  * @param [RClass*] c Reference to the class of the new object.
  * @param [mrb_int] argc Number of arguments in argv
@@ -516,15 +533,15 @@ MRB_API mrb_value mrb_instance_new(mrb_state *mrb, mrb_value cv);
  *
  * Example:
  *
- *   void
- *   mrb_example_gem_init(mrb_state* mrb) {
- *      struct RClass *example_class;
- *      mrb_value obj;
+ *      void
+ *      mrb_example_gem_init(mrb_state* mrb) {
+ *        struct RClass *example_class;
  *
- *      example_class = mrb_class_new(mrb, mrb->object_class);
- *      obj = mrb_obj_new(mrb, example_class, 0, NULL); // => #<#<Class:0x9a945b8>:0x9a94588>
- *      mrb_p(mrb, obj); // => Kernel#p
- *   }
+ *        mrb_value obj;
+ *        example_class = mrb_class_new(mrb, mrb->object_class);
+ *        obj = mrb_obj_new(mrb, example_class, 0, NULL); // => #<#<Class:0x9a945b8>:0x9a94588>
+ *        mrb_p(mrb, obj); // => Kernel#p
+ *       }
  *
  * @param [mrb_state*] mrb The current mruby state.
  * @param [struct RClass *] super The super class or parent.
@@ -536,12 +553,12 @@ MRB_API struct RClass * mrb_class_new(mrb_state *mrb, struct RClass *super);
  * Creates a new module, Module.
  *
  * Example:
- *   void
- *   mrb_example_gem_init(mrb_state* mrb) {
- *      struct RClass *example_module;
+ *      void
+ *      mrb_example_gem_init(mrb_state* mrb) {
+ *        struct RClass *example_module;
  *
- *      example_module = mrb_module_new(mrb);
- *   }
+ *        example_module = mrb_module_new(mrb);
+ *      }
  *
  * @param [mrb_state*] mrb The current mruby state.
  * @return [struct RClass *] Reference to the new module.
@@ -552,23 +569,23 @@ MRB_API struct RClass * mrb_module_new(mrb_state *mrb);
  * Returns an mrb_bool. True if class was defined, and false if the class was not defined.
  *
  * Example:
- *   void
- *   mrb_example_gem_init(mrb_state* mrb) {
- *      struct RClass *example_class;
- *      mrb_bool cd;
- *
- *      example_class = mrb_define_class(mrb, "ExampleClass", mrb->object_class);
- *      cd = mrb_class_defined(mrb, "ExampleClass");
- *      
- *      // If mrb_class_defined returns 1 then puts "True"
- *      // If mrb_class_defined returns 0 then puts "False"
- *      if (cd == 1){
- *        puts("True");
+ *     void
+ *     mrb_example_gem_init(mrb_state* mrb) {
+ *       struct RClass *example_class;
+ *       mrb_bool cd;
+ *
+ *       example_class = mrb_define_class(mrb, "ExampleClass", mrb->object_class);
+ *       cd = mrb_class_defined(mrb, "ExampleClass");
+ *
+ *       // If mrb_class_defined returns 1 then puts "True"
+ *       // If mrb_class_defined returns 0 then puts "False"
+ *       if (cd == 1){
+ *         puts("True");
+ *       }
+ *       else {
+ *         puts("False");
+ *       }
  *      }
- *      else {
- *        puts("False");
- *      }
- *   }
  *
  * @param [mrb_state*] mrb The current mruby state.
  * @param [const char *] name A string representing the name of the class.
@@ -585,6 +602,45 @@ MRB_API mrb_bool mrb_class_defined(mrb_state *mrb, const char *name);
 MRB_API struct RClass * mrb_class_get(mrb_state *mrb, const char *name);
 
 /**
+ * Gets a exception class.
+ * @param [mrb_state*] mrb The current mruby state.
+ * @param [const char *] name The name of the class.
+ * @return [struct RClass *] A reference to the class.
+*/
+MRB_API struct RClass * mrb_exc_get(mrb_state *mrb, const char *name);
+
+/**
+ * Returns an mrb_bool. True if inner class was defined, and false if the inner class was not defined.
+ *
+ * Example:
+ *     void
+ *     mrb_example_gem_init(mrb_state* mrb) {
+ *       struct RClass *example_outer, *example_inner;
+ *       mrb_bool cd;
+ *
+ *       example_outer = mrb_define_module(mrb, "ExampleOuter");
+ *
+ *       example_inner = mrb_define_class_under(mrb, example_outer, "ExampleInner", mrb->object_class);
+ *       cd = mrb_class_defined_under(mrb, example_outer, "ExampleInner");
+ *
+ *       // If mrb_class_defined_under returns 1 then puts "True"
+ *       // If mrb_class_defined_under returns 0 then puts "False"
+ *       if (cd == 1){
+ *         puts("True");
+ *       }
+ *       else {
+ *         puts("False");
+ *       }
+ *      }
+ *
+ * @param [mrb_state*] mrb The current mruby state.
+ * @param [struct RClass *] outer The name of the outer class.
+ * @param [const char *] name A string representing the name of the inner class.
+ * @return [mrb_bool] A boolean value.
+ */
+MRB_API mrb_bool mrb_class_defined_under(mrb_state *mrb, struct RClass *outer, const char *name);
+
+/**
  * Gets a child class.
  * @param [mrb_state*] mrb The current mruby state.
  * @param [struct RClass *] outer The name of the parent class.
@@ -615,7 +671,7 @@ MRB_API mrb_value mrb_notimplement_m(mrb_state*, mrb_value);
  * Duplicate an object.
  *
  * Equivalent to:
- *   Object#dup    
+ *   Object#dup
  * @param [mrb_state*] mrb The current mruby state.
  * @param [mrb_value] obj Object to be duplicate.
  * @return [mrb_value] The newly duplicated object.
@@ -627,36 +683,36 @@ MRB_API mrb_value mrb_check_to_integer(mrb_state *mrb, mrb_value val, const char
  * Returns true if obj responds to the given method. If the method was defined for that
  * class it returns true, it returns false otherwise.
  *
- * Example:
- *   # Ruby style
- *   class ExampleClass
- *      def example_method
+ *      Example:
+ *      # Ruby style
+ *      class ExampleClass
+ *        def example_method
+ *        end
  *      end
- *   end
  *
- *   ExampleClass.new.respond_to?(:example_method) # => true
- *
- *   // C style
- *   void
- *   mrb_example_gem_init(mrb_state* mrb) {
- *      struct RClass *example_class;
- *      mrb_sym mid;
- *      mrb_bool obj_resp;
- *
- *      example_class = mrb_define_class(mrb, "ExampleClass", mrb->object_class);
- *      mrb_define_method(mrb, example_class, "example_method", exampleMethod, MRB_ARGS_NONE());
- *      mid = mrb_intern_str(mrb, mrb_str_new_cstr(mrb, "example_method" ));
- *      obj_resp = mrb_obj_respond_to(mrb, example_class, mid); // => 1(true in Ruby world)
- *      
- *      // If mrb_obj_respond_to returns 1 then puts "True"
- *      // If mrb_obj_respond_to returns 0 then puts "False"
- *      if (obj_resp == 1) {
- *        puts("True");
+ *      ExampleClass.new.respond_to?(:example_method) # => true
+ *
+ *      // C style
+ *      void
+ *      mrb_example_gem_init(mrb_state* mrb) {
+ *        struct RClass *example_class;
+ *        mrb_sym mid;
+ *        mrb_bool obj_resp;
+ *
+ *        example_class = mrb_define_class(mrb, "ExampleClass", mrb->object_class);
+ *        mrb_define_method(mrb, example_class, "example_method", exampleMethod, MRB_ARGS_NONE());
+ *        mid = mrb_intern_str(mrb, mrb_str_new_lit(mrb, "example_method" ));
+ *        obj_resp = mrb_obj_respond_to(mrb, example_class, mid); // => 1(true in Ruby world)
+ *
+ *        // If mrb_obj_respond_to returns 1 then puts "True"
+ *        // If mrb_obj_respond_to returns 0 then puts "False"
+ *        if (obj_resp == 1) {
+ *          puts("True");
+ *        }
+ *        else if (obj_resp == 0) {
+ *          puts("False");
+ *        }
  *      }
- *      else if (obj_resp == 0) {
- *        puts("False");
- *      }
- *   }
  *
  * @param [mrb_state*] mrb The current mruby state.
  * @param [struct RClass *] c A reference to a class.
@@ -794,10 +850,78 @@ mrb_get_argc(mrb_state *mrb) /* get argc */
 
 /**
  * Call existing ruby functions.
+ *
+ *      #include <stdio.h>
+ *      #include <mruby.h>
+ *      #include "mruby/compile.h"
+ *
+ *      int
+ *      main()
+ *      {
+ *        mrb_int i = 99;
+ *        mrb_state *mrb = mrb_open();
+ *
+ *        if (!mrb) { }
+ *        FILE *fp = fopen("test.rb","r");
+ *        mrb_value obj = mrb_load_file(mrb,fp);
+ *        mrb_funcall(mrb, obj, "method_name", 1, mrb_fixnum_value(i));
+ *        fclose(fp);
+ *        mrb_close(mrb);
+ *       }
+ * @param [mrb_state*] mrb_state* The current mruby state.
+ * @param [mrb_value] mrb_value A reference to an mruby value.
+ * @param [const char*] const char* The name of the method.
+ * @param [mrb_int] mrb_int The number of arguments the method has.
+ * @param [...] ... Variadic values(not type safe!).
+ * @return [mrb_value] mrb_value mruby function value.
  */
 MRB_API mrb_value mrb_funcall(mrb_state*, mrb_value, const char*, mrb_int,...);
+/**
+ * Call existing ruby functions. This is basically the type safe version of mrb_funcall.
+ *
+ *      #include <stdio.h>
+ *      #include <mruby.h>
+ *      #include "mruby/compile.h"
+ *      int
+ *      main()
+ *      {
+ *        mrb_int i = 99;
+ *        mrb_state *mrb = mrb_open();
+ *
+ *        if (!mrb) { }
+ *        mrb_sym m_sym = mrb_intern_lit(mrb, "method_name"); // Symbol for method.
+ *
+ *        FILE *fp = fopen("test.rb","r");
+ *        mrb_value obj = mrb_load_file(mrb,fp);
+ *        mrb_funcall_argv(mrb, obj, m_sym, 1, &obj); // Calling ruby function from test.rb.
+ *        fclose(fp);
+ *        mrb_close(mrb);
+ *       }
+ * @param [mrb_state*] mrb_state* The current mruby state.
+ * @param [mrb_value] mrb_value A reference to an mruby value.
+ * @param [mrb_sym] mrb_sym The symbol representing the method.
+ * @param [mrb_int] mrb_int The number of arguments the method has.
+ * @param [const mrb_value*] mrb_value* Pointer to the object.
+ * @return [mrb_value] mrb_value mruby function value.
+ * @see mrb_funcall
+ */
 MRB_API mrb_value mrb_funcall_argv(mrb_state*, mrb_value, mrb_sym, mrb_int, const mrb_value*);
+/**
+ * Call existing ruby functions with a block.
+ */
 MRB_API mrb_value mrb_funcall_with_block(mrb_state*, mrb_value, mrb_sym, mrb_int, const mrb_value*, mrb_value);
+/**
+ * Create a symbol
+ *
+ *     # Ruby style:
+ *     :pizza # => :pizza
+ *
+ *     // C style:
+ *     mrb_sym m_sym = mrb_intern_lit(mrb, "pizza"); //  => :pizza
+ * @param [mrb_state*] mrb_state* The current mruby state.
+ * @param [const char*] const char* The name of the method.
+ * @return [mrb_sym] mrb_sym A symbol.
+ */
 MRB_API mrb_sym mrb_intern_cstr(mrb_state*,const char*);
 MRB_API mrb_sym mrb_intern(mrb_state*,const char*,size_t);
 MRB_API mrb_sym mrb_intern_static(mrb_state*,const char*,size_t);
@@ -891,8 +1015,13 @@ MRB_API void* mrb_default_allocf(mrb_state*, void*, size_t, void*);
 
 MRB_API mrb_value mrb_top_self(mrb_state *);
 MRB_API mrb_value mrb_run(mrb_state*, struct RProc*, mrb_value);
-MRB_API mrb_value mrb_toplevel_run(mrb_state*, struct RProc*);
-MRB_API mrb_value mrb_context_run(mrb_state*, struct RProc*, mrb_value, unsigned int);
+MRB_API mrb_value mrb_top_run(mrb_state*, struct RProc*, mrb_value, unsigned int);
+MRB_API mrb_value mrb_vm_run(mrb_state*, struct RProc*, mrb_value, unsigned int);
+MRB_API mrb_value mrb_vm_exec(mrb_state*, struct RProc*, mrb_code*);
+/* compatibility macros */
+#define mrb_toplevel_run_keep(m,p,k) mrb_top_run((m),(p),mrb_top_self(m),(k))
+#define mrb_toplevel_run(m,p) mrb_toplevel_run_keep((m),(p),0)
+#define mrb_context_run(m,p,s,k) mrb_vm_run((m),(p),(s),(k))
 
 MRB_API void mrb_p(mrb_state*, mrb_value);
 MRB_API mrb_int mrb_obj_id(mrb_value obj);
@@ -907,11 +1036,27 @@ MRB_API mrb_value mrb_Float(mrb_state *mrb, mrb_value val);
 MRB_API mrb_value mrb_inspect(mrb_state *mrb, mrb_value obj);
 MRB_API mrb_bool mrb_eql(mrb_state *mrb, mrb_value obj1, mrb_value obj2);
 
+static inline int mrb_gc_arena_save(mrb_state*);
+static inline void mrb_gc_arena_restore(mrb_state*,int);
+
+static inline int
+mrb_gc_arena_save(mrb_state *mrb)
+{
+  return mrb->gc.arena_idx;
+}
+
+static inline void
+mrb_gc_arena_restore(mrb_state *mrb, int idx)
+{
+  mrb->gc.arena_idx = idx;
+}
+
+MRB_API int mrb_gc_arena_save(mrb_state*);
+MRB_API void mrb_gc_arena_restore(mrb_state*,int);
+
 MRB_API void mrb_garbage_collect(mrb_state*);
 MRB_API void mrb_full_gc(mrb_state*);
 MRB_API void mrb_incremental_gc(mrb_state *);
-MRB_API int mrb_gc_arena_save(mrb_state*);
-MRB_API void mrb_gc_arena_restore(mrb_state*,int);
 MRB_API void mrb_gc_mark(mrb_state*,struct RBasic*);
 #define mrb_gc_mark_value(mrb,val) do {\
   if (!mrb_immediate_p(val)) mrb_gc_mark((mrb), mrb_basic_ptr(val)); \
@@ -964,28 +1109,32 @@ MRB_API void mrb_print_error(mrb_state *mrb);
    + those E_* macros requires mrb_state* variable named mrb.
    + exception objects obtained from those macros are local to mrb
 */
-#define E_RUNTIME_ERROR             (mrb_class_get(mrb, "RuntimeError"))
-#define E_TYPE_ERROR                (mrb_class_get(mrb, "TypeError"))
-#define E_ARGUMENT_ERROR            (mrb_class_get(mrb, "ArgumentError"))
-#define E_INDEX_ERROR               (mrb_class_get(mrb, "IndexError"))
-#define E_RANGE_ERROR               (mrb_class_get(mrb, "RangeError"))
-#define E_NAME_ERROR                (mrb_class_get(mrb, "NameError"))
-#define E_NOMETHOD_ERROR            (mrb_class_get(mrb, "NoMethodError"))
-#define E_SCRIPT_ERROR              (mrb_class_get(mrb, "ScriptError"))
-#define E_SYNTAX_ERROR              (mrb_class_get(mrb, "SyntaxError"))
-#define E_LOCALJUMP_ERROR           (mrb_class_get(mrb, "LocalJumpError"))
-#define E_REGEXP_ERROR              (mrb_class_get(mrb, "RegexpError"))
-#define E_SYSSTACK_ERROR            (mrb_class_get(mrb, "SystemStackError"))
-
-#define E_NOTIMP_ERROR              (mrb_class_get(mrb, "NotImplementedError"))
-#define E_FLOATDOMAIN_ERROR         (mrb_class_get(mrb, "FloatDomainError"))
-
-#define E_KEY_ERROR                 (mrb_class_get(mrb, "KeyError"))
+#define E_RUNTIME_ERROR             (mrb_exc_get(mrb, "RuntimeError"))
+#define E_TYPE_ERROR                (mrb_exc_get(mrb, "TypeError"))
+#define E_ARGUMENT_ERROR            (mrb_exc_get(mrb, "ArgumentError"))
+#define E_INDEX_ERROR               (mrb_exc_get(mrb, "IndexError"))
+#define E_RANGE_ERROR               (mrb_exc_get(mrb, "RangeError"))
+#define E_NAME_ERROR                (mrb_exc_get(mrb, "NameError"))
+#define E_NOMETHOD_ERROR            (mrb_exc_get(mrb, "NoMethodError"))
+#define E_SCRIPT_ERROR              (mrb_exc_get(mrb, "ScriptError"))
+#define E_SYNTAX_ERROR              (mrb_exc_get(mrb, "SyntaxError"))
+#define E_LOCALJUMP_ERROR           (mrb_exc_get(mrb, "LocalJumpError"))
+#define E_REGEXP_ERROR              (mrb_exc_get(mrb, "RegexpError"))
+
+#define E_NOTIMP_ERROR              (mrb_exc_get(mrb, "NotImplementedError"))
+#define E_FLOATDOMAIN_ERROR         (mrb_exc_get(mrb, "FloatDomainError"))
+
+#define E_KEY_ERROR                 (mrb_exc_get(mrb, "KeyError"))
 
 MRB_API mrb_value mrb_yield(mrb_state *mrb, mrb_value b, mrb_value arg);
 MRB_API mrb_value mrb_yield_argv(mrb_state *mrb, mrb_value b, mrb_int argc, const mrb_value *argv);
 MRB_API mrb_value mrb_yield_with_class(mrb_state *mrb, mrb_value b, mrb_int argc, const mrb_value *argv, mrb_value self, struct RClass *c);
 
+/* continue execution to the proc */
+/* this function should always be called as the last function of a method */
+/* e.g. return mrb_yield_cont(mrb, proc, self, argc, argv); */
+mrb_value mrb_yield_cont(mrb_state *mrb, mrb_value b, mrb_value self, mrb_int argc, const mrb_value *argv);
+
 /* mrb_gc_protect() leaves the object in the arena */
 MRB_API void mrb_gc_protect(mrb_state *mrb, mrb_value obj);
 /* mrb_gc_register() keeps the object from GC. */
@@ -1012,6 +1161,7 @@ MRB_API mrb_value mrb_attr_get(mrb_state *mrb, mrb_value obj, mrb_sym id);
 
 MRB_API mrb_bool mrb_respond_to(mrb_state *mrb, mrb_value obj, mrb_sym mid);
 MRB_API mrb_bool mrb_obj_is_instance_of(mrb_state *mrb, mrb_value obj, struct RClass* c);
+MRB_API mrb_bool mrb_func_basic_p(mrb_state *mrb, mrb_value obj, mrb_sym mid, mrb_func_t func);
 
 
 /*
@@ -1033,7 +1183,7 @@ MRB_API mrb_value mrb_fiber_yield(mrb_state *mrb, mrb_int argc, const mrb_value
  *
  * @mrbgem mruby-fiber
  */
-#define E_FIBER_ERROR (mrb_class_get(mrb, "FiberError"))
+#define E_FIBER_ERROR (mrb_exc_get(mrb, "FiberError"))
 
 /* memory pool implementation */
 typedef struct mrb_pool mrb_pool;
@@ -1049,22 +1199,34 @@ MRB_API void mrb_state_atexit(mrb_state *mrb, mrb_atexit_func func);
 MRB_API void mrb_show_version(mrb_state *mrb);
 MRB_API void mrb_show_copyright(mrb_state *mrb);
 
-#ifdef MRB_DEBUG
-#include <assert.h>
-#define mrb_assert(p) assert(p)
-#define mrb_assert_int_fit(t1,n,t2,max) assert((n)>=0 && ((sizeof(n)<=sizeof(t2))||(n<=(t1)(max))))
-#else
-#define mrb_assert(p) ((void)0)
-#define mrb_assert_int_fit(t1,n,t2,max) ((void)0)
-#endif
+MRB_API mrb_value mrb_format(mrb_state *mrb, const char *format, ...);
 
-#if __STDC_VERSION__ >= 201112L
-#define mrb_static_assert(exp, str) _Static_assert(exp, str)
-#else
-#define mrb_static_assert(exp, str) mrb_assert(exp)
-#endif
+#if 0
+/* memcpy and memset does not work with gdb reverse-next on my box */
+/* use naive memcpy and memset instead */
+#undef memcpy
+#undef memset
+static inline void*
+mrbmemcpy(void *dst, const void *src, size_t n)
+{
+  char *d = (char*)dst;
+  const char *s = (const char*)src;
+  while (n--)
+    *d++ = *s++;
+  return d;
+}
+#define memcpy(a,b,c) mrbmemcpy(a,b,c)
 
-MRB_API mrb_value mrb_format(mrb_state *mrb, const char *format, ...);
+static inline void*
+mrbmemset(void *s, int c, size_t n)
+{
+  char *t = (char*)s;
+  while (n--)
+    *t++ = c;
+  return s;
+}
+#define memset(a,b,c) mrbmemset(a,b,c)
+#endif
 
 MRB_END_DECL
 
index 16f5ace..bd3124d 100644 (file)
@@ -7,7 +7,7 @@
 #ifndef MRUBY_ARRAY_H
 #define MRUBY_ARRAY_H
 
-#include "mruby/common.h"
+#include "common.h"
 
 /*
  * Array class
@@ -21,22 +21,48 @@ typedef struct mrb_shared_array {
   mrb_value *ptr;
 } mrb_shared_array;
 
+#define MRB_ARY_EMBED_LEN_MAX ((mrb_int)(sizeof(void*)*3/sizeof(mrb_value)))
 struct RArray {
   MRB_OBJECT_HEADER;
-  mrb_int len;
   union {
-    mrb_int capa;
-    mrb_shared_array *shared;
-  } aux;
-  mrb_value *ptr;
+    struct {
+      mrb_int len;
+      union {
+        mrb_int capa;
+        mrb_shared_array *shared;
+      } aux;
+      mrb_value *ptr;
+    } heap;
+    mrb_value embed[MRB_ARY_EMBED_LEN_MAX];
+  } as;
 };
 
 #define mrb_ary_ptr(v)    ((struct RArray*)(mrb_ptr(v)))
 #define mrb_ary_value(p)  mrb_obj_value((void*)(p))
 #define RARRAY(v)  ((struct RArray*)(mrb_ptr(v)))
 
-#define RARRAY_LEN(a) (RARRAY(a)->len)
-#define RARRAY_PTR(a) ((const mrb_value*)RARRAY(a)->ptr)
+#define MRB_ARY_EMBED       4
+#define MRB_ARY_EMBED_MASK  3
+#define ARY_EMBED_P(a) ((a)->flags & MRB_ARY_EMBED)
+#define ARY_SET_EMBED_FLAG(a) ((a)->flags |= MRB_ARY_EMBED)
+#define ARY_UNSET_EMBED_FLAG(a) ((a)->flags &= ~(MRB_ARY_EMBED|MRB_ARY_EMBED_MASK))
+#define ARY_EMBED_LEN(a) ((a)->flags & MRB_ARY_EMBED_MASK)
+#define ARY_SET_EMBED_LEN(a,len) (a)->flags = ((a)->flags&~MRB_ARY_EMBED_MASK) | ((len)&MRB_ARY_EMBED_MASK);
+#define ARY_EMBED_PTR(a) (&((a)->as.embed[0]))
+
+#define ARY_LEN(a) (ARY_EMBED_P(a)?ARY_EMBED_LEN(a):(a)->as.heap.len)
+#define ARY_PTR(a) (ARY_EMBED_P(a)?ARY_EMBED_PTR(a):(a)->as.heap.ptr)
+#define RARRAY_LEN(a) ARY_LEN(RARRAY(a))
+#define RARRAY_PTR(a) ARY_PTR(RARRAY(a))
+#define ARY_SET_LEN(a,n) do {\
+  if (ARY_EMBED_P(a)) {\
+    mrb_assert((n) <= MRB_ARY_EMBED_LEN_MAX); \
+    ARY_SET_EMBED_LEN(a,n);\
+  }\
+  else\
+    (a)->as.heap.len = (n);\
+} while (0)
+#define ARY_CAPA(a) (ARY_EMBED_P(a)?MRB_ARY_EMBED_LEN_MAX:(a)->as.heap.aux.capa)
 #define MRB_ARY_SHARED      256
 #define ARY_SHARED_P(a) ((a)->flags & MRB_ARY_SHARED)
 #define ARY_SET_SHARED_FLAG(a) ((a)->flags |= MRB_ARY_SHARED)
@@ -54,14 +80,60 @@ MRB_API mrb_value mrb_ary_new_capa(mrb_state*, mrb_int);
  *      Array.new
  *
  * @param mrb The mruby state reference.
- * @return The initialized array
+ * @return The initialized array.
  */
 MRB_API mrb_value mrb_ary_new(mrb_state *mrb);
 
+/*
+ * Initializes a new array with initial values
+ *
+ * Equivalent to:
+ *
+ *      Array[value1, value2, ...]
+ *
+ * @param mrb The mruby state reference.
+ * @param size The numer of values.
+ * @param vals The actual values.
+ * @return The initialized array.
+ */
 MRB_API mrb_value mrb_ary_new_from_values(mrb_state *mrb, mrb_int size, const mrb_value *vals);
+
+/*
+ * Initializes a new array with two initial values
+ *
+ * Equivalent to:
+ *
+ *      Array[car, cdr]
+ *
+ * @param mrb The mruby state reference.
+ * @param car The first value.
+ * @param cdr The second value.
+ * @return The initialized array.
+ */
 MRB_API mrb_value mrb_assoc_new(mrb_state *mrb, mrb_value car, mrb_value cdr);
-MRB_API void mrb_ary_concat(mrb_state*, mrb_value, mrb_value);
-MRB_API mrb_value mrb_ary_splat(mrb_state*, mrb_value);
+
+/*
+ * Concatenate two arrays. The target array will be modified
+ *
+ * Equivalent to:
+ *      ary.concat(other)
+ *
+ * @param mrb The mruby state reference.
+ * @param self The target array.
+ * @param other The array that will be concatenated to self.
+ */
+MRB_API void mrb_ary_concat(mrb_state *mrb, mrb_value self, mrb_value other);
+
+/*
+ * Create an array from the input. It tries calling to_a on the
+ * value. If value does not respond to that, it creates a new
+ * array with just this value.
+ *
+ * @param mrb The mruby state reference.
+ * @param value The value to change into an array.
+ * @return An array representation of value.
+ */
+MRB_API mrb_value mrb_ary_splat(mrb_state *mrb, mrb_value value);
 
 /*
  * Pushes value into array.
@@ -84,8 +156,8 @@ MRB_API void mrb_ary_push(mrb_state *mrb, mrb_value array, mrb_value value);
  *      ary.pop
  *
  * @param mrb The mruby state reference.
- * @param ary The array from which the value will be poped.
- * @return The poped value.
+ * @param ary The array from which the value will be popped.
+ * @return The popped value.
  */
 MRB_API mrb_value mrb_ary_pop(mrb_state *mrb, mrb_value ary);
 
@@ -117,14 +189,81 @@ MRB_API mrb_value mrb_ary_ref(mrb_state *mrb, mrb_value ary, mrb_int n);
  */
 MRB_API void mrb_ary_set(mrb_state *mrb, mrb_value ary, mrb_int n, mrb_value val);
 
-MRB_API void mrb_ary_replace(mrb_state *mrb, mrb_value a, mrb_value b);
+/*
+ * Replace the array with another array
+ *
+ * Equivalent to:
+ *
+ *      ary.replace(other)
+ *
+ * @param mrb The mruby state reference
+ * @param self The target array.
+ * @param other The array to replace it with.
+ */
+MRB_API void mrb_ary_replace(mrb_state *mrb, mrb_value self, mrb_value other);
 MRB_API mrb_value mrb_check_array_type(mrb_state *mrb, mrb_value self);
+
+/*
+ * Unshift an element into an array
+ *
+ * Equivalent to:
+ *
+ *     ary.unshift(item)
+ *
+ * @param mrb The mruby state reference.
+ * @param self The target array.
+ * @param item The item to unshift.
+ */
 MRB_API mrb_value mrb_ary_unshift(mrb_state *mrb, mrb_value self, mrb_value item);
 MRB_API mrb_value mrb_ary_entry(mrb_value ary, mrb_int offset);
+
+/*
+ * Shifts the first element from the array.
+ *
+ * Equivalent to:
+ *
+ *      ary.shift
+ *
+ * @param mrb The mruby state reference.
+ * @param self The array from which the value will be shifted.
+ * @return The shifted value.
+ */
 MRB_API mrb_value mrb_ary_shift(mrb_state *mrb, mrb_value self);
+
+/*
+ * Removes all elements from this array
+ *
+ * Equivalent to:
+ *
+ *      ary.clear
+ *
+ * @param mrb The mruby state reference.
+ * @param self The target array.
+ * @return self
+ */
 MRB_API mrb_value mrb_ary_clear(mrb_state *mrb, mrb_value self);
+
+/*
+ * Join the array elements together in a string
+ *
+ * Equivalent to:
+ *
+ *      ary.join(sep="")
+ *
+ * @param mrb The mruby state reference.
+ * @param ary The target array
+ * @param sep The separater, can be NULL
+ */
 MRB_API mrb_value mrb_ary_join(mrb_state *mrb, mrb_value ary, mrb_value sep);
-MRB_API mrb_value mrb_ary_resize(mrb_state *mrb, mrb_value ary, mrb_int len);
+
+/*
+ * Update the capacity of the array
+ *
+ * @param mrb The mruby state reference.
+ * @param ary The target array.
+ * @param new_len The new capacity of the array
+ */
+MRB_API mrb_value mrb_ary_resize(mrb_state *mrb, mrb_value ary, mrb_int new_len);
 
 static inline mrb_int
 mrb_ary_len(mrb_state *mrb, mrb_value ary)
@@ -134,6 +273,15 @@ mrb_ary_len(mrb_state *mrb, mrb_value ary)
   return RARRAY_LEN(ary);
 }
 
+static inline mrb_value
+ary_elt(mrb_value ary, mrb_int offset)
+{
+  if (offset < 0 || RARRAY_LEN(ary) <= offset) {
+    return mrb_nil_value();
+  }
+  return RARRAY_PTR(ary)[offset];
+}
+
 MRB_END_DECL
 
 #endif  /* MRUBY_ARRAY_H */
index b71c4b7..700ea2e 100644 (file)
@@ -53,30 +53,35 @@ typedef struct mrb_value {
 #define mrb_float_pool(mrb,f) mrb_float_value(mrb,f)
 
 #define mrb_tt(o)       ((enum mrb_vtype)(((o).value.ttt & 0xfc000)>>14)-1)
-#define mrb_type(o)     ((uint32_t)0xfff00000 < (o).value.ttt ? mrb_tt(o) : MRB_TT_FLOAT)
+#define mrb_type(o)     (enum mrb_vtype)((uint32_t)0xfff00000 < (o).value.ttt ? mrb_tt(o) : MRB_TT_FLOAT)
 #define mrb_ptr(o)      ((void*)((((uintptr_t)0x3fffffffffff)&((uintptr_t)((o).value.p)))<<2))
 #define mrb_float(o)    (o).f
 #define mrb_cptr(o)     mrb_ptr(o)
 #define mrb_fixnum(o)   (o).value.i
 #define mrb_symbol(o)   (o).value.sym
 
+#ifdef MRB_64BIT
+#define BOXNAN_SHIFT_LONG_POINTER(v) (((uintptr_t)(v)>>34)&0x3fff)
+#else
+#define BOXNAN_SHIFT_LONG_POINTER(v) 0
+#endif
+
 #define BOXNAN_SET_VALUE(o, tt, attr, v) do {\
-  switch (tt) {\
-  case MRB_TT_FALSE:\
-  case MRB_TT_TRUE:\
-  case MRB_TT_UNDEF:\
-  case MRB_TT_FIXNUM:\
-  case MRB_TT_SYMBOL: (o).attr = (v); break;\
-  default: (o).value.i = 0; (o).value.p = (void*)((uintptr_t)(o).value.p | (((uintptr_t)(v))>>2)); break;\
-  }\
-  (o).value.ttt = (0xfff00000|(((tt)+1)<<14));\
+  (o).attr = (v);\
+  (o).value.ttt = 0xfff00000 | (((tt)+1)<<14);\
+} while (0)
+
+#define BOXNAN_SET_OBJ_VALUE(o, tt, v) do {\
+  (o).value.p = (void*)((uintptr_t)(v)>>2);\
+  (o).value.ttt = (0xfff00000|(((tt)+1)<<14)|BOXNAN_SHIFT_LONG_POINTER(v));\
 } while (0)
 
 #define SET_FLOAT_VALUE(mrb,r,v) do { \
   if (v != v) { \
     (r).value.ttt = 0x7ff80000; \
     (r).value.i = 0; \
-  } else { \
+  } \
+  else { \
     (r).f = v; \
   }} while(0)
 
@@ -86,8 +91,8 @@ typedef struct mrb_value {
 #define SET_BOOL_VALUE(r,b) BOXNAN_SET_VALUE(r, b ? MRB_TT_TRUE : MRB_TT_FALSE, value.i, 1)
 #define SET_INT_VALUE(r,n) BOXNAN_SET_VALUE(r, MRB_TT_FIXNUM, value.i, (n))
 #define SET_SYM_VALUE(r,v) BOXNAN_SET_VALUE(r, MRB_TT_SYMBOL, value.sym, (v))
-#define SET_OBJ_VALUE(r,v) BOXNAN_SET_VALUE(r, (((struct RObject*)(v))->tt), value.p, (v))
-#define SET_CPTR_VALUE(mrb,r,v) BOXNAN_SET_VALUE(r, MRB_TT_CPTR, value.p, v)
+#define SET_OBJ_VALUE(r,v) BOXNAN_SET_OBJ_VALUE(r, (((struct RObject*)(v))->tt), (v))
+#define SET_CPTR_VALUE(mrb,r,v) BOXNAN_SET_OBJ_VALUE(r, MRB_TT_CPTR, v)
 #define SET_UNDEF_VALUE(r) BOXNAN_SET_VALUE(r, MRB_TT_UNDEF, value.i, 0)
 
 #endif  /* MRUBY_BOXING_NAN_H */
index 8754087..d1c457f 100644 (file)
 # error MRB_INT16 is too small for MRB_WORD_BOXING.
 #endif
 
+#if defined(MRB_INT64) && !defined(MRB_64BIT)
+#error MRB_INT64 cannot be used with MRB_WORD_BOXING in 32-bit mode.
+#endif
+
 struct RFloat {
   MRB_OBJECT_HEADER;
   mrb_float f;
@@ -62,7 +66,7 @@ MRB_API mrb_value mrb_word_boxing_float_pool(struct mrb_state*, mrb_float);
 #define mrb_ptr(o)     (o).value.p
 #define mrb_cptr(o)    (o).value.vp->p
 #define mrb_float(o)   (o).value.fp->f
-#define mrb_fixnum(o)  (o).value.i
+#define mrb_fixnum(o)  ((mrb_int)(o).value.i)
 #define mrb_symbol(o)  (o).value.sym
 
 static inline enum mrb_vtype
@@ -91,13 +95,13 @@ mrb_type(mrb_value o)
 #define mrb_undef_p(o) ((o).w == MRB_Qundef)
 #define mrb_nil_p(o)  ((o).w == MRB_Qnil)
 
-#define BOXWORD_SET_VALUE(o, ttt, attr, v) do {\
+#define BOXWORD_SET_VALUE(o, ttt, attr, v) do { \
   switch (ttt) {\
   case MRB_TT_FALSE:  (o).w = (v) ? MRB_Qfalse : MRB_Qnil; break;\
   case MRB_TT_TRUE:   (o).w = MRB_Qtrue; break;\
   case MRB_TT_UNDEF:  (o).w = MRB_Qundef; break;\
-  case MRB_TT_FIXNUM: (o).value.i_flag = MRB_FIXNUM_FLAG; (o).attr = (v); break;\
-  case MRB_TT_SYMBOL: (o).value.sym_flag = MRB_SYMBOL_FLAG; (o).attr = (v); break;\
+  case MRB_TT_FIXNUM: (o).w = 0;(o).value.i_flag = MRB_FIXNUM_FLAG; (o).attr = (v); break;\
+  case MRB_TT_SYMBOL: (o).w = 0;(o).value.sym_flag = MRB_SYMBOL_FLAG; (o).attr = (v); break;\
   default:            (o).w = 0; (o).attr = (v); if ((o).value.bp) (o).value.bp->tt = ttt; break;\
   }\
 } while (0)
index 09565ec..0526386 100644 (file)
@@ -7,7 +7,7 @@
 #ifndef MRUBY_CLASS_H
 #define MRUBY_CLASS_H
 
-#include "mruby/common.h"
+#include "common.h"
 
 /**
  * Class class
@@ -51,7 +51,8 @@ mrb_class(mrb_state *mrb, mrb_value v)
   }
 }
 
-// TODO: figure out where to put user flags
+/* TODO: figure out where to put user flags */
+#define MRB_FLAG_IS_FROZEN (1 << 18)
 #define MRB_FLAG_IS_PREPENDED (1 << 19)
 #define MRB_FLAG_IS_ORIGIN (1 << 20)
 #define MRB_CLASS_ORIGIN(c) do {\
index e7841f3..1606399 100644 (file)
@@ -1,5 +1,5 @@
 /*
-** mruby/common.h - mruby common platform definitions
+**"common.h - mruby common platform definition"
 **
 ** See Copyright Notice in mruby.h
 */
@@ -9,8 +9,13 @@
 
 
 #ifdef __cplusplus
+#ifdef MRB_ENABLE_CXX_ABI
+#define MRB_BEGIN_DECL
+#define MRB_END_DECL
+#else
 # define MRB_BEGIN_DECL extern "C" {
 # define MRB_END_DECL  }
+#endif
 #else
 /** Start declarations in C mode */
 # define MRB_BEGIN_DECL
index 1909dea..13e04d0 100644 (file)
@@ -7,14 +7,14 @@
 #ifndef MRUBY_COMPILE_H
 #define MRUBY_COMPILE_H
 
-#include "mruby/common.h"
+#include "common.h"
 
 /**
  * MRuby Compiler
  */
 MRB_BEGIN_DECL
 
-#include "mruby.h"
+#include <mruby.h>
 
 struct mrb_jmpbuf;
 
@@ -33,6 +33,8 @@ typedef struct mrbc_context {
   mrb_bool no_exec:1;
   mrb_bool keep_lv:1;
   mrb_bool no_optimize:1;
+
+  size_t parser_nerr;
 } mrbc_context;
 
 MRB_API mrbc_context* mrbc_context_new(mrb_state *mrb);
@@ -40,8 +42,6 @@ MRB_API void mrbc_context_free(mrb_state *mrb, mrbc_context *cxt);
 MRB_API const char *mrbc_filename(mrb_state *mrb, mrbc_context *c, const char *s);
 MRB_API void mrbc_partial_hook(mrb_state *mrb, mrbc_context *c, int (*partial_hook)(struct mrb_parser_state*), void*data);
 
-MRB_API mrb_value mrb_toplevel_run_keep(mrb_state*, struct RProc*, unsigned int);
-
 /* AST node structure */
 typedef struct mrb_ast_node {
   struct mrb_ast_node *car, *cdr;
@@ -104,7 +104,8 @@ struct mrb_parser_heredoc_info {
   mrb_ast_node *doc;
 };
 
-#define MRB_PARSER_BUF_SIZE 1024
+#define MRB_PARSER_TOKBUF_MAX 65536
+#define MRB_PARSER_TOKBUF_SIZE 256
 
 /* parser structure */
 struct mrb_parser_state {
@@ -132,8 +133,10 @@ struct mrb_parser_state {
   mrb_ast_node *locals;
 
   mrb_ast_node *pb;
-  char buf[MRB_PARSER_BUF_SIZE];
-  int bidx;
+  char *tokbuf;
+  char buf[MRB_PARSER_TOKBUF_SIZE];
+  int tidx;
+  int tsiz;
 
   mrb_ast_node *all_heredocs;  /* list of mrb_parser_heredoc_info* */
   mrb_ast_node *heredocs_from_nextline;
@@ -162,6 +165,7 @@ struct mrb_parser_state {
 MRB_API struct mrb_parser_state* mrb_parser_new(mrb_state*);
 MRB_API void mrb_parser_free(struct mrb_parser_state*);
 MRB_API void mrb_parser_parse(struct mrb_parser_state*,mrbc_context*);
+MRB_API double mrb_float_read(const char*, char**);
 
 MRB_API void mrb_parser_set_filename(struct mrb_parser_state*, char const*);
 MRB_API char const* mrb_parser_get_filename(struct mrb_parser_state*, uint16_t idx);
@@ -173,6 +177,7 @@ MRB_API struct mrb_parser_state* mrb_parse_file(mrb_state*,FILE*,mrbc_context*);
 MRB_API struct mrb_parser_state* mrb_parse_string(mrb_state*,const char*,mrbc_context*);
 MRB_API struct mrb_parser_state* mrb_parse_nstring(mrb_state*,const char*,int,mrbc_context*);
 MRB_API struct RProc* mrb_generate_code(mrb_state*, struct mrb_parser_state*);
+MRB_API mrb_value mrb_load_exec(mrb_state *mrb, struct mrb_parser_state *p, mrbc_context *c);
 
 /* program load functions */
 #ifndef MRB_DISABLE_STDIO
index 472829c..5904705 100644 (file)
@@ -7,7 +7,7 @@
 #ifndef MRUBY_DATA_H
 #define MRUBY_DATA_H
 
-#include "mruby/common.h"
+#include "common.h"
 
 /**
  * Custom C wrapped data.
index 553968a..44f84e4 100644 (file)
@@ -7,7 +7,7 @@
 #ifndef MRUBY_DEBUG_H
 #define MRUBY_DEBUG_H
 
-#include "mruby/common.h"
+#include "common.h"
 
 /**
  * MRuby Debugging.
index 488a657..f56d66a 100644 (file)
@@ -7,9 +7,9 @@
 #ifndef MRUBY_DUMP_H
 #define MRUBY_DUMP_H
 
-#include "mruby.h"
-#include "mruby/irep.h"
-#include "mruby/common.h"
+#include <mruby.h>
+#include <mruby/irep.h>
+#include "common.h"
 
 /**
  * Dumping compiled mruby script.
@@ -52,7 +52,7 @@ MRB_API mrb_irep *mrb_read_irep(mrb_state*, const uint8_t*);
 /* Rite Binary File header */
 #define RITE_BINARY_IDENT              "RITE"
 #define RITE_BINARY_IDENT_LIL          "ETIR"
-#define RITE_BINARY_FORMAT_VER         "0003"
+#define RITE_BINARY_FORMAT_VER         "0004"
 #define RITE_COMPILER_NAME             "MATZ"
 #define RITE_COMPILER_VERSION          "0000"
 
index 1ac0791..0a26255 100644 (file)
@@ -7,7 +7,7 @@
 #ifndef MRUBY_ERROR_H
 #define MRUBY_ERROR_H
 
-#include "mruby/common.h"
+#include "common.h"
 
 /**
  * MRuby error handling.
@@ -32,6 +32,13 @@ MRB_API mrb_noreturn void mrb_no_method_error(mrb_state *mrb, mrb_sym id, mrb_va
 /* declaration for fail method */
 MRB_API mrb_value mrb_f_raise(mrb_state*, mrb_value);
 
+struct RBreak {
+  MRB_OBJECT_HEADER;
+  struct iv_tbl *iv;
+  struct RProc *proc;
+  mrb_value val;
+};
+
 /**
  * Protect
  *
index 64efb07..ce214aa 100644 (file)
@@ -7,7 +7,7 @@
 #ifndef MRUBY_GC_H
 #define MRUBY_GC_H
 
-#include "mruby/common.h"
+#include "common.h"
 
 /**
  * Uncommon memory management stuffs.
@@ -17,7 +17,9 @@ MRB_BEGIN_DECL
 
 struct mrb_state;
 
-typedef void (mrb_each_object_callback)(struct mrb_state *mrb, struct RBasic *obj, void *data);
+#define MRB_EACH_OBJ_OK 0
+#define MRB_EACH_OBJ_BREAK 1
+typedef int (mrb_each_object_callback)(struct mrb_state *mrb, struct RBasic *obj, void *data);
 void mrb_objspace_each_objects(struct mrb_state *mrb, mrb_each_object_callback *callback, void *data);
 MRB_API void mrb_free_context(struct mrb_state *mrb, struct mrb_context *c);
 
@@ -62,6 +64,7 @@ typedef struct mrb_gc {
   size_t threshold;
   int interval_ratio;
   int step_ratio;
+  mrb_bool iterating     :1;
   mrb_bool disabled      :1;
   mrb_bool full          :1;
   mrb_bool generational  :1;
index 542986c..1a87078 100644 (file)
@@ -7,7 +7,8 @@
 #ifndef MRUBY_HASH_H
 #define MRUBY_HASH_H
 
-#include "mruby/common.h"
+#include "common.h"
+#include <mruby/khash.h>
 
 /**
  * Hash class
@@ -23,42 +24,142 @@ struct RHash {
 #define mrb_hash_ptr(v)    ((struct RHash*)(mrb_ptr(v)))
 #define mrb_hash_value(p)  mrb_obj_value((void*)(p))
 
-MRB_API mrb_value mrb_hash_new_capa(mrb_state*, int);
+MRB_API mrb_value mrb_hash_new_capa(mrb_state*, mrb_int);
 
 /*
  * Initializes a new hash.
+ *
+ * Equivalent to:
+ *
+ *      Hash.new
+ *
+ * @param mrb The mruby state reference.
+ * @return The initialized hash.
  */
 MRB_API mrb_value mrb_hash_new(mrb_state *mrb);
 
 /*
  * Sets a keys and values to hashes.
+ *
+ * Equivalent to:
+ *
+ *      hash[key] = val
+ *
+ * @param mrb The mruby state reference.
+ * @param hash The target hash.
+ * @param key The key to set.
+ * @param val The value to set.
+ * @return The value.
  */
 MRB_API void mrb_hash_set(mrb_state *mrb, mrb_value hash, mrb_value key, mrb_value val);
 
 /*
- * Gets a value from a key.
+ * Gets a value from a key. If the key is not found, the default of the
+ * hash is used.
+ *
+ * Equivalent to:
+ *
+ *     hash[key]
+ *
+ * @param mrb The mruby state reference.
+ * @param hash The target hash.
+ * @param key The key to get.
+ * @return The found value.
  */
 MRB_API mrb_value mrb_hash_get(mrb_state *mrb, mrb_value hash, mrb_value key);
 
+/*
+ * Gets a value from a key. If the key is not found, the default parameter is
+ * used.
+ *
+ * Equivalent to:
+ *
+ *     hash.hash_key?(key) ? hash[key] : def
+ *
+ * @param mrb The mruby state reference.
+ * @param hash The target hash.
+ * @param key The key to get.
+ * @param def The default value.
+ * @return The found value.
+ */
 MRB_API mrb_value mrb_hash_fetch(mrb_state *mrb, mrb_value hash, mrb_value key, mrb_value def);
 
 /*
  * Deletes hash key and value pair.
+ *
+ * Equivalent to:
+ *
+ *     hash.delete(key)
+ *
+ * @param mrb The mruby state reference.
+ * @param hash The target hash.
+ * @param key The key to delete.
+ * @return The deleted value.
  */
 MRB_API mrb_value mrb_hash_delete_key(mrb_state *mrb, mrb_value hash, mrb_value key);
 
 /*
  * Gets an array of keys.
+ *
+ * Equivalent to:
+ *
+ *     hash.keys
+ *
+ * @param mrb The mruby state reference.
+ * @param hash The target hash.
+ * @return An array with the keys of the hash.
  */
 MRB_API mrb_value mrb_hash_keys(mrb_state *mrb, mrb_value hash);
 MRB_API mrb_value mrb_check_hash_type(mrb_state *mrb, mrb_value hash);
+
+/*
+ * Check if the hash is empty
+ *
+ * Equivalent to:
+ *
+ *     hash.empty?
+ *
+ * @param mrb The mruby state reference.
+ * @param self The target hash.
+ * @return True if the hash is empty, false otherwise.
+ */
 MRB_API mrb_value mrb_hash_empty_p(mrb_state *mrb, mrb_value self);
 
 /*
+ * Gets an array of values.
+ *
+ * Equivalent to:
+ *
+ *     hash.values
+ *
+ * @param mrb The mruby state reference.
+ * @param hash The target hash.
+ * @return An array with the values of the hash.
+ */
+MRB_API mrb_value mrb_hash_values(mrb_state *mrb, mrb_value hash);
+
+/*
  * Clears the hash.
+ *
+ * Equivalent to:
+ *
+ *     hash.clear
+ *
+ * @param mrb The mruby state reference.
+ * @param hash The target hash.
+ * @return The hash
  */
 MRB_API mrb_value mrb_hash_clear(mrb_state *mrb, mrb_value hash);
 
+/* declaration of struct kh_ht */
+/* be careful when you touch the internal */
+typedef struct {
+  mrb_value v;
+  mrb_int n;
+} mrb_hash_value;
+
+KHASH_DECLARE(ht, mrb_value, mrb_hash_value, TRUE)
+
 /* RHASH_TBL allocates st_table if not available. */
 #define RHASH(obj)   ((struct RHash*)(mrb_ptr(obj)))
 #define RHASH_TBL(h)          (RHASH(h)->ht)
@@ -66,7 +167,9 @@ MRB_API mrb_value mrb_hash_clear(mrb_state *mrb, mrb_value hash);
 #define RHASH_PROCDEFAULT(h)  RHASH_IFNONE(h)
 MRB_API struct kh_ht * mrb_hash_tbl(mrb_state *mrb, mrb_value hash);
 
-#define MRB_HASH_PROC_DEFAULT 256
+#define MRB_HASH_DEFAULT      1
+#define MRB_HASH_PROC_DEFAULT 2
+#define MRB_RHASH_DEFAULT_P(h) (RHASH(h)->flags & MRB_HASH_DEFAULT)
 #define MRB_RHASH_PROCDEFAULT_P(h) (RHASH(h)->flags & MRB_HASH_PROC_DEFAULT)
 
 /* GC functions */
index aaf8bd0..35ae2bb 100644 (file)
@@ -7,8 +7,8 @@
 #ifndef MRUBY_IREP_H
 #define MRUBY_IREP_H
 
-#include "mruby/common.h"
-#include "mruby/compile.h"
+#include "common.h"
+#include <mruby/compile.h>
 
 /**
  * Compiled mruby scripts.
@@ -39,6 +39,7 @@ typedef struct mrb_irep {
 
   struct mrb_locals *lv;
   /* debug info */
+  mrb_bool own_filename;
   const char *filename;
   uint16_t *lines;
   struct mrb_irep_debug_info* debug_info;
diff --git a/third-party/mruby/include/mruby/istruct.h b/third-party/mruby/include/mruby/istruct.h
new file mode 100644 (file)
index 0000000..4d2393c
--- /dev/null
@@ -0,0 +1,47 @@
+/*
+** mruby/istruct.h - Inline structures
+**
+** See Copyright Notice in mruby.h
+*/
+
+#ifndef MRUBY_ISTRUCT_H
+#define MRUBY_ISTRUCT_H
+
+#include "common.h"
+#include <string.h>
+
+/**
+ * Inline structures that fit in RVALUE
+ *
+ * They cannot have finalizer, and cannot have instance variables.
+ */
+MRB_BEGIN_DECL
+
+#define ISTRUCT_DATA_SIZE (sizeof(void*) * 3)
+
+struct RIstruct {
+  MRB_OBJECT_HEADER;
+  char inline_data[ISTRUCT_DATA_SIZE];
+};
+
+#define RISTRUCT(obj)         ((struct RIstruct*)(mrb_ptr(obj)))
+#define ISTRUCT_PTR(obj)      (RISTRUCT(obj)->inline_data)
+
+MRB_INLINE mrb_int mrb_istruct_size()
+{
+  return ISTRUCT_DATA_SIZE;
+}
+
+MRB_INLINE void* mrb_istruct_ptr(mrb_value object)
+{
+  return ISTRUCT_PTR(object);
+}
+
+MRB_INLINE void mrb_istruct_copy(mrb_value dest, mrb_value src)
+{
+  memcpy(ISTRUCT_PTR(dest), ISTRUCT_PTR(src), ISTRUCT_DATA_SIZE);
+}
+
+MRB_END_DECL
+
+#endif /* MRUBY_ISTRUCT_H */
index 4331ef1..9c40c6b 100644 (file)
@@ -9,8 +9,8 @@
 
 #include <string.h>
 
-#include "mruby.h"
-#include "mruby/common.h"
+#include <mruby.h>
+#include "common.h"
 
 /**
  * khash definitions used in mruby's hash table.
index 90cbd40..40c8c4a 100644 (file)
@@ -7,7 +7,7 @@
 #ifndef MRUBY_NUMERIC_H
 #define MRUBY_NUMERIC_H
 
-#include "mruby/common.h"
+#include "common.h"
 
 /**
  * Numeric class and it's sub-classes.
  */
 MRB_BEGIN_DECL
 
-#define POSFIXABLE(f) ((f) <= MRB_INT_MAX)
-#define NEGFIXABLE(f) ((f) >= MRB_INT_MIN)
-#define FIXABLE(f) (POSFIXABLE(f) && NEGFIXABLE(f))
+#define TYPED_POSFIXABLE(f,t) ((f) <= (t)MRB_INT_MAX)
+#define TYPED_NEGFIXABLE(f,t) ((f) >= (t)MRB_INT_MIN)
+#define TYPED_FIXABLE(f,t) (TYPED_POSFIXABLE(f,t) && TYPED_NEGFIXABLE(f,t))
+#define POSFIXABLE(f) TYPED_POSFIXABLE(f,mrb_int)
+#define NEGFIXABLE(f) TYPED_NEGFIXABLE(f,mrb_int)
+#define FIXABLE(f) TYPED_FIXABLE(f,mrb_int)
+#define FIXABLE_FLOAT(f) TYPED_FIXABLE(f,double)
 
 MRB_API mrb_value mrb_flo_to_fixnum(mrb_state *mrb, mrb_value val);
 MRB_API mrb_value mrb_fixnum_to_str(mrb_state *mrb, mrb_value x, int base);
@@ -31,53 +35,65 @@ mrb_value mrb_fixnum_minus(mrb_state *mrb, mrb_value x, mrb_value y);
 mrb_value mrb_fixnum_mul(mrb_state *mrb, mrb_value x, mrb_value y);
 mrb_value mrb_num_div(mrb_state *mrb, mrb_value x, mrb_value y);
 
-#define MRB_UINT_MAKE2(n) uint ## n ## _t
-#define MRB_UINT_MAKE(n) MRB_UINT_MAKE2(n)
-#define mrb_uint MRB_UINT_MAKE(MRB_INT_BIT)
+#ifndef __has_builtin
+  #define __has_builtin(x) 0
+#endif
 
-#define MRB_INT_OVERFLOW_MASK ((mrb_uint)1 << (MRB_INT_BIT - 1 - MRB_FIXNUM_SHIFT))
+#if (defined(__GNUC__) && __GNUC__ >= 5) ||   \
+    (__has_builtin(__builtin_add_overflow) && \
+     __has_builtin(__builtin_sub_overflow) && \
+     __has_builtin(__builtin_mul_overflow))
+# define MRB_HAVE_TYPE_GENERIC_CHECKED_ARITHMETIC_BUILTINS
+#endif
 
-/* Idea from Potion: https://github.com/perl11/potion (MIT) */
-#if (defined(__clang__) && ((__clang_major__ > 3) || (__clang_major__ == 3 && __clang_minor__ >= 4))) \
-    || (defined(__GNUC__) && __GNUC__ >= 5)
+/*
+// Clang 3.8 and 3.9 have problem compiling mruby in 32-bit mode, when MRB_INT64 is set
+// because of missing __mulodi4 and similar functions in its runtime. We need to use custom
+// implementation for them.
+*/
+#ifdef MRB_HAVE_TYPE_GENERIC_CHECKED_ARITHMETIC_BUILTINS
+#if defined(__clang__) && (__clang_major__ == 3) && (__clang_minor__ >= 8) && \
+    defined(MRB_32BIT) && defined(MRB_INT64)
+#undef MRB_HAVE_TYPE_GENERIC_CHECKED_ARITHMETIC_BUILTINS
+#endif
+#endif
 
-static inline mrb_bool
-mrb_int_add_overflow(mrb_int augend, mrb_int addend, mrb_int *sum)
-{
-  mrb_bool of;
+#ifdef MRB_HAVE_TYPE_GENERIC_CHECKED_ARITHMETIC_BUILTINS
 
-#ifdef MRB_INT64
-  long long val;
-  of = __builtin_saddll_overflow(augend, addend, &val) ||
+#ifndef MRB_WORD_BOXING
+# define WBCHK(x) 0
 #else
-  int val;
-  of = __builtin_sadd_overflow(augend, addend, &val) ||
+# define WBCHK(x) !FIXABLE(x)
 #endif
-  (val > MRB_INT_MAX) || (val < MRB_INT_MIN);
 
-  *sum = (mrb_int) val;
-  return of;
+static inline mrb_bool
+mrb_int_add_overflow(mrb_int augend, mrb_int addend, mrb_int *sum)
+{
+  return __builtin_add_overflow(augend, addend, sum) || WBCHK(*sum);
 }
 
 static inline mrb_bool
 mrb_int_sub_overflow(mrb_int minuend, mrb_int subtrahend, mrb_int *difference)
 {
-  mrb_bool of;
-
-#ifdef MRB_INT64
-  long long val;
-  of = __builtin_ssubll_overflow(minuend, subtrahend, &val) ||
-#else
-  int val;
-  of = __builtin_ssub_overflow(minuend, subtrahend, &val) ||
-#endif
-  (val > MRB_INT_MAX) || (val < MRB_INT_MIN);
+  return __builtin_sub_overflow(minuend, subtrahend, difference) || WBCHK(*difference);
+}
 
-  *difference = (mrb_int) val;
-  return of;
+static inline mrb_bool
+mrb_int_mul_overflow(mrb_int multiplier, mrb_int multiplicand, mrb_int *product)
+{
+  return __builtin_mul_overflow(multiplier, multiplicand, product) || WBCHK(*product);
 }
+
+#undef WBCHK
+
 #else
 
+#define MRB_UINT_MAKE2(n) uint ## n ## _t
+#define MRB_UINT_MAKE(n) MRB_UINT_MAKE2(n)
+#define mrb_uint MRB_UINT_MAKE(MRB_INT_BIT)
+
+#define MRB_INT_OVERFLOW_MASK ((mrb_uint)1 << (MRB_INT_BIT - 1 - MRB_FIXNUM_SHIFT))
+
 static inline mrb_bool
 mrb_int_add_overflow(mrb_int augend, mrb_int addend, mrb_int *sum)
 {
@@ -98,13 +114,42 @@ mrb_int_sub_overflow(mrb_int minuend, mrb_int subtrahend, mrb_int *difference)
   return !!(((x ^ z) & (~y ^ z)) & MRB_INT_OVERFLOW_MASK);
 }
 
+static inline mrb_bool
+mrb_int_mul_overflow(mrb_int multiplier, mrb_int multiplicand, mrb_int *product)
+{
+#if MRB_INT_BIT == 32
+  int64_t n = (int64_t)multiplier * multiplicand;
+  *product = (mrb_int)n;
+  return !FIXABLE(n);
+#else
+  if (multiplier > 0) {
+    if (multiplicand > 0) {
+      if (multiplier > MRB_INT_MAX / multiplicand) return TRUE;
+    }
+    else {
+      if (multiplicand < MRB_INT_MAX / multiplier) return TRUE;
+    }
+  }
+  else {
+    if (multiplicand > 0) {
+      if (multiplier < MRB_INT_MAX / multiplicand) return TRUE;
+    }
+    else {
+      if (multiplier != 0 && multiplicand < MRB_INT_MAX / multiplier) return TRUE;
+    }
+  }
+  *product = multiplier * multiplicand;
+  return FALSE;
 #endif
+}
 
 #undef MRB_INT_OVERFLOW_MASK
 #undef mrb_uint
 #undef MRB_UINT_MAKE
 #undef MRB_UINT_MAKE2
 
+#endif
+
 MRB_END_DECL
 
 #endif  /* MRUBY_NUMERIC_H */
index 9fbfe34..9347981 100644 (file)
@@ -22,6 +22,10 @@ struct RBasic {
 };
 #define mrb_basic_ptr(v) ((struct RBasic*)(mrb_ptr(v)))
 
+#define MRB_FROZEN_P(o) ((o)->flags & MRB_FLAG_IS_FROZEN)
+#define MRB_SET_FROZEN_FLAG(o) ((o)->flags |= MRB_FLAG_IS_FROZEN)
+#define MRB_UNSET_FROZEN_FLAG(o) ((o)->flags &= ~MRB_FLAG_IS_FROZEN)
+
 struct RObject {
   MRB_OBJECT_HEADER;
   struct iv_tbl *iv;
index 9dfa7f7..9a51162 100644 (file)
@@ -82,7 +82,8 @@ enum {
   OP_JMPIF,/*     A sBx   if R(A) pc+=sBx                                 */
   OP_JMPNOT,/*    A sBx   if !R(A) pc+=sBx                                */
   OP_ONERR,/*     sBx     rescue_push(pc+sBx)                             */
-  OP_RESCUE,/*    A       clear(exc); R(A) := exception (ignore when A=0) */
+  OP_RESCUE,/*    A B C   if A (if C exc=R(A) else R(A) := exc);
+                          if B R(B) := exc.isa?(R(B)); clear(exc)         */
   OP_POPERR,/*    A       A.times{rescue_pop()}                           */
   OP_RAISE,/*     A       raise(R(A))                                     */
   OP_EPUSH,/*     Bx      ensure_push(SEQ[Bx])                            */
index 85096fe..9c26662 100644 (file)
@@ -7,8 +7,8 @@
 #ifndef MRUBY_PROC_H
 #define MRUBY_PROC_H
 
-#include "mruby/common.h"
-#include "mruby/irep.h"
+#include "common.h"
+#include <mruby/irep.h>
 
 /**
  * Proc class
@@ -18,8 +18,11 @@ MRB_BEGIN_DECL
 struct REnv {
   MRB_OBJECT_HEADER;
   mrb_value *stack;
-  mrb_sym mid;
   ptrdiff_t cioff;
+  union {
+    mrb_sym mid;
+    struct mrb_context *c;
+  } cxt;
 };
 
 #define MRB_SET_ENV_STACK_LEN(e,len) (e)->flags = (unsigned int)(len)
@@ -27,6 +30,8 @@ struct REnv {
 #define MRB_ENV_UNSHARE_STACK(e) ((e)->cioff = -1)
 #define MRB_ENV_STACK_SHARED_P(e) ((e)->cioff >= 0)
 
+MRB_API void mrb_env_unshare(mrb_state*, struct REnv*);
+
 struct RProc {
   MRB_OBJECT_HEADER;
   union {
@@ -50,6 +55,8 @@ struct RProc {
 #define MRB_PROC_CFUNC_P(p) (((p)->flags & MRB_PROC_CFUNC) != 0)
 #define MRB_PROC_STRICT 256
 #define MRB_PROC_STRICT_P(p) (((p)->flags & MRB_PROC_STRICT) != 0)
+#define MRB_PROC_ORPHAN 512
+#define MRB_PROC_ORPHAN_P(p) (((p)->flags & MRB_PROC_ORPHAN) != 0)
 
 #define mrb_proc_ptr(v)    ((struct RProc*)(mrb_ptr(v)))
 
@@ -68,7 +75,7 @@ MRB_API mrb_value mrb_proc_cfunc_env_get(mrb_state*, mrb_int);
 /* old name */
 #define mrb_cfunc_env_get(mrb, idx) mrb_proc_cfunc_env_get(mrb, idx)
 
-#include "mruby/khash.h"
+#include <mruby/khash.h>
 KHASH_DECLARE(mt, mrb_sym, struct RProc*, TRUE)
 
 MRB_END_DECL
index 008698f..b166e58 100644 (file)
@@ -7,7 +7,7 @@
 #ifndef MRUBY_RANGE_H
 #define MRUBY_RANGE_H
 
-#include "mruby/common.h"
+#include "common.h"
 
 /**
  * Range class
@@ -25,7 +25,8 @@ struct RRange {
   mrb_bool excl : 1;
 };
 
-#define mrb_range_ptr(v)    ((struct RRange*)(mrb_ptr(v)))
+MRB_API struct RRange* mrb_range_ptr(mrb_state *mrb, mrb_value v);
+#define mrb_range_raw_ptr(v) ((struct RRange*)mrb_ptr(v))
 #define mrb_range_value(p)  mrb_obj_value((void*)(p))
 
 /*
@@ -40,7 +41,7 @@ struct RRange {
  */
 MRB_API mrb_value mrb_range_new(mrb_state *mrb, mrb_value start, mrb_value end, mrb_bool exclude);
 
-MRB_API mrb_bool mrb_range_beg_len(mrb_state *mrb, mrb_value range, mrb_int *begp, mrb_int *lenp, mrb_int len);
+MRB_API mrb_int mrb_range_beg_len(mrb_state *mrb, mrb_value range, mrb_int *begp, mrb_int *lenp, mrb_int len, mrb_bool trunc);
 mrb_value mrb_get_values_at(mrb_state *mrb, mrb_value obj, mrb_int olen, mrb_int argc, const mrb_value *argv, mrb_value (*func)(mrb_state*, mrb_value, mrb_int));
 
 MRB_END_DECL
index dfb3b0e..1d09d06 100644 (file)
@@ -7,14 +7,10 @@
 #ifndef MRUBY_RE_H
 #define MRUBY_RE_H
 
-#ifdef __cplusplus
-extern "C" {
-#endif
+MRB_BEGIN_DECL
 
 #define REGEXP_CLASS          "Regexp"
 
-#ifdef __cplusplus
-}
-#endif
+MRB_END_DECL
 
 #endif  /* RE_H */
index c049a72..b180932 100644 (file)
@@ -7,7 +7,7 @@
 #ifndef MRUBY_STRING_H
 #define MRUBY_STRING_H
 
-#include "mruby/common.h"
+#include "common.h"
 
 /**
  * String class
@@ -44,7 +44,8 @@ struct RString {
 #define RSTR_SET_LEN(s, n) do {\
   if (RSTR_EMBED_P(s)) {\
     RSTR_SET_EMBED_LEN((s),(n));\
-  } else {\
+  }\
+  else {\
     s->as.heap.len = (mrb_int)(n);\
   }\
 } while (0)
@@ -62,60 +63,236 @@ struct RString {
 #define RSTR_SET_NOFREE_FLAG(s) ((s)->flags |= MRB_STR_NOFREE)
 #define RSTR_UNSET_NOFREE_FLAG(s) ((s)->flags &= ~MRB_STR_NOFREE)
 
-#define RSTR_FROZEN_P(s) ((s)->flags & MRB_STR_FROZEN)
-#define RSTR_SET_FROZEN_FLAG(s) ((s)->flags |= MRB_STR_FROZEN)
-#define RSTR_UNSET_FROZEN_FLAG(s) ((s)->flags &= ~MRB_STR_FROZEN)
-
 /*
  * Returns a pointer from a Ruby string
  */
 #define mrb_str_ptr(s)       ((struct RString*)(mrb_ptr(s)))
 #define RSTRING(s)           mrb_str_ptr(s)
 #define RSTRING_PTR(s)       RSTR_PTR(RSTRING(s))
-#define RSTRING_EMBED_LEN(s) RSTR_ENBED_LEN(RSTRING(s))
+#define RSTRING_EMBED_LEN(s) RSTR_EMBED_LEN(RSTRING(s))
 #define RSTRING_LEN(s)       RSTR_LEN(RSTRING(s))
 #define RSTRING_CAPA(s)      RSTR_CAPA(RSTRING(s))
 #define RSTRING_END(s)       (RSTRING_PTR(s) + RSTRING_LEN(s))
-mrb_int mrb_str_strlen(mrb_state*, struct RString*);
+MRB_API mrb_int mrb_str_strlen(mrb_state*, struct RString*);
 
 #define MRB_STR_SHARED    1
 #define MRB_STR_NOFREE    2
-#define MRB_STR_FROZEN    4
-#define MRB_STR_EMBED     8
-#define MRB_STR_EMBED_LEN_MASK 0x1f0
-#define MRB_STR_EMBED_LEN_SHIFT 4
+#define MRB_STR_NO_UTF    8
+#define MRB_STR_EMBED    16
+#define MRB_STR_EMBED_LEN_MASK 0x3e0
+#define MRB_STR_EMBED_LEN_SHIFT 5
 
 void mrb_gc_free_str(mrb_state*, struct RString*);
 MRB_API void mrb_str_modify(mrb_state*, struct RString*);
+/*
+ * Appends self to other. Returns self as a concatnated string.
+ *
+ *
+ *  Example:
+ *
+ *     !!!c
+ *     int
+ *     main(int argc,
+ *          char **argv)
+ *     {
+ *       // Variable declarations.
+ *       mrb_value str1;
+ *       mrb_value str2;
+ *
+ *       mrb_state *mrb = mrb_open();
+ *       if (!mrb)
+ *       {
+ *          // handle error
+ *       }
+ *
+ *       // Creates new Ruby strings.
+ *       str1 = mrb_str_new_lit(mrb, "abc");
+ *       str2 = mrb_str_new_lit(mrb, "def");
+ *
+ *       // Concatnates str2 to str1.
+ *       mrb_str_concat(mrb, str1, str2);
+ *
+ *      // Prints new Concatnated Ruby string.
+ *      mrb_p(mrb, str1);
+ *
+ *      mrb_close(mrb);
+ *      return 0;
+ *    }
+ *
+ *
+ *  Result:
+ *
+ *     => "abcdef"
+ *
+ * @param [mrb_state] mrb The current mruby state.
+ * @param [mrb_value] self String to concatenate.
+ * @param [mrb_value] other String to append to self.
+ * @return [mrb_value] Returns a new String appending other to self.
+ */
 MRB_API void mrb_str_concat(mrb_state*, mrb_value, mrb_value);
 
 /*
  * Adds two strings together.
+ *
+ *
+ *  Example:
+ *
+ *     !!!c
+ *     int
+ *     main(int argc,
+ *          char **argv)
+ *     {
+ *       // Variable declarations.
+ *       mrb_value a;
+ *       mrb_value b;
+ *       mrb_value c;
+ *
+ *       mrb_state *mrb = mrb_open();
+ *       if (!mrb)
+ *       {
+ *          // handle error
+ *       }
+ *
+ *       // Creates two Ruby strings from the passed in C strings.
+ *       a = mrb_str_new_lit(mrb, "abc");
+ *       b = mrb_str_new_lit(mrb, "def");
+ *
+ *       // Prints both C strings.
+ *       mrb_p(mrb, a);
+ *       mrb_p(mrb, b);
+ *
+ *       // Concatnates both Ruby strings.
+ *       c = mrb_str_plus(mrb, a, b);
+ *
+ *      // Prints new Concatnated Ruby string.
+ *      mrb_p(mrb, c);
+ *
+ *      mrb_close(mrb);
+ *      return 0;
+ *    }
+ *
+ *
+ *  Result:
+ *
+ *     => "abc"  # First string
+ *     => "def"  # Second string
+ *     => "abcdef" # First & Second concatnated.
+ *
+ * @param [mrb_state] mrb The current mruby state.
+ * @param [mrb_value] a First string to concatenate.
+ * @param [mrb_value] b Second string to concatenate.
+ * @return [mrb_value] Returns a new String containing a concatenated to b.
  */
 MRB_API mrb_value mrb_str_plus(mrb_state*, mrb_value, mrb_value);
 
 /*
  * Converts pointer into a Ruby string.
+ *
+ * @param [mrb_state] mrb The current mruby state.
+ * @param [void*] p The pointer to convert to Ruby string.
+ * @return [mrb_value] Returns a new Ruby String.
  */
 MRB_API mrb_value mrb_ptr_to_str(mrb_state *, void*);
 
 /*
  * Returns an object as a Ruby string.
+ *
+ * @param [mrb_state] mrb The current mruby state.
+ * @param [mrb_value] obj An object to return as a Ruby string.
+ * @return [mrb_value] An object as a Ruby string.
  */
 MRB_API mrb_value mrb_obj_as_string(mrb_state *mrb, mrb_value obj);
 
 /*
- * Resizes the string's length.
+ * Resizes the string's length. Returns the amount of characters
+ * in the specified by len.
+ *
+ * Example:
+ *
+ *     !!!c
+ *     int
+ *     main(int argc,
+ *          char **argv)
+ *     {
+ *         // Variable declaration.
+ *         mrb_value str;
+ *
+ *         mrb_state *mrb = mrb_open();
+ *         if (!mrb)
+ *         {
+ *            // handle error
+ *         }
+ *         // Creates a new string.
+ *         str = mrb_str_new_lit(mrb, "Hello, world!");
+ *         // Returns 5 characters of
+ *         mrb_str_resize(mrb, str, 5);
+ *         mrb_p(mrb, str);
+ *
+ *         mrb_close(mrb);
+ *         return 0;
+ *      }
+ *
+ * Result:
+ *
+ *     => "Hello"
+ *
+ * @param [mrb_state] mrb The current mruby state.
+ * @param [mrb_value] str The Ruby string to resize.
+ * @param [mrb_value] len The length.
+ * @return [mrb_value] An object as a Ruby string.
  */
 MRB_API mrb_value mrb_str_resize(mrb_state *mrb, mrb_value str, mrb_int len);
 
 /*
  * Returns a sub string.
+ *
+ *  Example:
+ *
+ *     !!!c
+ *     int
+ *     main(int argc,
+ *     char const **argv)
+ *     {
+ *       // Variable declarations.
+ *       mrb_value str1;
+ *       mrb_value str2;
+ *
+ *       mrb_state *mrb = mrb_open();
+ *       if (!mrb)
+ *       {
+ *         // handle error
+ *       }
+ *       // Creates new string.
+ *       str1 = mrb_str_new_lit(mrb, "Hello, world!");
+ *       // Returns a sub-string within the range of 0..2
+ *       str2 = mrb_str_substr(mrb, str1, 0, 2);
+ *
+ *       // Prints sub-string.
+ *       mrb_p(mrb, str2);
+ *
+ *       mrb_close(mrb);
+ *       return 0;
+ *     }
+ *
+ *  Result:
+ *
+ *     => "He"
+ *
+ * @param [mrb_state] mrb The current mruby state.
+ * @param [mrb_value] str Ruby string.
+ * @param [mrb_int] beg The beginning point of the sub-string.
+ * @param [mrb_int] len The end point of the sub-string.
+ * @return [mrb_value] An object as a Ruby sub-string.
  */
 MRB_API mrb_value mrb_str_substr(mrb_state *mrb, mrb_value str, mrb_int beg, mrb_int len);
 
 /*
  * Returns a Ruby string type.
+ *
+ *
+ * @param [mrb_state] mrb The current mruby state.
+ * @param [mrb_value] str Ruby string.
+ * @return [mrb_value] A Ruby string.
  */
 MRB_API mrb_value mrb_string_type(mrb_state *mrb, mrb_value str);
 
@@ -123,15 +300,33 @@ MRB_API mrb_value mrb_check_string_type(mrb_state *mrb, mrb_value str);
 MRB_API mrb_value mrb_str_buf_new(mrb_state *mrb, size_t capa);
 
 MRB_API const char *mrb_string_value_cstr(mrb_state *mrb, mrb_value *ptr);
-MRB_API const char *mrb_string_value_ptr(mrb_state *mrb, mrb_value ptr);
+MRB_API const char *mrb_string_value_ptr(mrb_state *mrb, mrb_value str);
+/*
+ * Returns the length of the Ruby string.
+ *
+ *
+ * @param [mrb_state] mrb The current mruby state.
+ * @param [mrb_value] str Ruby string.
+ * @return [mrb_int] The length of the passed in Ruby string.
+ */
+MRB_API mrb_int mrb_string_value_len(mrb_state *mrb, mrb_value str);
 
 /*
  * Duplicates a string object.
+ *
+ *
+ * @param [mrb_state] mrb The current mruby state.
+ * @param [mrb_value] str Ruby string.
+ * @return [mrb_value] Duplicated Ruby string.
  */
 MRB_API mrb_value mrb_str_dup(mrb_state *mrb, mrb_value str);
 
 /*
- * Returns a symbol from a passed in string.
+ * Returns a symbol from a passed in Ruby string.
+ *
+ * @param [mrb_state] mrb The current mruby state.
+ * @param [mrb_value] self Ruby string.
+ * @return [mrb_value] A symbol.
  */
 MRB_API mrb_value mrb_str_intern(mrb_state *mrb, mrb_value self);
 
@@ -145,12 +340,22 @@ MRB_API mrb_value mrb_str_to_str(mrb_state *mrb, mrb_value str);
 
 /*
  * Returns true if the strings match and false if the strings don't match.
+ *
+ * @param [mrb_state] mrb The current mruby state.
+ * @param [mrb_value] str1 Ruby string to compare.
+ * @param [mrb_value] str2 Ruby string to compare.
+ * @return [mrb_value] boolean value.
  */
 MRB_API mrb_bool mrb_str_equal(mrb_state *mrb, mrb_value str1, mrb_value str2);
 
 /*
  * Returns a concated string comprised of a Ruby string and a C string.
  *
+ * @param [mrb_state] mrb The current mruby state.
+ * @param [mrb_value] str Ruby string.
+ * @param [const char *] ptr A C string.
+ * @param [size_t] len length of C string.
+ * @return [mrb_value] A Ruby string.
  * @see mrb_str_cat_cstr
  */
 MRB_API mrb_value mrb_str_cat(mrb_state *mrb, mrb_value str, const char *ptr, size_t len);
@@ -158,6 +363,10 @@ MRB_API mrb_value mrb_str_cat(mrb_state *mrb, mrb_value str, const char *ptr, si
 /*
  * Returns a concated string comprised of a Ruby string and a C string.
  *
+ * @param [mrb_state] mrb The current mruby state.
+ * @param [mrb_value] str Ruby string.
+ * @param [const char *] ptr A C string.
+ * @return [mrb_value] A Ruby string.
  * @see mrb_str_cat
  */
 MRB_API mrb_value mrb_str_cat_cstr(mrb_state *mrb, mrb_value str, const char *ptr);
@@ -175,7 +384,19 @@ MRB_API mrb_value mrb_str_append(mrb_state *mrb, mrb_value str, mrb_value str2);
 MRB_API int mrb_str_cmp(mrb_state *mrb, mrb_value str1, mrb_value str2);
 
 /*
- * Returns a C string from a Ruby string.
+ * Returns a newly allocated C string from a Ruby string.
+ * This is an utility function to pass a Ruby string to C library functions.
+ *
+ * - Returned string does not contain any NUL characters (but terminator).
+ * - It raises an ArgumentError exception if Ruby string contains
+ *   NUL characters.
+ * - Retured string will be freed automatically on next GC.
+ * - Caller can modify returned string without affecting Ruby string
+ *   (e.g. it can be used for mkstemp(3)).
+ *
+ * @param [mrb_state *] mrb The current mruby state.
+ * @param [mrb_value] str Ruby string. Must be an instance of String.
+ * @return [char *] A newly allocated C string.
  */
 MRB_API char *mrb_str_to_cstr(mrb_state *mrb, mrb_value str);
 
index 7f720d5..5d3d214 100644 (file)
@@ -7,7 +7,13 @@
 #ifndef MRB_THROW_H
 #define MRB_THROW_H
 
-#ifdef MRB_ENABLE_CXX_EXCEPTION
+#if defined(MRB_ENABLE_CXX_ABI)
+# if !defined(__cplusplus)
+#  error Trying to use C++ exception handling in C code
+# endif
+#endif
+
+#if defined(MRB_ENABLE_CXX_EXCEPTION) && defined(__cplusplus)
 
 #define MRB_TRY(buf) do { try {
 #define MRB_CATCH(buf) } catch(mrb_jmpbuf_impl e) { if (e != (buf)->impl) { throw e; }
@@ -20,11 +26,19 @@ typedef mrb_int mrb_jmpbuf_impl;
 
 #include <setjmp.h>
 
-#define MRB_TRY(buf) do { if (setjmp((buf)->impl) == 0) {
+#if defined(__APPLE__) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__)
+#define MRB_SETJMP _setjmp
+#define MRB_LONGJMP _longjmp
+#else
+#define MRB_SETJMP setjmp
+#define MRB_LONGJMP longjmp
+#endif
+
+#define MRB_TRY(buf) do { if (MRB_SETJMP((buf)->impl) == 0) {
 #define MRB_CATCH(buf) } else {
 #define MRB_END_EXC(buf) } } while(0)
 
-#define MRB_THROW(buf) longjmp((buf)->impl, 1);
+#define MRB_THROW(buf) MRB_LONGJMP((buf)->impl, 1);
 #define mrb_jmpbuf_impl jmp_buf
 
 #endif
@@ -32,7 +46,7 @@ typedef mrb_int mrb_jmpbuf_impl;
 struct mrb_jmpbuf {
   mrb_jmpbuf_impl impl;
 
-#ifdef MRB_ENABLE_CXX_EXCEPTION
+#if defined(MRB_ENABLE_CXX_EXCEPTION) && defined(__cplusplus)
   static mrb_int jmpbuf_id;
   mrb_jmpbuf() : impl(jmpbuf_id++) {}
 #endif
index 002ea85..98c68d6 100644 (file)
@@ -7,7 +7,7 @@
 #ifndef MRUBY_VALUE_H
 #define MRUBY_VALUE_H
 
-#include "mruby/common.h"
+#include "common.h"
 
 /**
  * MRuby Value definition functions and macros.
@@ -22,29 +22,52 @@ struct mrb_state;
 # error "You can't define MRB_INT16 and MRB_INT64 at the same time."
 #endif
 
+#if defined _MSC_VER && _MSC_VER < 1800
+# define PRIo64 "llo"
+# define PRId64 "lld"
+# define PRIx64 "llx"
+# define PRIo16 "ho"
+# define PRId16 "hd"
+# define PRIx16 "hx"
+# define PRIo32 "o"
+# define PRId32 "d"
+# define PRIx32 "x"
+#else
+# include <inttypes.h>
+#endif
+
 #if defined(MRB_INT64)
   typedef int64_t mrb_int;
 # define MRB_INT_BIT 64
 # define MRB_INT_MIN (INT64_MIN>>MRB_FIXNUM_SHIFT)
 # define MRB_INT_MAX (INT64_MAX>>MRB_FIXNUM_SHIFT)
+# define MRB_PRIo PRIo64
+# define MRB_PRId PRId64
+# define MRB_PRIx PRIx64
 #elif defined(MRB_INT16)
   typedef int16_t mrb_int;
 # define MRB_INT_BIT 16
 # define MRB_INT_MIN (INT16_MIN>>MRB_FIXNUM_SHIFT)
 # define MRB_INT_MAX (INT16_MAX>>MRB_FIXNUM_SHIFT)
+# define MRB_PRIo PRIo16
+# define MRB_PRId PRId16
+# define MRB_PRIx PRIx16
 #else
   typedef int32_t mrb_int;
 # define MRB_INT_BIT 32
 # define MRB_INT_MIN (INT32_MIN>>MRB_FIXNUM_SHIFT)
 # define MRB_INT_MAX (INT32_MAX>>MRB_FIXNUM_SHIFT)
+# define MRB_PRIo PRIo32
+# define MRB_PRId PRId32
+# define MRB_PRIx PRIx32
 #endif
 
+
+MRB_API double mrb_float_read(const char*, char**);
 #ifdef MRB_USE_FLOAT
   typedef float mrb_float;
-# define str_to_mrb_float(buf) strtof(buf, NULL)
 #else
   typedef double mrb_float;
-# define str_to_mrb_float(buf) strtod(buf, NULL)
 #endif
 
 #if defined _MSC_VER && _MSC_VER < 1900
@@ -62,7 +85,6 @@ MRB_API int mrb_msvc_snprintf(char *s, size_t n, const char *format, ...);
 #  define isnan _isnan
 #  define isinf(n) (!_finite(n) && !_isnan(n))
 #  define signbit(n) (_copysign(1.0, (n)) < 0.0)
-#  define strtof (float)strtod
 static const unsigned int IEEE754_INFINITY_BITS_SINGLE = 0x7F800000;
 #  define INFINITY (*(float *)&IEEE754_INFINITY_BITS_SINGLE)
 #  define NAN ((float)(INFINITY - INFINITY))
@@ -93,10 +115,12 @@ enum mrb_vtype {
   MRB_TT_ENV,         /*  20 */
   MRB_TT_DATA,        /*  21 */
   MRB_TT_FIBER,       /*  22 */
-  MRB_TT_MAXDEFINE    /*  23 */
+  MRB_TT_ISTRUCT,     /*  23 */
+  MRB_TT_BREAK,       /*  24 */
+  MRB_TT_MAXDEFINE    /*  25 */
 };
 
-#include "mruby/object.h"
+#include <mruby/object.h>
 
 #ifdef MRB_DOCUMENTATION_BLOCK
 
@@ -187,6 +211,8 @@ mrb_obj_value(void *p)
 {
   mrb_value v;
   SET_OBJ_VALUE(v, (struct RBasic*)p);
+  mrb_assert(p == mrb_ptr(v));
+  mrb_assert(((struct RBasic*)p)->tt == mrb_type(v));
   return v;
 }
 
@@ -241,6 +267,14 @@ mrb_undef_value(void)
 }
 
 #ifdef MRB_USE_ETEXT_EDATA
+#if (defined(__APPLE__) && defined(__MACH__))
+#include <mach-o/getsect.h>
+static inline mrb_bool
+mrb_ro_data_p(const char *p)
+{
+  return (const char*)get_etext() < p && p < (const char*)get_edata();
+}
+#else
 extern char _etext[];
 #ifdef MRB_NO_INIT_ARRAY_START
 extern char _edata[];
@@ -259,6 +293,7 @@ mrb_ro_data_p(const char *p)
   return _etext < p && p < (char*)&__init_array_start;
 }
 #endif
+#endif
 #else
 # define mrb_ro_data_p(p) FALSE
 #endif
index 063d65b..2f2bbbf 100644 (file)
@@ -7,7 +7,7 @@
 #ifndef MRUBY_VARIABLE_H
 #define MRUBY_VARIABLE_H
 
-#include "mruby/common.h"
+#include "common.h"
 
 /**
  * Functions to access mruby variables.
@@ -54,9 +54,67 @@ MRB_API mrb_bool mrb_iv_defined(mrb_state*, mrb_value, mrb_sym);
 MRB_API mrb_value mrb_iv_remove(mrb_state *mrb, mrb_value obj, mrb_sym sym);
 MRB_API void mrb_iv_copy(mrb_state *mrb, mrb_value dst, mrb_value src);
 MRB_API mrb_bool mrb_const_defined_at(mrb_state *mrb, mrb_value mod, mrb_sym id);
+
+/**
+ * Get a global variable. Will return nil if the var does not exist
+ *
+ * Example:
+ *
+ *     !!!ruby
+ *     # Ruby style
+ *     var = $value
+ *
+ *     !!!c
+ *     // C style
+ *     mrb_sym sym = mrb_intern_lit(mrb, "$value");
+ *     mrb_value var = mrb_gv_get(mrb, sym);
+ *
+ * @param mrb The mruby state reference
+ * @param sym The name of the global variable
+ * @return The value of that global variable. May be nil
+ */
 MRB_API mrb_value mrb_gv_get(mrb_state *mrb, mrb_sym sym);
+
+/**
+ * Set a global variable
+ *
+ * Example:
+ *
+ *     !!!ruby
+ *     # Ruby style
+ *     $value = "foo"
+ *
+ *     !!!c
+ *     // C style
+ *     mrb_sym sym = mrb_intern_lit(mrb, "$value");
+ *     mrb_gv_set(mrb, sym, mrb_str_new_lit("foo"));
+ *
+ * @param mrb The mruby state reference
+ * @param sym The name of the global variable
+ * @param val The value of the global variable
+ */
 MRB_API void mrb_gv_set(mrb_state *mrb, mrb_sym sym, mrb_value val);
+
+/**
+ * Remove a global variable.
+ *
+ * Example:
+ *
+ *     !!!ruby
+ *     # Ruby style
+ *     $value = nil
+ *
+ *     !!!c
+ *     // C style
+ *     mrb_sym sym = mrb_intern_lit(mrb, "$value");
+ *     mrb_gv_remove(mrb, sym);
+ *
+ * @param mrb The mruby state reference
+ * @param sym The name of the global variable
+ * @param val The value of the global variable
+ */
 MRB_API void mrb_gv_remove(mrb_state *mrb, mrb_sym sym);
+
 MRB_API mrb_value mrb_cv_get(mrb_state *mrb, mrb_value mod, mrb_sym sym);
 MRB_API void mrb_mod_cv_set(mrb_state *mrb, struct RClass * c, mrb_sym sym, mrb_value v);
 MRB_API void mrb_cv_set(mrb_state *mrb, mrb_value mod, mrb_sym sym, mrb_value v);
index ffe8106..8414bf2 100644 (file)
@@ -7,7 +7,7 @@
 #ifndef MRUBY_VERSION_H
 #define MRUBY_VERSION_H
 
-#include "mruby/common.h"
+#include "common.h"
 
 /**
  * mruby version definition macros
@@ -42,7 +42,7 @@ MRB_BEGIN_DECL
 /*
  * Minor release version number.
  */
-#define MRUBY_RELEASE_MINOR 2
+#define MRUBY_RELEASE_MINOR 3
 
 /*
  * Tiny release version number.
@@ -62,17 +62,17 @@ MRB_BEGIN_DECL
 /*
  * Release year.
  */
-#define MRUBY_RELEASE_YEAR 2015
+#define MRUBY_RELEASE_YEAR 2017
 
 /*
  * Release month.
  */
-#define MRUBY_RELEASE_MONTH 11
+#define MRUBY_RELEASE_MONTH 7
 
 /*
  * Release day.
  */
-#define MRUBY_RELEASE_DAY 17
+#define MRUBY_RELEASE_DAY 4
 
 /*
  * Release date as a string.
index eb219b9..542c37a 100755 (executable)
@@ -113,8 +113,7 @@ module MiniRake
     # Timestamp for this task.  Basic tasks return the current time for
     # their time stamp.  Other tasks can be more sophisticated.
     def timestamp
-      prerequisites = @prerequisites.collect{ |n| n.is_a?(Proc) ? n.call(name) : n }.flatten
-      prerequisites.collect { |n| Task[n].timestamp }.max || Time.now
+      Time.now
     end
 
     # Class Methods ----------------------------------------------------
@@ -237,6 +236,7 @@ module MiniRake
 
     # Time stamp for file task.
     def timestamp
+      return Time.at(0) unless File.exist?(name)
       stat = File::stat(name.to_s)
       stat.directory? ? Time.at(0) : stat.mtime
     end
index 0960ba9..64f05de 100644 (file)
@@ -53,7 +53,7 @@ MRuby::GemBox.new do |conf|
   # Use Enumerator class (require mruby-fiber)
   conf.gem :core => "mruby-enumerator"
 
-  # Use Enumerable::Lazy class (require mruby-enumerator)
+  # Use Enumerator::Lazy class (require mruby-enumerator)
   conf.gem :core => "mruby-enum-lazy"
 
   # Use toplevel object (main) methods extension
@@ -71,6 +71,9 @@ MRuby::GemBox.new do |conf|
   # Use Kernel module extension
   conf.gem :core => "mruby-kernel-ext"
 
+  # Use class/module extension
+  conf.gem :core => "mruby-class-ext"
+
   # Use mruby-compiler to build other mrbgems
   conf.gem :core => "mruby-compiler"
 end
index 35be793..1e6d4d5 100644 (file)
@@ -1,6 +1,31 @@
 class Array
   ##
   # call-seq:
+  #    Array.try_convert(obj) -> array or nil
+  #
+  # Tries to convert +obj+ into an array, using +to_ary+ method.
+  # converted array or +nil+ if +obj+ cannot be converted for any reason.
+  # This method can be used to check if an argument is an array.
+  #
+  #    Array.try_convert([1])   #=> [1]
+  #    Array.try_convert("1")   #=> nil
+  #
+  #    if tmp = Array.try_convert(arg)
+  #      # the argument is an array
+  #    elsif tmp = String.try_convert(arg)
+  #      # the argument is a string
+  #    end
+  #
+  def self.try_convert(obj)
+    if obj.respond_to?(:to_ary)
+      obj.to_ary
+    else
+      nil
+    end
+  end
+
+  ##
+  # call-seq:
   #    ary.uniq!                -> ary or nil
   #    ary.uniq! { |item| ... } -> ary or nil
   #
@@ -250,8 +275,9 @@ class Array
   #  +default+ value.
   #
   #  Alternatively, if a block is given it will only be executed when an
-  #  invalid +index+ is referenced.  Negative values of +index+ count from the
-  #  end of the array.
+  #  invalid +index+ is referenced.
+  #
+  #  Negative values of +index+ count from the end of the array.
   #
   #     a = [ 11, 22, 33, 44 ]
   #     a.fetch(1)               #=> 22
@@ -709,4 +735,31 @@ class Array
     end
     nil
   end
+
+  ##
+  #  call-seq:
+  #     ary.to_ary -> ary
+  #
+  #  Returns +self+.
+  #
+  def to_ary
+    self
+  end
+
+  ##
+  # call-seq:
+  #   ary.dig(idx, ...)                 -> object
+  #
+  # Extracts the nested value specified by the sequence of <i>idx</i>
+  # objects by calling +dig+ at each step, returning +nil+ if any
+  # intermediate step is +nil+.
+  #
+  def dig(idx,*args)
+    n = self[idx]
+    if args.size > 0
+      n&.dig(*args)
+    else
+      n
+    end
+  end
 end
index 177dd71..72e5080 100644 (file)
@@ -1,8 +1,8 @@
-#include "mruby.h"
-#include "mruby/value.h"
-#include "mruby/array.h"
-#include "mruby/range.h"
-#include "mruby/hash.h"
+#include <mruby.h>
+#include <mruby/value.h>
+#include <mruby/array.h>
+#include <mruby/range.h>
+#include <mruby/hash.h>
 
 /*
  *  call-seq:
@@ -131,7 +131,7 @@ mrb_ary_to_h(mrb_state *mrb, mrb_value ary)
 
     if (mrb_nil_p(v)) {
       mrb_raisef(mrb, E_TYPE_ERROR, "wrong element type %S at %S (expected array)",
-        mrb_str_new_cstr(mrb,  mrb_obj_classname(mrb, RARRAY_PTR(ary)[i])),
+        mrb_str_new_cstr(mrb,  mrb_obj_classname(mrb, ary_elt(ary, i))),
         mrb_fixnum_value(i)
       );
     }
@@ -149,6 +149,81 @@ mrb_ary_to_h(mrb_state *mrb, mrb_value ary)
   return hash;
 }
 
+/*
+ *  call-seq:
+ *     ary.slice!(index)         -> obj or nil
+ *     ary.slice!(start, length) -> new_ary or nil
+ *     ary.slice!(range)         -> new_ary or nil
+ *
+ *  Deletes the element(s) given by an +index+ (optionally up to +length+
+ *  elements) or by a +range+.
+ *
+ *  Returns the deleted object (or objects), or +nil+ if the +index+ is out of
+ *  range.
+ *
+ *     a = [ "a", "b", "c" ]
+ *     a.slice!(1)     #=> "b"
+ *     a               #=> ["a", "c"]
+ *     a.slice!(-1)    #=> "c"
+ *     a               #=> ["a"]
+ *     a.slice!(100)   #=> nil
+ *     a               #=> ["a"]
+ */
+
+static mrb_value
+mrb_ary_slice_bang(mrb_state *mrb, mrb_value self)
+{
+  struct RArray *a = mrb_ary_ptr(self);
+  mrb_int i, j, k, len, alen = ARY_LEN(a);
+  mrb_value index;
+  mrb_value val;
+  mrb_value *ptr;
+  mrb_value ary;
+
+  mrb_ary_modify(mrb, a);
+
+  if (mrb_get_args(mrb, "o|i", &index, &len) == 1) {
+    switch (mrb_type(index)) {
+    case MRB_TT_RANGE:
+      if (mrb_range_beg_len(mrb, index, &i, &len, alen, TRUE) == 1) {
+        goto delete_pos_len;
+      }
+      else {
+        return mrb_nil_value();
+      }
+    case MRB_TT_FIXNUM:
+      val = mrb_funcall(mrb, self, "delete_at", 1, index);
+      return val;
+    default:
+      val = mrb_funcall(mrb, self, "delete_at", 1, index);
+      return val;
+    }
+  }
+
+  i = mrb_fixnum(index);
+ delete_pos_len:
+  if (i < 0) i += alen;
+  if (i < 0 || alen < i) return mrb_nil_value();
+  if (len < 0) return mrb_nil_value();
+  if (alen == i) return mrb_ary_new(mrb);
+  if (len > alen - i) len = alen - i;
+
+  ary = mrb_ary_new_capa(mrb, len);
+  ptr = ARY_PTR(a);
+  for (j = i, k = 0; k < len; ++j, ++k) {
+    mrb_ary_push(mrb, ary, ptr[j]);
+  }
+
+  ptr += i;
+  for (j = i; j < alen - len; ++j) {
+    *ptr = *(ptr+len);
+    ++ptr;
+  }
+
+  mrb_ary_resize(mrb, self, alen - len);
+  return ary;
+}
+
 void
 mrb_mruby_array_ext_gem_init(mrb_state* mrb)
 {
@@ -159,6 +234,7 @@ mrb_mruby_array_ext_gem_init(mrb_state* mrb)
   mrb_define_method(mrb, a, "rassoc", mrb_ary_rassoc, MRB_ARGS_REQ(1));
   mrb_define_method(mrb, a, "values_at", mrb_ary_values_at, MRB_ARGS_ANY());
   mrb_define_method(mrb, a, "to_h",   mrb_ary_to_h, MRB_ARGS_REQ(0));
+  mrb_define_method(mrb, a, "slice!", mrb_ary_slice_bang,   MRB_ARGS_ANY());
 }
 
 void
index 6c2f523..95a796c 100644 (file)
@@ -1,6 +1,13 @@
 ##
 # Array(Ext) Test
 
+assert("Array.try_convert") do
+  assert_nil Array.try_convert(0)
+  assert_nil Array.try_convert(nil)
+  assert_equal [], Array.try_convert([])
+  assert_equal [1,2,3], Array.try_convert([1,2,3])
+end
+
 assert("Array#assoc") do
   s1 = [ "colors", "red", "blue", "green" ]
   s2 = [ "letters", "a", "b", "c" ]
@@ -294,7 +301,54 @@ assert('Array#to_h') do
   assert_raise(ArgumentError) { [[1]].to_h }
 end
 
+assert('Array#to_h (Modified)') do
+  class A
+    def to_ary
+      $a.clear
+      nil
+    end
+  end
+  $a = [A.new]
+  assert_raise(TypeError) { $a.to_h }
+end
+
 assert("Array#index (block)") do
   assert_nil (1..10).to_a.index { |i| i % 5 == 0 and i % 7 == 0 }
   assert_equal 34, (1..100).to_a.index { |i| i % 5 == 0 and i % 7 == 0 }
 end
+
+assert("Array#to_ary") do
+  assert_equal [], [].to_ary
+  assert_equal [1,2,3], [1,2,3].to_ary
+end
+
+assert("Array#dig") do
+  h = [[[1]], 0]
+  assert_equal(1, h.dig(0, 0, 0))
+  assert_nil(h.dig(2, 0))
+  assert_raise(TypeError) {h.dig(:a)}
+end
+
+assert("Array#slice!") do
+  a = [1, 2, 3]
+  b = a.slice!(0)
+  c = [1, 2, 3, 4, 5]
+  d = c.slice!(0, 2)
+  e = [1, 2, 3, 4, 5]
+  f = e.slice!(1..3)
+  g = [1, 2, 3]
+  h = g.slice!(-1)
+  i = [1, 2, 3]
+  j = i.slice!(0, -1)
+
+  assert_equal(a, [2, 3])
+  assert_equal(b, 1)
+  assert_equal(c, [3, 4, 5])
+  assert_equal(d, [1, 2])
+  assert_equal(e, [1, 5])
+  assert_equal(f, [2, 3, 4])
+  assert_equal(g, [1, 2])
+  assert_equal(h, 3)
+  assert_equal(i, [1, 2, 3])
+  assert_equal(j, nil)
+end
old mode 100755 (executable)
new mode 100644 (file)
index ae40f0a..26f3138
@@ -14,7 +14,7 @@ class BinTest_MrubyBinDebugger
 
     # compile
     `./bin/mrbc -g -o "#{bin.path}" "#{script.path}"`
-    
+
     # add mrdb quit
     testcase << {:cmd=>"quit"}
 
old mode 100755 (executable)
new mode 100644 (file)
index e9d85f3..1bc96c4
@@ -13,7 +13,7 @@ class BinTest_MrubyBinDebugger
 
     # compile
     `./bin/mrbc -g -o "#{bin.path}" "#{script.path}"`
-    
+
     # add mrdb quit
     testcase << {:cmd=>"quit"}
 
@@ -341,8 +341,8 @@ assert('mruby-bin-debugger(print) Literal:Numeric') do
   tc << {:cmd=>"p +0100",   :exp=>'$3 = 64'}
   tc << {:cmd=>"p 0x100",   :exp=>'$4 = 256'}
   tc << {:cmd=>"p 1_234",   :exp=>'$5 = 1234'}
-  tc << {:cmd=>"p 0b1000_0000", :exp=>"$6 = #{0b1000_0000.to_s}"}
-  tc << {:cmd=>"p 0x1000_0000", :exp=>"$7 = #{0x1000_0000.to_s}"}
+  tc << {:cmd=>"p 0b1000_0000", :exp=>"$6 = #{0b1000_0000}"}
+  tc << {:cmd=>"p 0x1000_0000", :exp=>"$7 = #{0x1000_0000}"}
 
   tc << {:cmd=>"p 3.14",    :exp=>'$8 = 3.14'}
   tc << {:cmd=>"p -12.3",   :exp=>'$9 = -12.3'}
old mode 100755 (executable)
new mode 100644 (file)
index 378c773..4553a96
@@ -4,14 +4,14 @@
 */
 
 #include <string.h>
-#include "mruby.h"
-#include "mruby/irep.h"
+#include <mruby.h>
+#include <mruby/irep.h>
 #include "mrdb.h"
-#include "mruby/debug.h"
-#include "mruby/opcode.h"
-#include "mruby/class.h"
-#include "mruby/proc.h"
-#include "mruby/variable.h"
+#include <mruby/debug.h>
+#include <mruby/opcode.h>
+#include <mruby/class.h>
+#include <mruby/proc.h>
+#include <mruby/variable.h>
 #include "mrdberror.h"
 #include "apibreak.h"
 
 #define MRB_DEBUG_BP_LINENO_OK (0x0002)
 
 static uint16_t
-check_lineno( mrb_irep_debug_info_file *info_file, uint16_t lineno )
+check_lineno(mrb_irep_debug_info_file *info_file, uint16_t lineno)
 {
   uint32_t count = info_file->line_entry_count;
   uint16_t l_idx;
 
-  if( info_file->line_type == mrb_debug_line_ary ) {
+  if (info_file->line_type == mrb_debug_line_ary) {
     for (l_idx = 0; l_idx < count; ++l_idx) {
-      if(lineno == info_file->lines.ary[l_idx]) {
+      if (lineno == info_file->lines.ary[l_idx]) {
         return lineno;
       }
     }
-  } else {
+  }
+  else {
     for (l_idx = 0; l_idx < count; ++l_idx) {
-      if(lineno == info_file->lines.flat_map[l_idx].line) {
+      if (lineno == info_file->lines.flat_map[l_idx].line) {
         return lineno;
       }
     }
@@ -43,21 +44,21 @@ check_lineno( mrb_irep_debug_info_file *info_file, uint16_t lineno )
 }
 
 static int32_t
-get_break_index( mrb_debug_context *dbg, int32_t bpno )
+get_break_index(mrb_debug_context *dbg, uint32_t bpno)
 {
   uint32_t i;
   int32_t index;
   char hit = FALSE;
 
   for(i = 0 ; i < dbg->bpnum; i++) {
-    if(dbg->bp[i].bpno == bpno) {
+    if (dbg->bp[i].bpno == bpno) {
       hit = TRUE;
       index = i;
       break;
     }
   }
 
-  if(hit == FALSE) {
+  if (hit == FALSE) {
     return MRB_DEBUG_BREAK_INVALID_NO;
   }
 
@@ -65,7 +66,7 @@ get_break_index( mrb_debug_context *dbg, int32_t bpno )
 }
 
 static void
-free_breakpoint( mrb_state *mrb, mrb_debug_breakpoint *bp )
+free_breakpoint(mrb_state *mrb, mrb_debug_breakpoint *bp)
 {
   switch(bp->type) {
     case MRB_DEBUG_BPTYPE_LINE:
@@ -73,7 +74,7 @@ free_breakpoint( mrb_state *mrb, mrb_debug_breakpoint *bp )
       break;
     case MRB_DEBUG_BPTYPE_METHOD:
       mrb_free(mrb, (void*)bp->point.methodpoint.method_name);
-      if(bp->point.methodpoint.class_name != NULL) {
+      if (bp->point.methodpoint.class_name != NULL) {
         mrb_free(mrb, (void*)bp->point.methodpoint.class_name);
       }
       break;
@@ -83,7 +84,7 @@ free_breakpoint( mrb_state *mrb, mrb_debug_breakpoint *bp )
 }
 
 static uint16_t
-check_file_lineno( struct mrb_irep *irep, const char *file, uint16_t lineno )
+check_file_lineno(struct mrb_irep *irep, const char *file, uint16_t lineno)
 {
   mrb_irep_debug_info_file *info_file;
   uint16_t result = 0;
@@ -93,17 +94,17 @@ check_file_lineno( struct mrb_irep *irep, const char *file, uint16_t lineno )
 
   for (f_idx = 0; f_idx < irep->debug_info->flen; ++f_idx) {
     info_file = irep->debug_info->files[f_idx];
-    if(!strcmp(info_file->filename, file)) {
+    if (!strcmp(info_file->filename, file)) {
       result = MRB_DEBUG_BP_FILE_OK;
 
-      fix_lineno = check_lineno( info_file, lineno );
-      if(fix_lineno != 0) { 
+      fix_lineno = check_lineno(info_file, lineno);
+      if (fix_lineno != 0) {
         return result | MRB_DEBUG_BP_LINENO_OK;
       }
     }
-    for ( i=0; i < irep->rlen; ++i ) {
+    for (i=0; i < irep->rlen; ++i) {
       result  |= check_file_lineno(irep->reps[i], file, lineno);
-      if(result == (MRB_DEBUG_BP_FILE_OK | MRB_DEBUG_BP_LINENO_OK)) {
+      if (result == (MRB_DEBUG_BP_FILE_OK | MRB_DEBUG_BP_LINENO_OK)) {
         return result;
       }
     }
@@ -112,7 +113,7 @@ check_file_lineno( struct mrb_irep *irep, const char *file, uint16_t lineno )
 }
 
 static const char*
-get_class_name( mrb_state *mrb, struct RClass *class_obj )
+get_class_name(mrb_state *mrb, struct RClass *class_obj)
 {
   struct RClass *outer;
   mrb_sym class_sym;
@@ -123,7 +124,7 @@ get_class_name( mrb_state *mrb, struct RClass *class_obj )
 }
 
 static int32_t
-compare_break_method( mrb_state *mrb, mrb_debug_breakpoint *bp, struct RClass *class_obj, mrb_sym method_sym, mrb_bool* isCfunc )
+compare_break_method(mrb_state *mrb, mrb_debug_breakpoint *bp, struct RClass *class_obj, mrb_sym method_sym, mrb_bool* isCfunc)
 {
   const char* class_name;
   const char* method_name;
@@ -137,37 +138,37 @@ compare_break_method( mrb_state *mrb, mrb_debug_breakpoint *bp, struct RClass *c
   method_name = mrb_sym2name(mrb, method_sym);
 
   method_p = &bp->point.methodpoint;
-  if(strcmp(method_p->method_name, method_name) == 0) {
+  if (strcmp(method_p->method_name, method_name) == 0) {
     class_name = get_class_name(mrb, class_obj);
-    if(class_name == NULL) {
-      if(method_p->class_name == NULL) {
+    if (class_name == NULL) {
+      if (method_p->class_name == NULL) {
         return bp->bpno;
       }
     }
-    else if(method_p->class_name != NULL) {
+    else if (method_p->class_name != NULL) {
       m = mrb_method_search_vm(mrb, &class_obj, method_sym);
-      if(m == NULL) {
+      if (m == NULL) {
         return MRB_DEBUG_OK;
       }
-      if(MRB_PROC_CFUNC_P(m)) {
+      if (MRB_PROC_CFUNC_P(m)) {
         *isCfunc = TRUE;
       }
 
       is_defined = mrb_class_defined(mrb, method_p->class_name);
-      if(is_defined == FALSE) {
+      if (is_defined == FALSE) {
         return MRB_DEBUG_OK;
       }
 
       sc = mrb_class_get(mrb, method_p->class_name);
       ssym = mrb_symbol(mrb_check_intern_cstr(mrb, method_p->method_name));
       m = mrb_method_search_vm(mrb, &sc, ssym);
-      if(m == NULL) {
+      if (m == NULL) {
         return MRB_DEBUG_OK;
       }
 
       class_name = get_class_name(mrb, class_obj);
       sn = get_class_name(mrb, sc);
-      if(strcmp(sn, class_name) == 0) {
+      if (strcmp(sn, class_name) == 0) {
         return bp->bpno;
       }
     }
@@ -176,31 +177,32 @@ compare_break_method( mrb_state *mrb, mrb_debug_breakpoint *bp, struct RClass *c
 }
 
 int32_t
-mrb_debug_set_break_line( mrb_state *mrb, mrb_debug_context *dbg, const char *file, uint16_t lineno)
+mrb_debug_set_break_line(mrb_state *mrb, mrb_debug_context *dbg, const char *file, uint16_t lineno)
 {
   int32_t index;
   char* set_file;
   uint16_t result;
 
-  if((mrb == NULL)||(dbg == NULL)||(file == NULL)) {
+  if ((mrb == NULL)||(dbg == NULL)||(file == NULL)) {
     return MRB_DEBUG_INVALID_ARGUMENT;
   }
 
-  if(dbg->bpnum >= MAX_BREAKPOINT) {
+  if (dbg->bpnum >= MAX_BREAKPOINT) {
     return MRB_DEBUG_BREAK_NUM_OVER;
   }
 
-  if(dbg->next_bpno > MAX_BREAKPOINTNO) {
+  if (dbg->next_bpno > MAX_BREAKPOINTNO) {
     return MRB_DEBUG_BREAK_NO_OVER;
   }
 
   /* file and lineno check (line type mrb_debug_line_ary only.) */
-  result = check_file_lineno( dbg->root_irep, file, lineno );
-  if(result == 0) {
+  result = check_file_lineno(dbg->root_irep, file, lineno);
+  if (result == 0) {
     return MRB_DEBUG_BREAK_INVALID_FILE;
-  }else if(result == MRB_DEBUG_BP_FILE_OK) {
+  }
+  else if (result == MRB_DEBUG_BP_FILE_OK) {
     return MRB_DEBUG_BREAK_INVALID_LINENO;
-  } 
+  }
 
   set_file = mrb_malloc(mrb, strlen(file) + 1);
 
@@ -220,25 +222,25 @@ mrb_debug_set_break_line( mrb_state *mrb, mrb_debug_context *dbg, const char *fi
 }
 
 int32_t
-mrb_debug_set_break_method( mrb_state *mrb, mrb_debug_context *dbg, const char *class_name, const char *method_name )
+mrb_debug_set_break_method(mrb_state *mrb, mrb_debug_context *dbg, const char *class_name, const char *method_name)
 {
   int32_t index;
   char* set_class;
   char* set_method;
 
-  if((mrb == NULL) || (dbg == NULL) || (method_name == NULL)) {
+  if ((mrb == NULL) || (dbg == NULL) || (method_name == NULL)) {
     return MRB_DEBUG_INVALID_ARGUMENT;
   }
 
-  if(dbg->bpnum >= MAX_BREAKPOINT) {
+  if (dbg->bpnum >= MAX_BREAKPOINT) {
     return MRB_DEBUG_BREAK_NUM_OVER;
   }
 
-  if(dbg->next_bpno > MAX_BREAKPOINTNO) {
+  if (dbg->next_bpno > MAX_BREAKPOINTNO) {
     return MRB_DEBUG_BREAK_NO_OVER;
   }
 
-  if(class_name != NULL) {
+  if (class_name != NULL) {
     set_class = mrb_malloc(mrb, strlen(class_name) + 1);
     strncpy(set_class, class_name, strlen(class_name) + 1);
   }
@@ -263,25 +265,25 @@ mrb_debug_set_break_method( mrb_state *mrb, mrb_debug_context *dbg, const char *
 }
 
 int32_t
-mrb_debug_get_breaknum( mrb_state *mrb, mrb_debug_context *dbg )
+mrb_debug_get_breaknum(mrb_state *mrb, mrb_debug_context *dbg)
 {
-  if((mrb == NULL) || (dbg == NULL)) {
+  if ((mrb == NULL) || (dbg == NULL)) {
     return MRB_DEBUG_INVALID_ARGUMENT;
   }
 
   return dbg->bpnum;
 }
 
-int32_t 
-mrb_debug_get_break_all( mrb_state *mrb, mrb_debug_context *dbg, uint32_t size, mrb_debug_breakpoint *bp )
+int32_t
+mrb_debug_get_break_all(mrb_state *mrb, mrb_debug_context *dbg, uint32_t size, mrb_debug_breakpoint *bp)
 {
   uint32_t get_size = 0;
 
-  if((mrb == NULL) || (dbg == NULL) || (bp == NULL)) {
+  if ((mrb == NULL) || (dbg == NULL) || (bp == NULL)) {
     return MRB_DEBUG_INVALID_ARGUMENT;
   }
 
-  if(dbg->bpnum >= size) {
+  if (dbg->bpnum >= size) {
     get_size = size;
   }
   else {
@@ -294,16 +296,16 @@ mrb_debug_get_break_all( mrb_state *mrb, mrb_debug_context *dbg, uint32_t size,
 }
 
 int32_t
-mrb_debug_get_break( mrb_state *mrb, mrb_debug_context *dbg, uint32_t bpno, mrb_debug_breakpoint *bp )
+mrb_debug_get_break(mrb_state *mrb, mrb_debug_context *dbg, uint32_t bpno, mrb_debug_breakpoint *bp)
 {
-  uint32_t index;
+  int32_t index;
 
-  if((mrb == NULL) || (dbg == NULL) || (bp == NULL)) {
+  if ((mrb == NULL) || (dbg == NULL) || (bp == NULL)) {
     return MRB_DEBUG_INVALID_ARGUMENT;
   }
 
   index = get_break_index(dbg, bpno);
-  if(index == MRB_DEBUG_BREAK_INVALID_NO) {
+  if (index == MRB_DEBUG_BREAK_INVALID_NO) {
     return MRB_DEBUG_BREAK_INVALID_NO;
   }
 
@@ -315,25 +317,25 @@ mrb_debug_get_break( mrb_state *mrb, mrb_debug_context *dbg, uint32_t bpno, mrb_
   return 0;
 }
 
-int32_t 
-mrb_debug_delete_break( mrb_state *mrb, mrb_debug_context *dbg, uint32_t bpno )
+int32_t
+mrb_debug_delete_break(mrb_state *mrb, mrb_debug_context *dbg, uint32_t bpno)
 {
   uint32_t i;
   int32_t index;
 
-  if((mrb == NULL) ||(dbg == NULL)) {
+  if ((mrb == NULL) ||(dbg == NULL)) {
     return MRB_DEBUG_INVALID_ARGUMENT;
   }
 
   index = get_break_index(dbg, bpno);
-  if(index == MRB_DEBUG_BREAK_INVALID_NO) {
+  if (index == MRB_DEBUG_BREAK_INVALID_NO) {
     return MRB_DEBUG_BREAK_INVALID_NO;
   }
 
   free_breakpoint(mrb, &dbg->bp[index]);
 
   for(i = index ; i < dbg->bpnum; i++) {
-    if((i + 1) == dbg->bpnum) {
+    if ((i + 1) == dbg->bpnum) {
       memset(&dbg->bp[i], 0, sizeof(mrb_debug_breakpoint));
     }
     else {
@@ -346,12 +348,12 @@ mrb_debug_delete_break( mrb_state *mrb, mrb_debug_context *dbg, uint32_t bpno )
   return MRB_DEBUG_OK;
 }
 
-int32_t 
-mrb_debug_delete_break_all( mrb_state *mrb, mrb_debug_context *dbg )
+int32_t
+mrb_debug_delete_break_all(mrb_state *mrb, mrb_debug_context *dbg)
 {
   uint32_t i;
 
-  if((mrb == NULL) || (dbg == NULL)) {
+  if ((mrb == NULL) || (dbg == NULL)) {
     return MRB_DEBUG_INVALID_ARGUMENT;
   }
 
@@ -364,17 +366,17 @@ mrb_debug_delete_break_all( mrb_state *mrb, mrb_debug_context *dbg )
   return MRB_DEBUG_OK;
 }
 
-int32_t 
-mrb_debug_enable_break( mrb_state *mrb, mrb_debug_context *dbg, uint32_t bpno )
+int32_t
+mrb_debug_enable_break(mrb_state *mrb, mrb_debug_context *dbg, uint32_t bpno)
 {
   int32_t index = 0;
 
-  if((mrb == NULL) || (dbg == NULL)) {
+  if ((mrb == NULL) || (dbg == NULL)) {
     return MRB_DEBUG_INVALID_ARGUMENT;
   }
 
   index = get_break_index(dbg, bpno);
-  if(index == MRB_DEBUG_BREAK_INVALID_NO) {
+  if (index == MRB_DEBUG_BREAK_INVALID_NO) {
     return MRB_DEBUG_BREAK_INVALID_NO;
   }
 
@@ -384,11 +386,11 @@ mrb_debug_enable_break( mrb_state *mrb, mrb_debug_context *dbg, uint32_t bpno )
 }
 
 int32_t
-mrb_debug_enable_break_all( mrb_state *mrb, mrb_debug_context *dbg )
+mrb_debug_enable_break_all(mrb_state *mrb, mrb_debug_context *dbg)
 {
   uint32_t i;
 
-  if((mrb == NULL) || (dbg == NULL)) {
+  if ((mrb == NULL) || (dbg == NULL)) {
     return MRB_DEBUG_INVALID_ARGUMENT;
   }
 
@@ -399,17 +401,17 @@ mrb_debug_enable_break_all( mrb_state *mrb, mrb_debug_context *dbg )
   return MRB_DEBUG_OK;
 }
 
-int32_t 
-mrb_debug_disable_break( mrb_state *mrb, mrb_debug_context *dbg, uint32_t bpno )
+int32_t
+mrb_debug_disable_break(mrb_state *mrb, mrb_debug_context *dbg, uint32_t bpno)
 {
   int32_t index = 0;
 
-  if((mrb == NULL) || (dbg == NULL)) {
+  if ((mrb == NULL) || (dbg == NULL)) {
     return MRB_DEBUG_INVALID_ARGUMENT;
   }
 
   index = get_break_index(dbg, bpno);
-  if(index == MRB_DEBUG_BREAK_INVALID_NO) {
+  if (index == MRB_DEBUG_BREAK_INVALID_NO) {
     return MRB_DEBUG_BREAK_INVALID_NO;
   }
 
@@ -418,12 +420,12 @@ mrb_debug_disable_break( mrb_state *mrb, mrb_debug_context *dbg, uint32_t bpno )
   return MRB_DEBUG_OK;
 }
 
-int32_t 
-mrb_debug_disable_break_all( mrb_state *mrb, mrb_debug_context *dbg )
+int32_t
+mrb_debug_disable_break_all(mrb_state *mrb, mrb_debug_context *dbg)
 {
   uint32_t i;
 
-  if((mrb == NULL) || (dbg == NULL)) {
+  if ((mrb == NULL) || (dbg == NULL)) {
     return MRB_DEBUG_INVALID_ARGUMENT;
   }
 
@@ -435,10 +437,10 @@ mrb_debug_disable_break_all( mrb_state *mrb, mrb_debug_context *dbg )
 }
 
 static mrb_bool
-check_start_pc_for_line( mrb_irep *irep, mrb_code *pc, uint16_t line )
+check_start_pc_for_line(mrb_irep *irep, mrb_code *pc, uint16_t line)
 {
-  if( pc > irep->iseq ) {
-    ifline == mrb_debug_get_line(irep, (uint32_t)(pc - irep->iseq - 1))) {
+  if (pc > irep->iseq) {
+    if (line == mrb_debug_get_line(irep, (uint32_t)(pc - irep->iseq - 1))) {
       return FALSE;
     }
   }
@@ -446,17 +448,17 @@ check_start_pc_for_line( mrb_irep *irep, mrb_code *pc, uint16_t line )
 }
 
 int32_t
-mrb_debug_check_breakpoint_line( mrb_state *mrb, mrb_debug_context *dbg, const char *file, uint16_t line )
+mrb_debug_check_breakpoint_line(mrb_state *mrb, mrb_debug_context *dbg, const char *file, uint16_t line)
 {
   mrb_debug_breakpoint *bp;
   mrb_debug_linepoint *line_p;
-  int i;
+  uint32_t i;
 
-  if((mrb == NULL) || (dbg == NULL) || (file == NULL) || (line <= 0)) {
+  if ((mrb == NULL) || (dbg == NULL) || (file == NULL) || (line <= 0)) {
     return MRB_DEBUG_INVALID_ARGUMENT;
   }
 
-  if(!check_start_pc_for_line(dbg->irep, dbg->pc, line)) {
+  if (!check_start_pc_for_line(dbg->irep, dbg->pc, line)) {
     return MRB_DEBUG_OK;
   }
 
@@ -464,9 +466,9 @@ mrb_debug_check_breakpoint_line( mrb_state *mrb, mrb_debug_context *dbg, const c
   for(i=0; i<dbg->bpnum; i++) {
     switch (bp->type) {
       case MRB_DEBUG_BPTYPE_LINE:
-        if(bp->enable == TRUE) {
+        if (bp->enable == TRUE) {
           line_p = &bp->point.linepoint;
-          if((strcmp(line_p->file, file) == 0) && (line_p->lineno == line)) {
+          if ((strcmp(line_p->file, file) == 0) && (line_p->lineno == line)) {
             return bp->bpno;
           }
         }
@@ -483,28 +485,28 @@ mrb_debug_check_breakpoint_line( mrb_state *mrb, mrb_debug_context *dbg, const c
 }
 
 
-int32_t 
-mrb_debug_check_breakpoint_method( mrb_state *mrb, mrb_debug_context *dbg, struct RClass *class_obj, mrb_sym method_sym, mrb_bool* isCfunc )
+int32_t
+mrb_debug_check_breakpoint_method(mrb_state *mrb, mrb_debug_context *dbg, struct RClass *class_obj, mrb_sym method_sym, mrb_bool* isCfunc)
 {
   mrb_debug_breakpoint *bp;
   int32_t bpno;
-  int i;
+  uint32_t i;
 
-  if((mrb == NULL) || (dbg == NULL) || (class_obj == NULL)) {
+  if ((mrb == NULL) || (dbg == NULL) || (class_obj == NULL)) {
     return MRB_DEBUG_INVALID_ARGUMENT;
   }
 
   bp = dbg->bp;
   for(i=0; i<dbg->bpnum; i++) {
-    if(bp->type == MRB_DEBUG_BPTYPE_METHOD) {
-      if(bp->enable == TRUE) {
+    if (bp->type == MRB_DEBUG_BPTYPE_METHOD) {
+      if (bp->enable == TRUE) {
         bpno = compare_break_method(mrb, bp, class_obj, method_sym, isCfunc);
-        if(bpno > 0) {
+        if (bpno > 0) {
           return bpno;
         }
       }
     }
-    else if(bp->type == MRB_DEBUG_BPTYPE_NONE) {
+    else if (bp->type == MRB_DEBUG_BPTYPE_NONE) {
       break;
     }
     bp++;
old mode 100755 (executable)
new mode 100644 (file)
index 8bb39cb..08f1d80
@@ -6,21 +6,21 @@
 #ifndef APIBREAK_H_
 #define APIBREAK_H_
 
-#include "mruby.h"
+#include <mruby.h>
 #include "mrdb.h"
 
-int32_t mrb_debug_set_break_line( mrb_state *, mrb_debug_context *, const char *, uint16_t );
-int32_t mrb_debug_set_break_method( mrb_state *, mrb_debug_context *, const char *, const char * );
-int32_t mrb_debug_get_breaknum( mrb_state *, mrb_debug_context * );
-int32_t mrb_debug_get_break_all( mrb_state *, mrb_debug_context *, uint32_t, mrb_debug_breakpoint bp[]);
-int32_t mrb_debug_get_break( mrb_state *, mrb_debug_context *, uint32_t, mrb_debug_breakpoint * );
-int32_t mrb_debug_delete_break( mrb_state *, mrb_debug_context *, uint32_t );
-int32_t mrb_debug_delete_break_all( mrb_state *, mrb_debug_context * );
-int32_t mrb_debug_enable_break( mrb_state *, mrb_debug_context *, uint32_t );
-int32_t mrb_debug_enable_break_all( mrb_state *, mrb_debug_context * );
-int32_t mrb_debug_disable_break( mrb_state *, mrb_debug_context *, uint32_t );
-int32_t mrb_debug_disable_break_all( mrb_state *, mrb_debug_context * );
-int32_t mrb_debug_check_breakpoint_line( mrb_state *, mrb_debug_context *, const char *, uint16_t );
-int32_t mrb_debug_check_breakpoint_method( mrb_state *, mrb_debug_context *, struct RClass *, mrb_sym, mrb_bool* );
+int32_t mrb_debug_set_break_line(mrb_state *, mrb_debug_context *, const char *, uint16_t);
+int32_t mrb_debug_set_break_method(mrb_state *, mrb_debug_context *, const char *, const char *);
+int32_t mrb_debug_get_breaknum(mrb_state *, mrb_debug_context *);
+int32_t mrb_debug_get_break_all(mrb_state *, mrb_debug_context *, uint32_t, mrb_debug_breakpoint bp[]);
+int32_t mrb_debug_get_break(mrb_state *, mrb_debug_context *, uint32_t, mrb_debug_breakpoint *);
+int32_t mrb_debug_delete_break(mrb_state *, mrb_debug_context *, uint32_t);
+int32_t mrb_debug_delete_break_all(mrb_state *, mrb_debug_context *);
+int32_t mrb_debug_enable_break(mrb_state *, mrb_debug_context *, uint32_t);
+int32_t mrb_debug_enable_break_all(mrb_state *, mrb_debug_context *);
+int32_t mrb_debug_disable_break(mrb_state *, mrb_debug_context *, uint32_t);
+int32_t mrb_debug_disable_break_all(mrb_state *, mrb_debug_context *);
+int32_t mrb_debug_check_breakpoint_line(mrb_state *, mrb_debug_context *, const char *, uint16_t);
+int32_t mrb_debug_check_breakpoint_method(mrb_state *, mrb_debug_context *, struct RClass *, mrb_sym, mrb_bool*);
 
 #endif /* APIBREAK_H_ */
old mode 100755 (executable)
new mode 100644 (file)
index 03846cd..33e9575
@@ -9,9 +9,9 @@
 #include "mrdb.h"
 #include "mrdberror.h"
 #include "apilist.h"
-#include "mruby/compile.h"
-#include "mruby/irep.h"
-#include "mruby/debug.h"
+#include <mruby/compile.h>
+#include <mruby/irep.h>
+#include <mruby/debug.h>
 
 #define LINE_BUF_SIZE MAX_COMMAND_LINE
 
@@ -71,7 +71,7 @@ dirname(mrb_state *mrb, const char *path)
   }
 
   p = strrchr(path, '/');
-  len = p != NULL ? p - path : strlen(path);
+  len = p != NULL ? (size_t)(p - path) : strlen(path);
 
   dir = mrb_malloc(mrb, len + 1);
   strncpy(dir, path, len);
old mode 100755 (executable)
new mode 100644 (file)
index 4580b23..6c41078
@@ -5,7 +5,7 @@
 #ifndef APILIST_H_
 #define APILIST_H_
 
-#include "mruby.h"
+#include <mruby.h>
 #include "mrdb.h"
 
 int32_t mrb_debug_list(mrb_state *, mrb_debug_context *, char *, uint16_t, uint16_t);
old mode 100755 (executable)
new mode 100644 (file)
index 2c93eb4..a9c895b
@@ -5,12 +5,12 @@
 
 #include <string.h>
 #include "mrdb.h"
-#include "mruby/value.h"
-#include "mruby/class.h"
-#include "mruby/compile.h"
-#include "mruby/error.h"
-#include "mruby/numeric.h"
-#include "mruby/string.h"
+#include <mruby/value.h>
+#include <mruby/class.h>
+#include <mruby/compile.h>
+#include <mruby/error.h>
+#include <mruby/numeric.h>
+#include <mruby/string.h>
 #include "apiprint.h"
 
 static void
old mode 100755 (executable)
new mode 100644 (file)
index 81d685e..e256f62
@@ -5,7 +5,7 @@
 #ifndef APIPRINT_H_
 #define APIPRINT_H_
 
-#include "mruby.h"
+#include <mruby.h>
 #include "mrdb.h"
 
 mrb_value mrb_debug_eval(mrb_state*, mrb_debug_context*, const char*, size_t, mrb_bool*);
old mode 100755 (executable)
new mode 100644 (file)
index b913915..6bbe4cf
@@ -5,10 +5,10 @@
 
 #include <ctype.h>
 #include <string.h>
-#include "mruby.h"
-#include "mruby/dump.h"
-#include "mruby/debug.h"
-#include "mruby/string.h"
+#include <mruby.h>
+#include <mruby/dump.h>
+#include <mruby/debug.h>
+#include <mruby/string.h>
 #include "mrdb.h"
 #include "mrdberror.h"
 #include "apibreak.h"
@@ -70,12 +70,12 @@ parse_breakpoint_no(char* args)
   char* ps = args;
   uint32_t l;
 
-  if((*ps == '0')||(strlen(ps) >= BPNO_LETTER_NUM)) {
+  if ((*ps == '0')||(strlen(ps) >= BPNO_LETTER_NUM)) {
     return 0;
   }
 
-  while( !(ISBLANK(*ps)||ISCNTRL(*ps)) ) {
-    if(!ISDIGIT(*ps)) {
+  while (!(ISBLANK(*ps)||ISCNTRL(*ps))) {
+    if (!ISDIGIT(*ps)) {
       return 0;
     }
     ps++;
@@ -90,7 +90,7 @@ exe_set_command_all(mrb_state *mrb, mrdb_state *mrdb, all_command_func func)
 {
   int32_t ret = MRB_DEBUG_OK;
 
-  if(mrdb->wcnt == 1) {
+  if (mrdb->wcnt == 1) {
     ret = func(mrb, mrdb->dbg);
     print_api_common_error(ret);
     return TRUE;
@@ -109,15 +109,15 @@ exe_set_command_select(mrb_state *mrb, mrdb_state *mrdb, select_command_func fun
   for(i=1; i<mrdb->wcnt; i++) {
     ps = mrdb->words[i];
     bpno = parse_breakpoint_no(ps);
-    if(bpno == 0) {
+    if (bpno == 0) {
       printf(BREAK_ERR_MSG_INVALIDBPNO, ps);
       break;
     }
     ret = func(mrb, mrdb->dbg, (uint32_t)bpno);
-    if(ret == MRB_DEBUG_BREAK_INVALID_NO) {
+    if (ret == MRB_DEBUG_BREAK_INVALID_NO) {
       printf(BREAK_ERR_MSG_NOBPNO, bpno);
     }
-    else if(ret != MRB_DEBUG_OK) {
+    else if (ret != MRB_DEBUG_OK) {
       print_api_common_error(ret);
     }
   }
@@ -128,24 +128,24 @@ check_bptype(char* args)
 {
   char* ps = args;
 
-  if(ISBLANK(*ps)||ISCNTRL(*ps)) {
+  if (ISBLANK(*ps)||ISCNTRL(*ps)) {
     puts(BREAK_ERR_MSG_BLANK);
     return MRB_DEBUG_BPTYPE_NONE;
   }
 
-  if(!ISDIGIT(*ps)) {
+  if (!ISDIGIT(*ps)) {
     return MRB_DEBUG_BPTYPE_METHOD;
   }
 
-  while( !(ISBLANK(*ps)||ISCNTRL(*ps)) ) {
-    if(!ISDIGIT(*ps)) {
+  while (!(ISBLANK(*ps)||ISCNTRL(*ps))) {
+    if (!ISDIGIT(*ps)) {
       printf(BREAK_ERR_MSG_INVALIDSTR, args);
       return MRB_DEBUG_BPTYPE_NONE;
     }
     ps++;
   }
 
-  if((*args == '0')||(strlen(args) >= LINENO_MAX_DIGIT)) {
+  if ((*args == '0')||(strlen(args) >= LINENO_MAX_DIGIT)) {
     puts(BREAK_ERR_MSG_RANGEOVER);
     return MRB_DEBUG_BPTYPE_NONE;
   }
@@ -158,12 +158,12 @@ print_breakpoint(mrb_debug_breakpoint *bp)
 {
   const char* enable_letter[] = {BREAK_INFO_MSG_DISABLE, BREAK_INFO_MSG_ENABLE};
 
-  if(bp->type == MRB_DEBUG_BPTYPE_LINE) {
+  if (bp->type == MRB_DEBUG_BPTYPE_LINE) {
     printf(BREAK_INFO_MSG_LINEBREAK,
       bp->bpno, enable_letter[bp->enable], bp->point.linepoint.file, bp->point.linepoint.lineno);
   }
   else {
-    if(bp->point.methodpoint.class_name == NULL) {
+    if (bp->point.methodpoint.class_name == NULL) {
       printf(BREAK_INFO_MSG_METHODBREAK_NOCLASS,
         bp->bpno, enable_letter[bp->enable], bp->point.methodpoint.method_name);
     }
@@ -183,18 +183,18 @@ info_break_all(mrb_state *mrb, mrdb_state *mrdb)
   mrb_debug_breakpoint *bp_list;
 
   bpnum = mrb_debug_get_breaknum(mrb, mrdb->dbg);
-  if(bpnum < 0) {
+  if (bpnum < 0) {
     print_api_common_error(bpnum);
     return;
   }
-  else if(bpnum == 0) {
+  else if (bpnum == 0) {
     puts(BREAK_ERR_MSG_NOBPNO_INFOALL);
     return;
   }
   bp_list = (mrb_debug_breakpoint*)mrb_malloc(mrb, bpnum * sizeof(mrb_debug_breakpoint));
 
   ret = mrb_debug_get_break_all(mrb, mrdb->dbg, (uint32_t)bpnum, bp_list);
-  if(ret < 0) {
+  if (ret < 0) {
     print_api_common_error(ret);
     return;
   }
@@ -219,21 +219,21 @@ info_break_select(mrb_state *mrb, mrdb_state *mrdb)
   for(i=2; i<mrdb->wcnt; i++) {
     ps = mrdb->words[i];
     bpno = parse_breakpoint_no(ps);
-    if(bpno == 0) {
+    if (bpno == 0) {
       puts(BREAK_ERR_MSG_INVALIDBPNO_INFO);
       break;
     }
 
     ret = mrb_debug_get_break(mrb, mrdb->dbg, bpno, &bp);
-    if(ret == MRB_DEBUG_BREAK_INVALID_NO) {
+    if (ret == MRB_DEBUG_BREAK_INVALID_NO) {
       printf(BREAK_ERR_MSG_NOBPNO_INFO, bpno);
       break;
     }
-    else if(ret != MRB_DEBUG_OK) {
+    else if (ret != MRB_DEBUG_OK) {
       print_api_common_error(ret);
       break;
     }
-    else if(isFirst == TRUE) {
+    else if (isFirst == TRUE) {
       isFirst = FALSE;
       puts(BREAK_INFO_MSG_HEADER);
     }
@@ -250,17 +250,18 @@ parse_breakcommand(mrdb_state *mrdb, const char **file, uint32_t *line, char **c
   mrb_debug_bptype type;
   uint32_t l;
 
-  if(mrdb->wcnt <= 1) {
+  if (mrdb->wcnt <= 1) {
     puts(BREAK_ERR_MSG_BLANK);
     return MRB_DEBUG_BPTYPE_NONE;
   }
 
   args = mrdb->words[1];
-  if((body = strrchr(args, ':')) == NULL) {
+  if ((body = strrchr(args, ':')) == NULL) {
     body = args;
     type = check_bptype(body);
-  } else {
-    if(body == args) {
+  }
+  else {
+    if (body == args) {
       printf(BREAK_ERR_MSG_INVALIDSTR, args);
       return MRB_DEBUG_BPTYPE_NONE;
     }
@@ -271,26 +272,29 @@ parse_breakcommand(mrdb_state *mrdb, const char **file, uint32_t *line, char **c
   switch(type) {
     case MRB_DEBUG_BPTYPE_LINE:
       STRTOUL(l, body);
-      if( l <= 65535 ) {
+      if (l <= 65535) {
         *line = l;
         *file = (body == args)? mrb_debug_get_filename(dbg->irep, (uint32_t)(dbg->pc - dbg->irep->iseq)): args;
-      } else {
+      }
+      else {
         puts(BREAK_ERR_MSG_RANGEOVER);
         type = MRB_DEBUG_BPTYPE_NONE;
       }
       break;
     case MRB_DEBUG_BPTYPE_METHOD:
-      if(body == args) {
+      if (body == args) {
         /* method only */
-        if( ISUPPER(*body)||ISLOWER(*body)||(*body == '_') ) {
+        if (ISUPPER(*body)||ISLOWER(*body)||(*body == '_')) {
           *method = body;
           *cname = NULL;
-        } else {
+        }
+        else {
           printf(BREAK_ERR_MSG_INVALIDMETHOD, args);
           type = MRB_DEBUG_BPTYPE_NONE;
         }
-      } else {
-        if( ISUPPER(*args) ) {
+      }
+      else {
+        if (ISUPPER(*args)) {
           switch(*body) {
             case '@': case '$': case '?': case '.': case ',': case ':':
             case ';': case '#': case '\\': case '\'': case '\"':
@@ -302,7 +306,8 @@ parse_breakcommand(mrdb_state *mrdb, const char **file, uint32_t *line, char **c
             *cname = args;
             break;
           }
-        } else {
+        }
+        else {
           printf(BREAK_ERR_MSG_INVALIDCLASS, args);
           type = MRB_DEBUG_BPTYPE_NONE;
         }
@@ -343,12 +348,15 @@ dbgcmd_break(mrb_state *mrb, mrdb_state *mrdb)
   if (ret >= 0) {
     if (type == MRB_DEBUG_BPTYPE_LINE) {
       printf(BREAK_SET_MSG_LINE, ret, file, line);
-    } else if ((type == MRB_DEBUG_BPTYPE_METHOD)&&(cname == NULL)) {
+    }
+    else if ((type == MRB_DEBUG_BPTYPE_METHOD)&&(cname == NULL)) {
       printf(BREAK_SET_MSG_METHOD, ret, method);
-    } else {
+    }
+    else {
       printf(BREAK_SET_MSG_CLASS_METHOD, ret, cname, method);
     }
-  } else {
+  }
+  else {
     switch (ret) {
       case MRB_DEBUG_BREAK_INVALID_LINENO:
         printf(BREAK_ERR_MSG_INVALIDLINENO, line, file);
@@ -379,7 +387,7 @@ dbgcmd_break(mrb_state *mrb, mrdb_state *mrdb)
 dbgcmd_state
 dbgcmd_info_break(mrb_state *mrb, mrdb_state *mrdb)
 {
-  if(mrdb->wcnt == 2) {
+  if (mrdb->wcnt == 2) {
     info_break_all(mrb, mrdb);
   }
   else {
@@ -395,7 +403,7 @@ dbgcmd_delete(mrb_state *mrb, mrdb_state *mrdb)
   mrb_bool ret = FALSE;
 
   ret = exe_set_command_all(mrb, mrdb, mrb_debug_delete_break_all);
-  if(ret != TRUE) {
+  if (ret != TRUE) {
     exe_set_command_select(mrb, mrdb, mrb_debug_delete_break);
   }
 
@@ -408,7 +416,7 @@ dbgcmd_enable(mrb_state *mrb, mrdb_state *mrdb)
   mrb_bool ret = FALSE;
 
   ret = exe_set_command_all(mrb, mrdb, mrb_debug_enable_break_all);
-  if(ret != TRUE) {
+  if (ret != TRUE) {
     exe_set_command_select(mrb, mrdb, mrb_debug_enable_break);
   }
 
@@ -421,7 +429,7 @@ dbgcmd_disable(mrb_state *mrb, mrdb_state *mrdb)
   mrb_bool ret = FALSE;
 
   ret = exe_set_command_all(mrb, mrdb, mrb_debug_disable_break_all);
-  if(ret != TRUE) {
+  if (ret != TRUE) {
     exe_set_command_select(mrb, mrdb, mrb_debug_disable_break);
   }
   return DBGST_PROMPT;
old mode 100755 (executable)
new mode 100644 (file)
index f525148..5984b62
@@ -8,7 +8,7 @@
 #include <string.h>
 
 #include "apilist.h"
-#include "mruby/compile.h"
+#include <mruby/compile.h>
 
 typedef struct help_msg {
   const char *cmd1;
@@ -333,7 +333,7 @@ check_cmd_pattern(const char *pattern, const char *cmd)
   if (pattern == NULL || cmd == NULL) {
     return FALSE;
   }
-  if((lbracket = strchr(pattern, '[')) == NULL) {
+  if ((lbracket = strchr(pattern, '[')) == NULL) {
     return !strcmp(pattern, cmd);
   }
   if ((rbracket = strchr(pattern, ']')) == NULL) {
old mode 100755 (executable)
new mode 100644 (file)
index a617aff..cca6367
@@ -5,12 +5,12 @@
 
 #include <string.h>
 #include "mrdb.h"
-#include "mruby/value.h"
-#include "mruby/class.h"
-#include "mruby/compile.h"
-#include "mruby/error.h"
-#include "mruby/numeric.h"
-#include "mruby/string.h"
+#include <mruby/value.h>
+#include <mruby/class.h>
+#include <mruby/compile.h>
+#include <mruby/error.h>
+#include <mruby/numeric.h>
+#include <mruby/string.h>
 #include "apiprint.h"
 
 dbgcmd_state
old mode 100755 (executable)
new mode 100644 (file)
index 8d83405..cb4c738
@@ -3,7 +3,7 @@
 **
 */
 
-#include "mruby/opcode.h"
+#include <mruby/opcode.h>
 #include "mrdb.h"
 
 dbgcmd_state
@@ -11,18 +11,19 @@ dbgcmd_run(mrb_state *mrb, mrdb_state *mrdb)
 {
   mrb_debug_context *dbg = mrdb->dbg;
 
-  if( dbg->xm == DBG_INIT ){
+  if (dbg->xm == DBG_INIT){
     dbg->xm = DBG_RUN;
-  } else {
+  }
+  else {
     dbg->xm = DBG_QUIT;
-    if( dbg->xphase == DBG_PHASE_RUNNING ){
+    if (dbg->xphase == DBG_PHASE_RUNNING){
       struct RClass *exc;
       puts("Start it from the beginning.");
       exc = mrb_define_class(mrb, "DebuggerRestart", mrb_class_get(mrb, "Exception"));
       mrb_raise(mrb, exc, "Restart mrdb.");
     }
   }
-  
+
   return DBGST_RESTART;
 }
 
@@ -32,15 +33,16 @@ dbgcmd_continue(mrb_state *mrb, mrdb_state *mrdb)
   mrb_debug_context *dbg = mrdb->dbg;
   int ccnt = 1;
 
-  if( mrdb->wcnt > 1 ){
+  if (mrdb->wcnt > 1){
     sscanf(mrdb->words[1], "%d", &ccnt);
   }
   dbg->ccnt = (uint16_t)(ccnt > 0 ? ccnt : 1);  /* count of continue */
 
-  if( dbg->xphase == DBG_PHASE_AFTER_RUN ){
+  if (dbg->xphase == DBG_PHASE_AFTER_RUN){
     puts("The program is not running.");
     dbg->xm = DBG_QUIT;
-  } else {
+  }
+  else {
     dbg->xm = DBG_RUN;
   }
   return DBGST_CONTINUE;
@@ -52,3 +54,11 @@ dbgcmd_step(mrb_state *mrb, mrdb_state *mrdb)
   mrdb->dbg->xm = DBG_STEP;
   return DBGST_CONTINUE;
 }
+
+dbgcmd_state
+dbgcmd_next(mrb_state *mrb, mrdb_state *mrdb)
+{
+  mrdb->dbg->xm = DBG_NEXT;
+  mrdb->dbg->prvci = mrb->c->ci;
+  return DBGST_CONTINUE;
+}
old mode 100755 (executable)
new mode 100644 (file)
index 3e43fdb..3177d7a
@@ -8,12 +8,12 @@
 #include <stdio.h>
 #include <ctype.h>
 
-#include "mruby.h"
-#include "mruby/dump.h"
-#include "mruby/debug.h"
-#include "mruby/class.h"
-#include "mruby/opcode.h"
-#include "mruby/variable.h"
+#include <mruby.h>
+#include <mruby/dump.h>
+#include <mruby/debug.h>
+#include <mruby/class.h>
+#include <mruby/opcode.h>
+#include <mruby/variable.h>
 
 #include "mrdb.h"
 #include "apibreak.h"
@@ -57,6 +57,7 @@ static const debug_command debug_command_list[] = {
   {"quit",      NULL,           1, 0, 0, DBGCMD_QUIT,           dbgcmd_quit},            /* q[uit] */
   {"run",       NULL,           1, 0, 0, DBGCMD_RUN,            dbgcmd_run},             /* r[un] */
   {"step",      NULL,           1, 0, 1, DBGCMD_STEP,           dbgcmd_step},            /* s[tep] */
+  {"next",      NULL,           1, 0, 1, DBGCMD_NEXT,           dbgcmd_next},            /* n[ext] */
   {NULL}
 };
 
@@ -405,7 +406,7 @@ print_info_stopped_break(mrb_state *mrb, mrdb_state *mrdb)
   const char *class_name;
 
   ret = mrb_debug_get_break(mrb, mrdb->dbg, mrdb->dbg->stopped_bpno, &bp);
-  if(ret == 0) {
+  if (ret == 0) {
     switch(bp.type) {
       case MRB_DEBUG_BPTYPE_LINE:
         file = bp.point.linepoint.file;
@@ -415,13 +416,13 @@ print_info_stopped_break(mrb_state *mrb, mrdb_state *mrdb)
       case MRB_DEBUG_BPTYPE_METHOD:
         method_name = bp.point.methodpoint.method_name;
         class_name = bp.point.methodpoint.class_name;
-        if(class_name == NULL) {
+        if (class_name == NULL) {
           printf("Breakpoint %d, %s\n", bp.bpno, method_name);
         }
         else {
           printf("Breakpoint %d, %s:%s\n", bp.bpno, class_name, method_name);
         }
-        if(mrdb->dbg->isCfunc) {
+        if (mrdb->dbg->isCfunc) {
           printf("Stopped before calling the C function.\n");
         }
         break;
@@ -444,7 +445,7 @@ print_info_stopped_code(mrb_state *mrb, mrdb_state *mrdb)
 {
   char* file = mrb_debug_get_source(mrb, mrdb, mrdb->srcpath, mrdb->dbg->prvfile);
   uint16_t lineno = mrdb->dbg->prvline;
-  if(file != NULL) {
+  if (file != NULL) {
     mrb_debug_list(mrb, mrdb->dbg, file, lineno, lineno);
     mrb_free(mrb, file);
   }
@@ -530,9 +531,9 @@ check_method_breakpoint(mrb_state *mrb, mrb_irep *irep, mrb_code *pc, mrb_value
       sym = 0;
       break;
   }
-  if(sym != 0) {
+  if (sym != 0) {
     dbg->method_bpno = mrb_debug_check_breakpoint_method(mrb, dbg, c, sym, &isCfunc);
-    if(isCfunc) {
+    if (isCfunc) {
       bpno = dbg->method_bpno;
       dbg->method_bpno = 0;
     }
@@ -556,10 +557,11 @@ mrb_code_fetch_hook(mrb_state *mrb, mrb_irep *irep, mrb_code *pc, mrb_value *reg
   dbg->pc   = pc;
   dbg->regs = regs;
 
-  if(dbg->xphase == DBG_PHASE_RESTART) {
+  if (dbg->xphase == DBG_PHASE_RESTART) {
     dbg->root_irep = irep;
     dbg->prvfile = NULL;
     dbg->prvline = 0;
+    dbg->prvci = NULL;
     dbg->xm = DBG_RUN;
     dbg->xphase = DBG_PHASE_RUNNING;
   }
@@ -569,7 +571,6 @@ mrb_code_fetch_hook(mrb_state *mrb, mrb_irep *irep, mrb_code *pc, mrb_value *reg
 
   switch (dbg->xm) {
   case DBG_STEP:
-  case DBG_NEXT:  // temporary
     if (!file || (dbg->prvfile == file && dbg->prvline == line)) {
       return;
     }
@@ -577,6 +578,18 @@ mrb_code_fetch_hook(mrb_state *mrb, mrb_irep *irep, mrb_code *pc, mrb_value *reg
     dbg->bm = BRK_STEP;
     break;
 
+  case DBG_NEXT:
+    if (!file || (dbg->prvfile == file && dbg->prvline == line)) {
+      return;
+    }
+    if ((intptr_t)(dbg->prvci) < (intptr_t)(mrb->c->ci)) {
+      return;
+    }
+    dbg->prvci = NULL;
+    dbg->method_bpno = 0;
+    dbg->bm = BRK_NEXT;
+    break;
+
   case DBG_RUN:
     bpno = check_method_breakpoint(mrb, irep, pc, regs);
     if (bpno > 0) {
@@ -610,7 +623,7 @@ mrb_code_fetch_hook(mrb_state *mrb, mrb_irep *irep, mrb_code *pc, mrb_value *reg
   dbg->prvfile = file;
   dbg->prvline = line;
 
-  if(dbg->bm == BRK_BREAK && --dbg->ccnt > 0) {
+  if (dbg->bm == BRK_BREAK && --dbg->ccnt > 0) {
     return;
   }
   dbg->break_hook(mrb, dbg);
@@ -633,7 +646,7 @@ mrb_debug_break_hook(mrb_state *mrb, mrb_debug_context *dbg)
 
     st = cmd->func(mrb, mrdb);
 
-    if( (st == DBGST_CONTINUE) || (st == DBGST_RESTART) ) break;
+    if ((st == DBGST_CONTINUE) || (st == DBGST_RESTART)) break;
   }
   return dbg->xm;
 }
@@ -670,7 +683,7 @@ main(int argc, char **argv)
   mrb_assert(mrdb && mrdb->dbg);
   mrdb->srcpath = args.srcpath;
 
-  if(mrdb->dbg->xm == DBG_QUIT) {
+  if (mrdb->dbg->xm == DBG_QUIT) {
     mrdb->dbg->xphase = DBG_PHASE_RESTART;
   }
   else {
@@ -678,7 +691,7 @@ main(int argc, char **argv)
   }
   mrdb->dbg->xm = DBG_INIT;
   mrdb->dbg->ccnt = 1;
-  
+
   /* setup hook functions */
   mrb->code_fetch_hook = mrb_code_fetch_hook;
   mrdb->dbg->break_hook = mrb_debug_break_hook;
@@ -725,21 +738,21 @@ main(int argc, char **argv)
       mrb_p(mrb, v);
     }
   }
-  
+
   mrdb->dbg->prvfile = "-";
   mrdb->dbg->prvline = 0;
-  
+
   while (1) {
     cmd = get_and_parse_command(mrb, mrdb);
     mrb_assert(cmd);
-    
+
     if (cmd->id == DBGCMD_QUIT) {
       break;
     }
-    
-    if( cmd->func(mrb, mrdb) == DBGST_RESTART ) goto l_restart;
+
+    if ( cmd->func(mrb, mrdb) == DBGST_RESTART ) goto l_restart;
   }
-  
+
   cleanup(mrb, &args);
 
   return 0;
old mode 100755 (executable)
new mode 100644 (file)
index 9e92ce8..5ac12c1
@@ -6,7 +6,7 @@
 #ifndef MRDB_H
 #define MRDB_H
 
-#include "mruby.h"
+#include <mruby.h>
 
 #include "mrdbconf.h"
 
@@ -109,6 +109,7 @@ typedef struct mrb_debug_context {
 
   const char *prvfile;
   int32_t prvline;
+  mrb_callinfo *prvci;
 
   mrdb_exemode xm;
   mrdb_exephase xphase;
@@ -146,6 +147,7 @@ typedef dbgcmd_state (*debug_command_func)(mrb_state*, mrdb_state*);
 dbgcmd_state dbgcmd_run(mrb_state*, mrdb_state*);
 dbgcmd_state dbgcmd_continue(mrb_state*, mrdb_state*);
 dbgcmd_state dbgcmd_step(mrb_state*, mrdb_state*);
+dbgcmd_state dbgcmd_next(mrb_state*, mrdb_state*);
 /* cmdbreak.c */
 dbgcmd_state dbgcmd_break(mrb_state*, mrdb_state*);
 dbgcmd_state dbgcmd_info_break(mrb_state*, mrdb_state*);
index 37fda35..fe311d8 100644 (file)
@@ -11,6 +11,9 @@
 #include <stdio.h>
 #include <ctype.h>
 
+#include <signal.h>
+#include <setjmp.h>
+
 #ifdef ENABLE_READLINE
 #include <readline/readline.h>
 #include <readline/history.h>
 #define MIRB_USING_HISTORY()
 #endif
 
-#include "mruby.h"
-#include "mruby/array.h"
-#include "mruby/proc.h"
-#include "mruby/compile.h"
-#include "mruby/string.h"
+#ifndef _WIN32
+#define MIRB_SIGSETJMP(env) sigsetjmp(env, 1)
+#define MIRB_SIGLONGJMP(env, val) siglongjmp(env, val)
+#define SIGJMP_BUF sigjmp_buf
+#else
+#define MIRB_SIGSETJMP(env) setjmp(env)
+#define MIRB_SIGLONGJMP(env, val) longjmp(env, val)
+#define SIGJMP_BUF jmp_buf
+#endif
+
+#include <mruby.h>
+#include <mruby/array.h>
+#include <mruby/proc.h>
+#include <mruby/compile.h>
+#include <mruby/string.h>
 
 #ifdef ENABLE_READLINE
 
@@ -194,6 +207,7 @@ is_code_block_open(struct mrb_parser_state *parser)
 }
 
 struct _args {
+  FILE *rfp;
   mrb_bool verbose      : 1;
   int argc;
   char** argv;
@@ -251,12 +265,30 @@ parse_args(mrb_state *mrb, int argc, char **argv, struct _args *args)
       return EXIT_FAILURE;
     }
   }
+
+  if (args->rfp == NULL) {
+    if (*argv != NULL) {
+      args->rfp = fopen(argv[0], "r");
+      if (args->rfp == NULL) {
+        printf("Cannot open program file. (%s)\n", *argv);
+        return EXIT_FAILURE;
+      }
+      argc--; argv++;
+    }
+  }
+  args->argv = (char **)mrb_realloc(mrb, args->argv, sizeof(char*) * (argc + 1));
+  memcpy(args->argv, argv, (argc+1) * sizeof(char*));
+  args->argc = argc;
+
   return EXIT_SUCCESS;
 }
 
 static void
 cleanup(mrb_state *mrb, struct _args *args)
 {
+  if (args->rfp)
+    fclose(args->rfp);
+  mrb_free(mrb, args->argv);
   mrb_close(mrb);
 }
 
@@ -307,23 +339,43 @@ check_keyword(const char *buf, const char *word)
   return 1;
 }
 
+
+#ifndef ENABLE_READLINE
+volatile sig_atomic_t input_canceled = 0;
+void
+ctrl_c_handler(int signo)
+{
+  input_canceled = 1;
+}
+#else
+SIGJMP_BUF ctrl_c_buf;
+void
+ctrl_c_handler(int signo)
+{
+  MIRB_SIGLONGJMP(ctrl_c_buf, 1);
+}
+#endif
+
 int
 main(int argc, char **argv)
 {
-  char ruby_code[1024] = { 0 };
+  char ruby_code[4096] = { 0 };
   char last_code_line[1024] = { 0 };
 #ifndef ENABLE_READLINE
   int last_char;
-  int char_index;
+  size_t char_index;
 #else
   char *history_path;
+  char* line;
 #endif
   mrbc_context *cxt;
   struct mrb_parser_state *parser;
   mrb_state *mrb;
   mrb_value result;
   struct _args args;
+  mrb_value ARGV;
   int n;
+  int i;
   mrb_bool code_block_open = FALSE;
   int ai;
   unsigned int stack_keep = 0;
@@ -334,7 +386,6 @@ main(int argc, char **argv)
     fputs("Invalid mrb interpreter, exiting mirb\n", stderr);
     return EXIT_FAILURE;
   }
-  mrb_define_global_const(mrb, "ARGV", mrb_ary_new_capa(mrb, 0));
 
   n = parse_args(mrb, argc, argv, &args);
   if (n == EXIT_FAILURE) {
@@ -343,6 +394,16 @@ main(int argc, char **argv)
     return n;
   }
 
+  ARGV = mrb_ary_new_capa(mrb, args.argc);
+  for (i = 0; i < args.argc; i++) {
+    char* utf8 = mrb_utf8_from_locale(args.argv[i], -1);
+    if (utf8) {
+      mrb_ary_push(mrb, ARGV, mrb_str_new_cstr(mrb, utf8));
+      mrb_utf8_free(utf8);
+    }
+  }
+  mrb_define_global_const(mrb, "ARGV", ARGV);
+
 #ifdef ENABLE_READLINE
   history_path = get_history_path(mrb);
   if (history_path == NULL) {
@@ -368,18 +429,34 @@ main(int argc, char **argv)
   while (TRUE) {
     char *utf8;
 
+    if (args.rfp) {
+      if (fgets(last_code_line, sizeof(last_code_line)-1, args.rfp) != NULL)
+        goto done;
+      break;
+    }
+
 #ifndef ENABLE_READLINE
     print_cmdline(code_block_open);
 
+    signal(SIGINT, ctrl_c_handler);
     char_index = 0;
     while ((last_char = getchar()) != '\n') {
       if (last_char == EOF) break;
-      if (char_index > sizeof(last_code_line)-2) {
+      if (char_index >= sizeof(last_code_line)-2) {
         fputs("input string too long\n", stderr);
         continue;
       }
       last_code_line[char_index++] = last_char;
     }
+    signal(SIGINT, SIG_DFL);
+    if (input_canceled) {
+      ruby_code[0] = '\0';
+      last_code_line[0] = '\0';
+      code_block_open = FALSE;
+      puts("^C");
+      input_canceled = 0;
+      continue;
+    }
     if (last_char == EOF) {
       fputs("\n", stdout);
       break;
@@ -388,7 +465,19 @@ main(int argc, char **argv)
     last_code_line[char_index++] = '\n';
     last_code_line[char_index] = '\0';
 #else
-    char* line = MIRB_READLINE(code_block_open ? "* " : "> ");
+    if (MIRB_SIGSETJMP(ctrl_c_buf) == 0) {
+      ;
+    }
+    else {
+      ruby_code[0] = '\0';
+      last_code_line[0] = '\0';
+      code_block_open = FALSE;
+      puts("^C");
+    }
+    signal(SIGINT, ctrl_c_handler);
+    line = MIRB_READLINE(code_block_open ? "* " : "> ");
+    signal(SIGINT, SIG_DFL);
+
     if (line == NULL) {
       printf("\n");
       break;
@@ -403,6 +492,8 @@ main(int argc, char **argv)
     free(line);
 #endif
 
+done:
+
     if (code_block_open) {
       if (strlen(ruby_code)+strlen(last_code_line) > sizeof(ruby_code)-1) {
         fputs("concatenated input string too long\n", stderr);
@@ -453,9 +544,9 @@ main(int argc, char **argv)
         if (args.verbose) {
           mrb_codedump_all(mrb, proc);
         }
-        /* pass a proc for evaulation */
+        /* pass a proc for evaluation */
         /* evaluate the bytecode */
-        result = mrb_context_run(mrb,
+        result = mrb_vm_run(mrb,
             proc,
             mrb_top_self(mrb),
             stack_keep);
index 2f50790..580c2e2 100644 (file)
@@ -1,10 +1,10 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
-#include "mruby.h"
-#include "mruby/compile.h"
-#include "mruby/dump.h"
-#include "mruby/proc.h"
+#include <mruby.h>
+#include <mruby/compile.h>
+#include <mruby/dump.h>
+#include <mruby/proc.h>
 
 #define RITEBIN_EXT ".mrb"
 #define C_EXT       ".c"
index 73f971a..66d6ef8 100644 (file)
@@ -23,7 +23,7 @@ MRuby.each_target do
     FileUtils.copy "#{File.dirname(__FILE__)}/#{mruby_config}", t.name
     config = Hash[open("#{build_dir}/lib/libmruby.flags.mak").read.split("\n").map {|x| a = x.split(/\s*=\s*/, 2); [a[0], a[1].gsub('\\"', '"') ]}]
     IO.write(t.name, File.open(t.name) {|f|
-      f.read.gsub (/echo (MRUBY_CFLAGS|MRUBY_LIBS|MRUBY_LDFLAGS_BEFORE_LIBS|MRUBY_LDFLAGS)/) {|x| config[$1].empty? ? '' : "echo #{config[$1]}"}
+      f.read.gsub (/echo (MRUBY_CFLAGS|MRUBY_LIBS|MRUBY_LDFLAGS_BEFORE_LIBS|MRUBY_LDFLAGS|MRUBY_LIBMRUBY_PATH)/) {|x| config[$1].empty? ? '' : "echo #{config[$1]}"}
     })
     FileUtils.chmod(0755, t.name)
   end
index fcc3fab..57346c0 100644 (file)
@@ -6,12 +6,14 @@ while [ $# -gt 0 ]; do
     --ldflags) echo MRUBY_LDFLAGS;;
     --ldflags-before-libs) echo MRUBY_LDFLAGS_BEFORE_LIBS;;
     --libs) echo MRUBY_LIBS;;
+    --libmruby-path) echo MRUBY_LIBMRUBY_PATH;;
     --help) echo "Usage: mruby-config [switches]"
             echo "  switches:"
             echo "  --cflags                    print flags passed to compiler"
             echo "  --ldflags                   print flags passed to linker"
-            echo "  --ldflags-before-libs       print flags passwd to linker before linked libraries"
+            echo "  --ldflags-before-libs       print flags passed to linker before linked libraries"
             echo "  --libs                      print linked libraries"
+            echo "  --libmruby-path             print libmruby path"
             exit 0;;
   esac
   shift
index 212dbdd..a1f7bfd 100644 (file)
@@ -7,6 +7,7 @@ if "%0" equ "--cflags" goto cflags
 if "%0" equ "--ldflags" goto ldflags
 if "%0" equ "--ldflags-before-libs" goto ldflagsbeforelibs
 if "%0" equ "--libs" goto libs
+if "%0" equ "--libmruby-path" goto libmrubypath
 if "%0" equ "--help" goto showhelp
 echo Invalid Option
 goto :eof
@@ -27,10 +28,15 @@ goto top
 echo MRUBY_LDFLAGS_BEFORE_LIBS
 goto top
 
+:libmrubypath
+echo MRUBY_LIBMRUBY_PATH
+goto top
+
 :showhelp
 echo Usage: mruby-config [switches]
 echo   switches:
 echo   --cflags                   print flags passed to compiler
 echo   --ldflags                  print flags passed to linker
-echo   --ldflags-before-libs      print flags passwd to linker before linked libraries
+echo   --ldflags-before-libs      print flags passed to linker before linked libraries
 echo   --libs                     print linked libraries
+echo   --libmruby-path            print libmruby path
index 01fc946..b6b0901 100644 (file)
@@ -2,9 +2,9 @@ require 'tempfile'
 
 assert('regression for #1564') do
   o = `#{cmd('mruby')} -e #{shellquote('<<')} 2>&1`
-  assert_equal o, "-e:1:2: syntax error, unexpected tLSHFT\n"
+  assert_include o, "-e:1:2: syntax error"
   o = `#{cmd('mruby')} -e #{shellquote('<<-')} 2>&1`
-  assert_equal o, "-e:1:3: syntax error, unexpected tLSHFT\n"
+  assert_include o, "-e:1:3: syntax error"
 end
 
 assert('regression for #1572') do
@@ -44,3 +44,17 @@ EOS
   script.flush
   assert_equal "\"test\"\n\"fin\"\n", `#{cmd('mruby')} #{script.path}`
 end
+
+assert('garbage collecting built-in classes') do
+  script = Tempfile.new('test.rb')
+
+  script.write <<RUBY
+NilClass = nil
+GC.start
+Array.dup
+print nil.class.to_s
+RUBY
+  script.flush
+  assert_equal "NilClass", `#{cmd('mruby')} #{script.path}`
+  assert_equal 0, $?.exitstatus
+end
index ba7fad1..fbec138 100644 (file)
@@ -4,4 +4,9 @@ MRuby::Gem::Specification.new('mruby-bin-mruby') do |spec|
   spec.summary = 'mruby command'
   spec.bins = %w(mruby)
   spec.add_dependency('mruby-compiler', :core => 'mruby-compiler')
+  spec.add_dependency('mruby-error', :core => 'mruby-error')
+
+  if build.cxx_exception_enabled?
+    build.compile_as_cxx("#{spec.dir}/tools/mruby/mruby.c", "#{spec.build_dir}/tools/mruby/mruby.cxx")
+  end
 end
index 4b801bb..61d4cde 100644 (file)
@@ -1,11 +1,11 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
-#include "mruby.h"
-#include "mruby/array.h"
-#include "mruby/compile.h"
-#include "mruby/dump.h"
-#include "mruby/variable.h"
+#include <mruby.h>
+#include <mruby/array.h>
+#include <mruby/compile.h>
+#include <mruby/dump.h>
+#include <mruby/variable.h>
 
 #ifdef MRB_DISABLE_STDIO
 static void
@@ -188,59 +188,65 @@ main(int argc, char **argv)
     usage(argv[0]);
     return n;
   }
+  else {
+    int ai = mrb_gc_arena_save(mrb);
+    ARGV = mrb_ary_new_capa(mrb, args.argc);
+    for (i = 0; i < args.argc; i++) {
+      char* utf8 = mrb_utf8_from_locale(args.argv[i], -1);
+      if (utf8) {
+        mrb_ary_push(mrb, ARGV, mrb_str_new_cstr(mrb, utf8));
+        mrb_utf8_free(utf8);
+      }
+    }
+    mrb_define_global_const(mrb, "ARGV", ARGV);
+
+    c = mrbc_context_new(mrb);
+    if (args.verbose)
+      c->dump_result = TRUE;
+    if (args.check_syntax)
+      c->no_exec = TRUE;
+
+    /* Set $0 */
+    zero_sym = mrb_intern_lit(mrb, "$0");
+    if (args.rfp) {
+      const char *cmdline;
+      cmdline = args.cmdline ? args.cmdline : "-";
+      mrbc_filename(mrb, c, cmdline);
+      mrb_gv_set(mrb, zero_sym, mrb_str_new_cstr(mrb, cmdline));
+    }
+    else {
+      mrbc_filename(mrb, c, "-e");
+      mrb_gv_set(mrb, zero_sym, mrb_str_new_lit(mrb, "-e"));
+    }
 
-  ARGV = mrb_ary_new_capa(mrb, args.argc);
-  for (i = 0; i < args.argc; i++) {
-    char* utf8 = mrb_utf8_from_locale(args.argv[i], -1);
-    if (utf8) {
-      mrb_ary_push(mrb, ARGV, mrb_str_new_cstr(mrb, utf8));
+    /* Load program */
+    if (args.mrbfile) {
+      v = mrb_load_irep_file_cxt(mrb, args.rfp, c);
+    }
+    else if (args.rfp) {
+      v = mrb_load_file_cxt(mrb, args.rfp, c);
+    }
+    else {
+      char* utf8 = mrb_utf8_from_locale(args.cmdline, -1);
+      if (!utf8) abort();
+      v = mrb_load_string_cxt(mrb, utf8, c);
       mrb_utf8_free(utf8);
     }
-  }
-  mrb_define_global_const(mrb, "ARGV", ARGV);
-
-  c = mrbc_context_new(mrb);
-  if (args.verbose)
-    c->dump_result = TRUE;
-  if (args.check_syntax)
-    c->no_exec = TRUE;
-
-  /* Set $0 */
-  zero_sym = mrb_intern_lit(mrb, "$0");
-  if (args.rfp) {
-    const char *cmdline;
-    cmdline = args.cmdline ? args.cmdline : "-";
-    mrbc_filename(mrb, c, cmdline);
-    mrb_gv_set(mrb, zero_sym, mrb_str_new_cstr(mrb, cmdline));
-  }
-  else {
-    mrbc_filename(mrb, c, "-e");
-    mrb_gv_set(mrb, zero_sym, mrb_str_new_lit(mrb, "-e"));
-  }
-
-  /* Load program */
-  if (args.mrbfile) {
-    v = mrb_load_irep_file_cxt(mrb, args.rfp, c);
-  }
-  else if (args.rfp) {
-    v = mrb_load_file_cxt(mrb, args.rfp, c);
-  }
-  else {
-    char* utf8 = mrb_utf8_from_locale(args.cmdline, -1);
-    if (!utf8) abort();
-    v = mrb_load_string_cxt(mrb, utf8, c);
-    mrb_utf8_free(utf8);
-  }
 
-  mrbc_context_free(mrb, c);
-  if (mrb->exc) {
-    if (!mrb_undef_p(v)) {
-      mrb_print_error(mrb);
+    mrb_gc_arena_restore(mrb, ai);
+    mrbc_context_free(mrb, c);
+    if (mrb->exc) {
+      if (mrb_undef_p(v)) {
+        mrb_p(mrb, mrb_obj_value(mrb->exc));
+      }
+      else {
+        mrb_print_error(mrb);
+      }
+      n = -1;
+    }
+    else if (args.check_syntax) {
+      printf("Syntax OK\n");
     }
-    n = -1;
-  }
-  else if (args.check_syntax) {
-    printf("Syntax OK\n");
   }
   cleanup(mrb, &args);
 
index 0529d39..1fd2bc5 100644 (file)
@@ -1,9 +1,9 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
-#include "mruby.h"
-#include "mruby/irep.h"
-#include "mruby/dump.h"
+#include <mruby.h>
+#include <mruby/irep.h>
+#include <mruby/dump.h>
 
 struct strip_args {
   int argc_start;
diff --git a/third-party/mruby/mrbgems/mruby-class-ext/mrbgem.rake b/third-party/mruby/mrbgems/mruby-class-ext/mrbgem.rake
new file mode 100644 (file)
index 0000000..a384b1e
--- /dev/null
@@ -0,0 +1,5 @@
+MRuby::Gem::Specification.new('mruby-class-ext') do |spec|
+  spec.license = 'MIT'
+  spec.author  = 'mruby developers'
+  spec.summary = 'class/module extension'
+end
diff --git a/third-party/mruby/mrbgems/mruby-class-ext/src/class.c b/third-party/mruby/mrbgems/mruby-class-ext/src/class.c
new file mode 100644 (file)
index 0000000..5506c48
--- /dev/null
@@ -0,0 +1,30 @@
+#include "mruby.h"
+#include "mruby/class.h"
+#include "mruby/string.h"
+
+static mrb_value
+mrb_mod_name(mrb_state *mrb, mrb_value self)
+{
+  mrb_value name = mrb_class_path(mrb, mrb_class_ptr(self));
+  return mrb_nil_p(name)? name : mrb_str_dup(mrb, name);
+}
+
+static mrb_value
+mrb_mod_singleton_class_p(mrb_state *mrb, mrb_value self)
+{
+  return mrb_bool_value(mrb_type(self) == MRB_TT_SCLASS);
+}
+
+void
+mrb_mruby_class_ext_gem_init(mrb_state *mrb)
+{
+  struct RClass *mod = mrb->module_class;
+
+  mrb_define_method(mrb, mod, "name", mrb_mod_name, MRB_ARGS_NONE());
+  mrb_define_method(mrb, mod, "singleton_class?", mrb_mod_singleton_class_p, MRB_ARGS_NONE());
+}
+
+void
+mrb_mruby_class_ext_gem_final(mrb_state *mrb)
+{
+}
diff --git a/third-party/mruby/mrbgems/mruby-class-ext/test/module.rb b/third-party/mruby/mrbgems/mruby-class-ext/test/module.rb
new file mode 100644 (file)
index 0000000..65abde1
--- /dev/null
@@ -0,0 +1,34 @@
+assert 'Module#name' do
+  module Outer
+    class Inner; end
+    const_set :SetInner, Class.new
+  end
+
+  assert_equal 'Outer', Outer.name
+  assert_equal 'Outer::Inner', Outer::Inner.name
+  assert_equal 'Outer::SetInner', Outer::SetInner.name
+
+  outer = Module.new do
+    const_set :SetInner, Class.new
+  end
+  Object.const_set :SetOuter, outer
+
+  assert_equal 'SetOuter', SetOuter.name
+  assert_equal 'SetOuter::SetInner', SetOuter::SetInner.name
+
+  mod = Module.new
+  cls = Class.new
+
+  assert_nil mod.name
+  assert_nil cls.name
+end
+
+assert 'Module#singleton_class?' do
+  mod = Module.new
+  cls = Class.new
+  scl = cls.singleton_class
+
+  assert_false mod.singleton_class?
+  assert_false cls.singleton_class?
+  assert_true scl.singleton_class?
+end
index e4dc6a9..f4d9208 100644 (file)
@@ -10,3 +10,21 @@ assert('Compiling multiple files without new line in last line. #2361') do
   assert_equal "#{cmd('mrbc')}:#{a.path}:Syntax OK", result.chomp
   assert_equal 0, $?.exitstatus
 end
+
+assert('parsing function with void argument') do
+  a, out = Tempfile.new('a.rb'), Tempfile.new('out.mrb')
+  a.write('f ()')
+  a.flush
+  result = `#{cmd('mrbc')} -c -o #{out.path} #{a.path} 2>&1`
+  assert_equal "#{cmd('mrbc')}:#{a.path}:Syntax OK", result.chomp
+  assert_equal 0, $?.exitstatus
+end
+
+assert('embedded document with invalid terminator') do
+  a, out = Tempfile.new('a.rb'), Tempfile.new('out.mrb')
+  a.write("=begin\n=endx\n")
+  a.flush
+  result = `#{cmd('mrbc')} -c -o #{out.path} #{a.path} 2>&1`
+  assert_equal "#{a.path}:3:0: embedded document meets end of file", result.chomp
+  assert_equal 1, $?.exitstatus
+end
index b47c1c6..a1045c3 100644 (file)
@@ -8,16 +8,20 @@
 #include <limits.h>
 #include <stdlib.h>
 #include <string.h>
-#include "mruby.h"
-#include "mruby/compile.h"
-#include "mruby/proc.h"
-#include "mruby/numeric.h"
-#include "mruby/string.h"
-#include "mruby/debug.h"
+#include <mruby.h>
+#include <mruby/compile.h>
+#include <mruby/proc.h>
+#include <mruby/numeric.h>
+#include <mruby/string.h>
+#include <mruby/debug.h>
 #include "node.h"
-#include "mruby/opcode.h"
-#include "mruby/re.h"
-#include "mruby/throw.h"
+#include <mruby/opcode.h>
+#include <mruby/re.h>
+#include <mruby/throw.h>
+
+#ifndef MRB_CODEGEN_LEVEL_MAX
+#define MRB_CODEGEN_LEVEL_MAX 1024
+#endif
 
 typedef mrb_ast_node node;
 typedef struct mrb_parser_state parser_state;
@@ -73,6 +77,8 @@ typedef struct scope {
   int debug_start_pos;
   uint16_t filename_index;
   parser_state* parser;
+
+  int rlev;                     /* recursion levels */
 } codegen_scope;
 
 static codegen_scope* scope_new(mrb_state *mrb, codegen_scope *prev, node *lv);
@@ -93,10 +99,11 @@ codegen_error(codegen_scope *s, const char *message)
   if (!s) return;
   while (s->prev) {
     codegen_scope *tmp = s->prev;
+    mrb_free(s->mrb, s->iseq);
     mrb_pool_close(s->mpool);
     s = tmp;
   }
-#ifndef MBB_DISABLE_STDIO
+#ifndef MRB_DISABLE_STDIO
   if (s->filename && s->lineno) {
     fprintf(stderr, "codegen error:%s:%d: %s\n", s->filename, s->lineno, message);
   }
@@ -339,6 +346,12 @@ genop_peep(codegen_scope *s, mrb_code i, int val)
           return 0;
         }
       }
+      if (c0 == OP_LOADNIL) {
+        if (GETARG_B(i) == GETARG_A(i0)) {
+          s->pc--;
+          return 0;
+        }
+      }
       break;
     case OP_JMPIF:
     case OP_JMPNOT:
@@ -381,6 +394,9 @@ dispatch(codegen_scope *s, int pc)
     scope_error(s);
     break;
   }
+  if (diff > MAXARG_sBx) {
+    codegen_error(s, "too distant jump address");
+  }
   s->iseq[pc] = MKOP_AsBx(c, GETARG_A(i), diff);
 }
 
@@ -494,7 +510,12 @@ new_lit(codegen_scope *s, mrb_value val)
   return i;
 }
 
-static inline int
+/* method symbols should be fit in 9 bits */
+#define MAXMSYMLEN 512
+/* maximum symbol numbers */
+#define MAXSYMLEN 65536
+
+static int
 new_msym(codegen_scope *s, mrb_sym sym)
 {
   size_t i, len;
@@ -502,20 +523,20 @@ new_msym(codegen_scope *s, mrb_sym sym)
   mrb_assert(s->irep);
 
   len = s->irep->slen;
-  if (len > 256) len = 256;
+  if (len > MAXMSYMLEN) len = MAXMSYMLEN;
   for (i=0; i<len; i++) {
     if (s->irep->syms[i] == sym) return i;
     if (s->irep->syms[i] == 0) break;
   }
-  if (i == 256) {
-    codegen_error(s, "too many symbols (max 256)");
+  if (i == MAXMSYMLEN) {
+    codegen_error(s, "too many symbols (max " MRB_STRINGIZE(MAXMSYMLEN) ")");
   }
   s->irep->syms[i] = sym;
   if (i == s->irep->slen) s->irep->slen++;
   return i;
 }
 
-static inline int
+static int
 new_sym(codegen_scope *s, mrb_sym sym)
 {
   size_t i;
@@ -523,13 +544,18 @@ new_sym(codegen_scope *s, mrb_sym sym)
   for (i=0; i<s->irep->slen; i++) {
     if (s->irep->syms[i] == sym) return i;
   }
-  if (s->irep->slen > 125 && s->irep->slen < 256) {
-    s->irep->syms = (mrb_sym *)codegen_realloc(s, s->irep->syms, sizeof(mrb_sym)*65536);
-    for (i = 0; i < 256 - s->irep->slen; i++) {
+  if (s->irep->slen == MAXSYMLEN) {
+    codegen_error(s, "too many symbols (max " MRB_STRINGIZE(MAXSYMLEN) ")");
+  }
+
+  if (s->irep->slen > MAXMSYMLEN/2 && s->scapa == MAXMSYMLEN) {
+    s->scapa = MAXSYMLEN;
+    s->irep->syms = (mrb_sym *)codegen_realloc(s, s->irep->syms, sizeof(mrb_sym)*MAXSYMLEN);
+    for (i = s->irep->slen; i < MAXMSYMLEN; i++) {
       static const mrb_sym mrb_sym_zero = { 0 };
-      s->irep->syms[i + s->irep->slen] = mrb_sym_zero;
+      s->irep->syms[i] = mrb_sym_zero;
     }
-    s->irep->slen = 256;
+    s->irep->slen = MAXMSYMLEN;
   }
   s->irep->syms[s->irep->slen] = sym;
   return s->irep->slen++;
@@ -582,9 +608,6 @@ for_body(codegen_scope *s, node *tree)
 
   push();                       /* push for a block parameter */
 
-  lp = loop_push(s, LOOP_FOR);
-  lp->pc1 = new_label(s);
-
   /* generate loop variable */
   n2 = tree->car;
   genop(s, MKOP_Ax(OP_ENTER, 0x40000));
@@ -594,6 +617,11 @@ for_body(codegen_scope *s, node *tree)
   else {
     gen_vmassignment(s, n2, 1, VAL);
   }
+  /* construct loop */
+  lp = loop_push(s, LOOP_FOR);
+  lp->pc2 = new_label(s);
+
+  /* loop body */
   codegen(s, tree->cdr->cdr->car, VAL);
   pop();
   if (s->pc > 0) {
@@ -644,6 +672,9 @@ lambda_body(codegen_scope *s, node *tree, int blk)
     ka = kd = 0;
     ba = tree->car->cdr->cdr->cdr->cdr ? 1 : 0;
 
+    if (ma > 0x1f || oa > 0x1f || pa > 0x1f || ka > 0x1f) {
+      codegen_error(s, "too many formal arguments");
+    }
     a = ((mrb_aspec)(ma & 0x1f) << 18)
       | ((mrb_aspec)(oa & 0x1f) << 13)
       | ((ra & 1) << 12)
@@ -764,26 +795,36 @@ attrsym(codegen_scope *s, mrb_sym a)
   return mrb_intern(s->mrb, name2, len+1);
 }
 
+#define CALL_MAXARGS 127
+
 static int
-gen_values(codegen_scope *s, node *t, int val)
+gen_values(codegen_scope *s, node *t, int val, int extra)
 {
   int n = 0;
   int is_splat;
 
   while (t) {
     is_splat = (intptr_t)t->car->car == NODE_SPLAT; /* splat mode */
-    if (n >= 127 || is_splat) {
+    if (
+      n+extra >= CALL_MAXARGS - 1 /* need to subtract one because vm.c expects an array if n == CALL_MAXARGS */
+      || is_splat) {
       if (val) {
-        pop_n(n);
-        genop(s, MKOP_ABC(OP_ARRAY, cursp(), cursp(), n));
-        push();
-        codegen(s, t->car, VAL);
-        pop(); pop();
-        if (is_splat) {
-          genop(s, MKOP_AB(OP_ARYCAT, cursp(), cursp()+1));
+        if (is_splat && n == 0 && (intptr_t)t->car->cdr->car == NODE_ARRAY) {
+          codegen(s, t->car->cdr, VAL);
+          pop();
         }
         else {
-          genop(s, MKOP_AB(OP_ARYPUSH, cursp(), cursp()+1));
+          pop_n(n);
+          genop(s, MKOP_ABC(OP_ARRAY, cursp(), cursp(), n));
+          push();
+          codegen(s, t->car, VAL);
+          pop(); pop();
+          if (is_splat) {
+            genop(s, MKOP_AB(OP_ARYCAT, cursp(), cursp()+1));
+          }
+          else {
+            genop(s, MKOP_AB(OP_ARYPUSH, cursp(), cursp()+1));
+          }
         }
         t = t->cdr;
         while (t) {
@@ -800,8 +841,6 @@ gen_values(codegen_scope *s, node *t, int val)
         }
       }
       else {
-        codegen(s, t->car->cdr, NOVAL);
-        t = t->cdr;
         while (t) {
           codegen(s, t->car, NOVAL);
           t = t->cdr;
@@ -817,20 +856,29 @@ gen_values(codegen_scope *s, node *t, int val)
   return n;
 }
 
-#define CALL_MAXARGS 127
-
 static void
-gen_call(codegen_scope *s, node *tree, mrb_sym name, int sp, int val)
+gen_call(codegen_scope *s, node *tree, mrb_sym name, int sp, int val, int safe)
 {
   mrb_sym sym = name ? name : sym(tree->cdr->car);
-  int idx;
+  int idx, skip = 0;
   int n = 0, noop = 0, sendv = 0, blk = 0;
 
   codegen(s, tree->car, VAL); /* receiver */
+  if (safe) {
+    int recv = cursp()-1;
+    genop(s, MKOP_A(OP_LOADNIL, cursp()));
+    push();
+    genop(s, MKOP_AB(OP_MOVE, cursp(), recv));
+    push(); pop();              /* space for a block */
+    pop();
+    idx = new_msym(s, mrb_intern_lit(s->mrb, "=="));
+    genop(s, MKOP_ABC(OP_EQ, cursp(), idx, 1));
+    skip = genop(s, MKOP_AsBx(OP_JMPIF, cursp(), 0));
+  }
   idx = new_msym(s, sym);
   tree = tree->cdr->cdr->car;
   if (tree) {
-    n = gen_values(s, tree->car, VAL);
+    n = gen_values(s, tree->car, VAL, sp?1:0);
     if (n < 0) {
       n = noop = sendv = 1;
       push();
@@ -862,31 +910,31 @@ gen_call(codegen_scope *s, node *tree, mrb_sym name, int sp, int val)
     mrb_int symlen;
     const char *symname = mrb_sym2name_len(s->mrb, sym, &symlen);
 
-    if (!noop && symlen == 1 && symname[0] == '+')  {
+    if (!noop && symlen == 1 && symname[0] == '+' && n == 1)  {
       genop_peep(s, MKOP_ABC(OP_ADD, cursp(), idx, n), val);
     }
-    else if (!noop && symlen == 1 && symname[0] == '-')  {
+    else if (!noop && symlen == 1 && symname[0] == '-' && n == 1)  {
       genop_peep(s, MKOP_ABC(OP_SUB, cursp(), idx, n), val);
     }
-    else if (!noop && symlen == 1 && symname[0] == '*')  {
+    else if (!noop && symlen == 1 && symname[0] == '*' && n == 1)  {
       genop(s, MKOP_ABC(OP_MUL, cursp(), idx, n));
     }
-    else if (!noop && symlen == 1 && symname[0] == '/')  {
+    else if (!noop && symlen == 1 && symname[0] == '/' && n == 1)  {
       genop(s, MKOP_ABC(OP_DIV, cursp(), idx, n));
     }
-    else if (!noop && symlen == 1 && symname[0] == '<')  {
+    else if (!noop && symlen == 1 && symname[0] == '<' && n == 1)  {
       genop(s, MKOP_ABC(OP_LT, cursp(), idx, n));
     }
-    else if (!noop && symlen == 2 && symname[0] == '<' && symname[1] == '=')  {
+    else if (!noop && symlen == 2 && symname[0] == '<' && symname[1] == '=' && n == 1)  {
       genop(s, MKOP_ABC(OP_LE, cursp(), idx, n));
     }
-    else if (!noop && symlen == 1 && symname[0] == '>')  {
+    else if (!noop && symlen == 1 && symname[0] == '>' && n == 1)  {
       genop(s, MKOP_ABC(OP_GT, cursp(), idx, n));
     }
-    else if (!noop && symlen == 2 && symname[0] == '>' && symname[1] == '=')  {
+    else if (!noop && symlen == 2 && symname[0] == '>' && symname[1] == '=' && n == 1)  {
       genop(s, MKOP_ABC(OP_GE, cursp(), idx, n));
     }
-    else if (!noop && symlen == 2 && symname[0] == '=' && symname[1] == '=')  {
+    else if (!noop && symlen == 2 && symname[0] == '=' && symname[1] == '=' && n == 1)  {
       genop(s, MKOP_ABC(OP_EQ, cursp(), idx, n));
     }
     else {
@@ -899,6 +947,9 @@ gen_call(codegen_scope *s, node *tree, mrb_sym name, int sp, int val)
       }
     }
   }
+  if (safe) {
+    dispatch(s, skip);
+  }
   if (val) {
     push();
   }
@@ -911,7 +962,7 @@ gen_assignment(codegen_scope *s, node *tree, int sp, int val)
   int type = (intptr_t)tree->car;
 
   tree = tree->cdr;
-  switch ((intptr_t)type) {
+  switch (type) {
   case NODE_GVAR:
     idx = new_sym(s, sym(tree));
     genop_peep(s, MKOP_ABx(OP_SETGLOBAL, sp, idx), val);
@@ -961,8 +1012,10 @@ gen_assignment(codegen_scope *s, node *tree, int sp, int val)
     break;
 
   case NODE_CALL:
+  case NODE_SCALL:
     push();
-    gen_call(s, tree, attrsym(s, sym(tree->cdr->car)), sp, NOVAL);
+    gen_call(s, tree, attrsym(s, sym(tree->cdr->car)), sp, NOVAL,
+             type == NODE_SCALL);
     pop();
     if (val) {
       genop_peep(s, MKOP_AB(OP_MOVE, cursp(), sp), val);
@@ -979,7 +1032,7 @@ gen_assignment(codegen_scope *s, node *tree, int sp, int val)
 
   default:
 #ifndef MRB_DISABLE_STDIO
-    printf("unknown lhs %d\n", type);
+    fprintf(stderr, "unknown lhs %d\n", type);
 #endif
     break;
   }
@@ -1032,7 +1085,9 @@ gen_vmassignment(codegen_scope *s, node *tree, int rhs, int val)
         n++;
       }
     }
-    push();
+    if (!val) {
+      push();
+    }
   }
 }
 
@@ -1175,12 +1230,39 @@ readint_mrb_int(codegen_scope *s, const char *p, int base, mrb_bool neg, mrb_boo
 }
 
 static void
+gen_retval(codegen_scope *s, node *tree)
+{
+  if ((intptr_t)tree->car == NODE_SPLAT) {
+    genop(s, MKOP_ABC(OP_ARRAY, cursp(), cursp(), 0));
+    push();
+    codegen(s, tree, VAL);
+    pop(); pop();
+    genop(s, MKOP_AB(OP_ARYCAT, cursp(), cursp()+1));
+  }
+  else {
+    codegen(s, tree, VAL);
+    pop();
+  }
+}
+
+static void
 codegen(codegen_scope *s, node *tree, int val)
 {
   int nt;
+  int rlev = s->rlev;
 
-  if (!tree) return;
+  if (!tree) {
+    if (val) {
+      genop(s, MKOP_A(OP_LOADNIL, cursp()));
+      push();
+    }
+    return;
+  }
 
+  s->rlev++;
+  if (s->rlev > MRB_CODEGEN_LEVEL_MAX) {
+    codegen_error(s, "too complex expression");
+  }
   if (s->irep && s->filename_index != tree->filename_index) {
     s->irep->filename = mrb_parser_get_filename(s->parser, s->filename_index);
     mrb_debug_info_append_file(s->mrb, s->irep, s->debug_start_pos, s->pc);
@@ -1209,13 +1291,12 @@ codegen(codegen_scope *s, node *tree, int val)
       int onerr, noexc, exend, pos1, pos2, tmp;
       struct loopinfo *lp;
 
+      if (tree->car == NULL) goto exit;
       onerr = genop(s, MKOP_Bx(OP_ONERR, 0));
       lp = loop_push(s, LOOP_BEGIN);
       lp->pc1 = onerr;
-      if (tree->car) {
-        codegen(s, tree->car, val);
-        if (val) pop();
-      }
+      codegen(s, tree->car, VAL);
+      pop();
       lp->type = LOOP_RESCUE;
       noexc = genop(s, MKOP_Bx(OP_JMP, 0));
       dispatch(s, onerr);
@@ -1226,7 +1307,7 @@ codegen(codegen_scope *s, node *tree, int val)
         node *n2 = tree->car;
         int exc = cursp();
 
-        genop(s, MKOP_A(OP_RESCUE, exc));
+        genop(s, MKOP_ABC(OP_RESCUE, exc, 0, 0));
         push();
         while (n2) {
           node *n3 = n2->car;
@@ -1235,20 +1316,22 @@ codegen(codegen_scope *s, node *tree, int val)
           if (pos1) dispatch(s, pos1);
           pos2 = 0;
           do {
-            if (n4) {
-              codegen(s, n4->car, VAL);
-            }
-            else {
-              genop(s, MKOP_ABx(OP_GETCONST, cursp(), new_msym(s, mrb_intern_lit(s->mrb, "StandardError"))));
-              push();
-            }
-            genop(s, MKOP_AB(OP_MOVE, cursp(), exc));
-            pop();
             if (n4 && n4->car && (intptr_t)n4->car->car == NODE_SPLAT) {
+              codegen(s, n4->car, VAL);
+              genop(s, MKOP_AB(OP_MOVE, cursp(), exc));
+              pop();
               genop(s, MKOP_ABC(OP_SEND, cursp(), new_msym(s, mrb_intern_lit(s->mrb, "__case_eqq")), 1));
             }
             else {
-              genop(s, MKOP_ABC(OP_SEND, cursp(), new_msym(s, mrb_intern_lit(s->mrb, "===")), 1));
+              if (n4) {
+                codegen(s, n4->car, VAL);
+              }
+              else {
+                genop(s, MKOP_ABx(OP_GETCONST, cursp(), new_msym(s, mrb_intern_lit(s->mrb, "StandardError"))));
+                push();
+              }
+              pop();
+              genop(s, MKOP_ABC(OP_RESCUE, exc, cursp(), 1));
             }
             tmp = genop(s, MKOP_AsBx(OP_JMPIF, cursp(), pos2));
             pos2 = tmp;
@@ -1293,7 +1376,9 @@ codegen(codegen_scope *s, node *tree, int val)
     break;
 
   case NODE_ENSURE:
-    {
+    if (!tree->cdr || !tree->cdr->cdr ||
+        ((intptr_t)tree->cdr->cdr->car == NODE_BEGIN &&
+         tree->cdr->cdr->cdr)) {
       int idx;
       int epush = s->pc;
 
@@ -1305,10 +1390,13 @@ codegen(codegen_scope *s, node *tree, int val)
       s->ensure_level--;
       genop_peep(s, MKOP_A(OP_EPOP, 1), NOVAL);
     }
+    else {                      /* empty ensure ignored */
+      codegen(s, tree->car, val);
+    }
     break;
 
   case NODE_LAMBDA:
-    {
+    if (val) {
       int idx = lambda_body(s, tree, 1);
 
       genop(s, MKOP_Abc(OP_LAMBDA, cursp(), idx, OP_L_LAMBDA));
@@ -1317,7 +1405,7 @@ codegen(codegen_scope *s, node *tree, int val)
     break;
 
   case NODE_BLOCK:
-    {
+    if (val) {
       int idx = lambda_body(s, tree, 1);
 
       genop(s, MKOP_Abc(OP_LAMBDA, cursp(), idx, OP_L_BLOCK));
@@ -1330,26 +1418,26 @@ codegen(codegen_scope *s, node *tree, int val)
       int pos1, pos2;
       node *e = tree->cdr->cdr->car;
 
+      if (!tree->car) {
+        codegen(s, e, val);
+        goto exit;
+      }
       switch ((intptr_t)tree->car->car) {
       case NODE_TRUE:
       case NODE_INT:
       case NODE_STR:
         codegen(s, tree->cdr->car, val);
-        return;
+        goto exit;
       case NODE_FALSE:
       case NODE_NIL:
         codegen(s, e, val);
-        return;
+        goto exit;
       }
       codegen(s, tree->car, VAL);
       pop();
       pos1 = genop_peep(s, MKOP_AsBx(OP_JMPNOT, cursp(), 0), NOVAL);
 
       codegen(s, tree->cdr->car, val);
-      if (val && !(tree->cdr->car)) {
-        genop(s, MKOP_A(OP_LOADNIL, cursp()));
-        push();
-      }
       if (e) {
         if (val) pop();
         pos2 = genop(s, MKOP_sBx(OP_JMP, 0));
@@ -1484,7 +1572,9 @@ codegen(codegen_scope *s, node *tree, int val)
         genop(s, MKOP_A(OP_LOADNIL, cursp()));
         if (pos3) dispatch_linked(s, pos3);
         if (head) pop();
-        genop(s, MKOP_AB(OP_MOVE, cursp(), pos));
+        if (cursp() != pos) {
+          genop(s, MKOP_AB(OP_MOVE, cursp(), pos));
+        }
         push();
       }
       else {
@@ -1504,7 +1594,10 @@ codegen(codegen_scope *s, node *tree, int val)
 
   case NODE_FCALL:
   case NODE_CALL:
-    gen_call(s, tree, 0, 0, val);
+    gen_call(s, tree, 0, 0, val, 0);
+    break;
+  case NODE_SCALL:
+    gen_call(s, tree, 0, 0, val, 1);
     break;
 
   case NODE_DOT2:
@@ -1552,7 +1645,7 @@ codegen(codegen_scope *s, node *tree, int val)
     {
       int n;
 
-      n = gen_values(s, tree, val);
+      n = gen_values(s, tree, val, 0);
       if (n >= 0) {
         if (val) {
           pop_n(n);
@@ -1601,7 +1694,7 @@ codegen(codegen_scope *s, node *tree, int val)
     break;
 
   case NODE_SPLAT:
-    codegen(s, tree, VAL);
+    codegen(s, tree, val);
     break;
 
   case NODE_ASGN:
@@ -1616,7 +1709,7 @@ codegen(codegen_scope *s, node *tree, int val)
       node *t = tree->cdr, *p;
       int rhs = cursp();
 
-      if ((intptr_t)t->car == NODE_ARRAY && nosplat(t->cdr)) {
+      if ((intptr_t)t->car == NODE_ARRAY && t->cdr && nosplat(t->cdr)) {
         /* fixed rhs */
         t = t->cdr;
         while (t) {
@@ -1629,8 +1722,14 @@ codegen(codegen_scope *s, node *tree, int val)
           t = tree->car;
           n = 0;
           while (t) {
-            gen_assignment(s, t->car, rhs+n, NOVAL);
-            n++;
+            if (n < len) {
+              gen_assignment(s, t->car, rhs+n, NOVAL);
+              n++;
+            }
+            else {
+              genop(s, MKOP_A(OP_LOADNIL, rhs+n));
+              gen_assignment(s, t->car, rhs+n, NOVAL);
+            }
             t = t->cdr;
           }
         }
@@ -1687,21 +1786,113 @@ codegen(codegen_scope *s, node *tree, int val)
       mrb_sym sym = sym(tree->cdr->car);
       mrb_int len;
       const char *name = mrb_sym2name_len(s->mrb, sym, &len);
-      int idx;
+      int idx, callargs = -1, vsp = -1;
+
+      if ((len == 2 && name[0] == '|' && name[1] == '|') &&
+          ((intptr_t)tree->car->car == NODE_CONST ||
+           (intptr_t)tree->car->car == NODE_CVAR)) {
+        int onerr, noexc, exc;
+        struct loopinfo *lp;
+
+        onerr = genop(s, MKOP_Bx(OP_ONERR, 0));
+        lp = loop_push(s, LOOP_BEGIN);
+        lp->pc1 = onerr;
+        exc = cursp();
+        codegen(s, tree->car, VAL);
+        lp->type = LOOP_RESCUE;
+        genop(s, MKOP_A(OP_POPERR, 1));
+        noexc = genop(s, MKOP_Bx(OP_JMP, 0));
+        dispatch(s, onerr);
+        genop(s, MKOP_ABC(OP_RESCUE, exc, 0, 0));
+        genop(s, MKOP_A(OP_LOADF, exc));
+        dispatch(s, noexc);
+        loop_pop(s, NOVAL);
+      }
+      else if ((intptr_t)tree->car->car == NODE_CALL) {
+        node *n = tree->car->cdr;
 
-      codegen(s, tree->car, VAL);
+        if (val) {
+          vsp = cursp();
+          push();
+        }
+        codegen(s, n->car, VAL);   /* receiver */
+        idx = new_msym(s, sym(n->cdr->car));
+        if (n->cdr->cdr->car) {
+          int base = cursp()-1;
+          int nargs = gen_values(s, n->cdr->cdr->car->car, VAL, 1);
+
+          /* copy receiver and arguments */
+          if (nargs >= 0) {
+            int i;
+
+            genop(s, MKOP_AB(OP_MOVE, cursp(), base));
+            for (i=0; i<nargs; i++) {
+              genop(s, MKOP_AB(OP_MOVE, cursp()+i+1, base+i+1));
+            }
+            push_n(nargs+1);
+            pop_n(nargs+1);
+            callargs = nargs;
+          }
+          else {
+            /* varargs */
+            push();
+            genop(s, MKOP_AB(OP_MOVE, cursp(), base));
+            genop(s, MKOP_AB(OP_MOVE, cursp()+1, base+1));
+            callargs = CALL_MAXARGS;
+          }
+          genop(s, MKOP_ABC(OP_SEND, cursp(), idx, callargs));
+        }
+        else {
+          genop(s, MKOP_AB(OP_MOVE, cursp(), cursp()-1));
+          genop(s, MKOP_ABC(OP_SEND, cursp(), idx, 0));
+          callargs = 0;
+        }
+        push();
+      }
+      else {
+        codegen(s, tree->car, VAL);
+      }
       if (len == 2 &&
           ((name[0] == '|' && name[1] == '|') ||
            (name[0] == '&' && name[1] == '&'))) {
         int pos;
 
         pop();
-        pos = genop_peep(s, MKOP_AsBx(name[0] == '|' ? OP_JMPIF : OP_JMPNOT, cursp(), 0), NOVAL);
+        if (val) {
+          if (vsp >= 0) {
+            genop(s, MKOP_AB(OP_MOVE, vsp, cursp()));
+          }
+          pos = genop(s, MKOP_AsBx(name[0]=='|'?OP_JMPIF:OP_JMPNOT, cursp(), 0));
+        }
+        else {
+          pos = genop_peep(s, MKOP_AsBx(name[0]=='|'?OP_JMPIF:OP_JMPNOT, cursp(), 0), NOVAL);
+        }
         codegen(s, tree->cdr->cdr->car, VAL);
         pop();
-        gen_assignment(s, tree->car, cursp(), val);
+        if (val && vsp >= 0) {
+          genop(s, MKOP_AB(OP_MOVE, vsp, cursp()));
+        }
+        if ((intptr_t)tree->car->car == NODE_CALL) {
+          mrb_sym m = sym(tree->car->cdr->cdr->car);
+          mrb_sym m2 = attrsym(s, m);
+
+          idx = new_msym(s, m2);
+          pop();
+          if (callargs == CALL_MAXARGS) {
+            genop(s, MKOP_AB(OP_ARYPUSH, cursp(), cursp()+1));
+            pop();
+            genop(s, MKOP_ABC(OP_SEND, cursp(), idx, callargs));
+          }
+          else {
+            pop_n(callargs);
+            genop(s, MKOP_ABC(OP_SEND, cursp(), idx, callargs+1));
+          }
+        }
+        else {
+          gen_assignment(s, tree->car, cursp(), val);
+        }
         dispatch(s, pos);
-        break;
+        goto exit;
       }
       codegen(s, tree->cdr->cdr->car, VAL);
       push(); pop();
@@ -1735,19 +1926,47 @@ codegen(codegen_scope *s, node *tree, int val)
       else {
         genop(s, MKOP_ABC(OP_SEND, cursp(), idx, 1));
       }
+      if (callargs < 0) {
+        gen_assignment(s, tree->car, cursp(), val);
+      }
+      else {
+        if (val && vsp >= 0) {
+          genop(s, MKOP_AB(OP_MOVE, vsp, cursp()));
+        }
+        if (callargs == CALL_MAXARGS) {
+          pop();
+          genop(s, MKOP_AB(OP_ARYPUSH, cursp(), cursp()+1));
+        }
+        else {
+          pop_n(callargs);
+          callargs++;
+        }
+        pop();
+        idx = new_msym(s, attrsym(s,sym(tree->car->cdr->cdr->car)));
+        genop(s, MKOP_ABC(OP_SEND, cursp(), idx, callargs));
+      }
     }
-    gen_assignment(s, tree->car, cursp(), val);
     break;
 
   case NODE_SUPER:
     {
+      codegen_scope *s2 = s;
+      int lv = 0;
       int n = 0, noop = 0, sendv = 0;
 
       push();        /* room for receiver */
+      while (!s2->mscope) {
+        lv++;
+        s2 = s2->prev;
+        if (!s2) break;
+      }
+      genop(s, MKOP_ABx(OP_ARGARY, cursp(), (lv & 0xf)));
+      push(); push();         /* ARGARY pushes two values */
+      pop(); pop();
       if (tree) {
         node *args = tree->car;
         if (args) {
-          n = gen_values(s, args, VAL);
+          n = gen_values(s, args, VAL, 0);
           if (n < 0) {
             n = noop = sendv = 1;
             push();
@@ -1795,8 +2014,7 @@ codegen(codegen_scope *s, node *tree, int val)
 
   case NODE_RETURN:
     if (tree) {
-      codegen(s, tree, VAL);
-      pop();
+      gen_retval(s, tree);
     }
     else {
       genop(s, MKOP_A(OP_LOADNIL, cursp()));
@@ -1822,16 +2040,16 @@ codegen(codegen_scope *s, node *tree, int val)
         if (!s2) break;
       }
       if (s2) ainfo = s2->ainfo;
-      genop(s, MKOP_ABx(OP_BLKPUSH, cursp(), (ainfo<<4)|(lv & 0xf)));
       push();
       if (tree) {
-        n = gen_values(s, tree, VAL);
+        n = gen_values(s, tree, VAL, 0);
         if (n < 0) {
           n = sendv = 1;
           push();
         }
       }
       pop_n(n+1);
+      genop(s, MKOP_ABx(OP_BLKPUSH, cursp(), (ainfo<<4)|(lv & 0xf)));
       if (sendv) n = CALL_MAXARGS;
       genop(s, MKOP_ABC(OP_SEND, cursp(), new_msym(s, mrb_intern_lit(s->mrb, "call")), n));
       if (val) push();
@@ -1868,7 +2086,7 @@ codegen(codegen_scope *s, node *tree, int val)
     break;
 
   case NODE_REDO:
-    if (!s->loop) {
+    if (!s->loop || s->loop->type == LOOP_BEGIN || s->loop->type == LOOP_RESCUE) {
       raise_error(s, "unexpected redo");
     }
     else {
@@ -1877,6 +2095,7 @@ codegen(codegen_scope *s, node *tree, int val)
       }
       genop(s, MKOP_sBx(OP_JMP, s->loop->pc2 - s->pc));
     }
+    if (val) push();
     break;
 
   case NODE_RETRY:
@@ -1911,6 +2130,7 @@ codegen(codegen_scope *s, node *tree, int val)
           genop(s, MKOP_sBx(OP_JMP, lp->pc1 - s->pc));
         }
       }
+      if (val) push();
     }
     break;
 
@@ -1983,13 +2203,13 @@ codegen(codegen_scope *s, node *tree, int val)
 
   case NODE_BACK_REF:
     if (val) {
-      char buf[2] = { '$' };
-      mrb_value str;
+      char buf[3];
       int sym;
 
+      buf[0] = '$';
       buf[1] = (char)(intptr_t)tree;
-      str = mrb_str_new(s->mrb, buf, 2);
-      sym = new_sym(s, mrb_intern_str(s->mrb, str));
+      buf[2] = 0;
+      sym = new_sym(s, mrb_intern_cstr(s->mrb, buf));
       genop(s, MKOP_ABx(OP_GETGLOBAL, cursp(), sym));
       push();
     }
@@ -1997,13 +2217,11 @@ codegen(codegen_scope *s, node *tree, int val)
 
   case NODE_NTH_REF:
     if (val) {
-      int sym;
       mrb_state *mrb = s->mrb;
-      mrb_value fix = mrb_fixnum_value((intptr_t)tree);
-      mrb_value str = mrb_str_buf_new(mrb, 4);
+      mrb_value str;
+      int sym;
 
-      mrb_str_cat_lit(mrb, str, "$");
-      mrb_str_cat_str(mrb, str, mrb_fixnum_to_str(mrb, fix, 10));
+      str = mrb_format(mrb, "$%S", mrb_fixnum_value((mrb_int)(intptr_t)tree));
       sym = new_sym(s, mrb_intern_str(mrb, str));
       genop(s, MKOP_ABx(OP_GETGLOBAL, cursp(), sym));
       push();
@@ -2050,7 +2268,7 @@ codegen(codegen_scope *s, node *tree, int val)
   case NODE_FLOAT:
     if (val) {
       char *p = (char*)tree;
-      mrb_float f = str_to_mrb_float(p);
+      mrb_float f = mrb_float_read(p, NULL);
       int off = new_lit(s, mrb_float_value(s->mrb, f));
 
       genop(s, MKOP_ABx(OP_LOADL, cursp(), off));
@@ -2064,9 +2282,9 @@ codegen(codegen_scope *s, node *tree, int val)
       tree = tree->cdr;
       switch (nt) {
       case NODE_FLOAT:
-        {
+        if (val) {
           char *p = (char*)tree;
-          mrb_float f = str_to_mrb_float(p);
+          mrb_float f = mrb_float_read(p, NULL);
           int off = new_lit(s, mrb_float_value(s->mrb, -f));
 
           genop(s, MKOP_ABx(OP_LOADL, cursp(), off));
@@ -2075,7 +2293,7 @@ codegen(codegen_scope *s, node *tree, int val)
         break;
 
       case NODE_INT:
-        {
+        if (val) {
           char *p = (char*)tree->car;
           int base = (intptr_t)tree->cdr->car;
           mrb_int i;
@@ -2104,7 +2322,7 @@ codegen(codegen_scope *s, node *tree, int val)
         break;
 
       default:
-        {
+        if (val) {
           int sym = new_msym(s, mrb_intern_lit(s->mrb, "-"));
 
           genop(s, MKOP_ABx(OP_LOADI, cursp(), 0));
@@ -2113,6 +2331,9 @@ codegen(codegen_scope *s, node *tree, int val)
           pop(); pop();
           genop(s, MKOP_ABC(OP_SUB, cursp(), sym, 2));
         }
+        else {
+          codegen(s, tree, NOVAL);
+        }
         break;
       }
     }
@@ -2138,7 +2359,11 @@ codegen(codegen_scope *s, node *tree, int val)
     if (val) {
       node *n = tree;
 
-      if (!n) break;
+      if (!n) {
+        genop(s, MKOP_A(OP_LOADNIL, cursp()));
+        push();
+        break;
+      }
       codegen(s, n->car, VAL);
       n = n->cdr;
       while (n) {
@@ -2175,9 +2400,7 @@ codegen(codegen_scope *s, node *tree, int val)
       int ai = mrb_gc_arena_save(s->mrb);
       int sym = new_sym(s, mrb_intern_lit(s->mrb, "Kernel"));
 
-      if (val == NOVAL) { push(); }
-      genop(s, MKOP_A(OP_OCLASS, cursp()));
-      genop(s, MKOP_ABx(OP_GETMCNST, cursp(), sym));
+      genop(s, MKOP_A(OP_LOADSELF, cursp()));
       push();
       codegen(s, tree->car, VAL);
       n = tree->cdr;
@@ -2192,12 +2415,11 @@ codegen(codegen_scope *s, node *tree, int val)
         push();
         n = n->cdr;
       }
-      pop();
-      pop();
+      push();                   /* for block */
+      pop_n(3);
       sym = new_sym(s, mrb_intern_lit(s->mrb, "`"));
       genop(s, MKOP_ABC(OP_SEND, cursp(), sym, 1));
-      if (val == NOVAL) { pop(); }
-      else { push(); }
+      if (val) push();
       mrb_gc_arena_restore(s->mrb, ai);
     }
     break;
@@ -2207,19 +2429,17 @@ codegen(codegen_scope *s, node *tree, int val)
       char *p = (char*)tree->car;
       size_t len = (intptr_t)tree->cdr;
       int ai = mrb_gc_arena_save(s->mrb);
-      int sym = new_sym(s, mrb_intern_lit(s->mrb, "Kernel"));
       int off = new_lit(s, mrb_str_new(s->mrb, p, len));
+      int sym;
 
-      if (val == NOVAL) { push(); }
-      genop(s, MKOP_A(OP_OCLASS, cursp()));
-      genop(s, MKOP_ABx(OP_GETMCNST, cursp(), sym));
+      genop(s, MKOP_A(OP_LOADSELF, cursp()));
       push();
       genop(s, MKOP_ABx(OP_STRING, cursp(), off));
-      pop();
+      push(); push();
+      pop_n(3);
       sym = new_sym(s, mrb_intern_lit(s->mrb, "`"));
       genop(s, MKOP_ABC(OP_SEND, cursp(), sym, 1));
-      if (val == NOVAL) { pop(); }
-      else { push(); }
+      if (val) push();
       mrb_gc_arena_restore(s->mrb, ai);
     }
     break;
@@ -2243,7 +2463,8 @@ codegen(codegen_scope *s, node *tree, int val)
         if (p2) {
           off = new_lit(s, mrb_str_new_cstr(s->mrb, p2));
           genop(s, MKOP_ABx(OP_STRING, cursp(), off));
-        } else {
+        }
+        else {
           genop(s, MKOP_A(OP_LOADNIL, cursp()));
         }
         argc++;
@@ -2294,16 +2515,23 @@ codegen(codegen_scope *s, node *tree, int val)
         pop();
         genop_peep(s, MKOP_AB(OP_STRCAT, cursp(), cursp()+1), VAL);
       }
-      if (n->cdr) {
-        char *p2 = (char*)n->cdr;
+      if (n->cdr->car) {
+        char *p2 = (char*)n->cdr->car;
 
         push();
         off = new_lit(s, mrb_str_new_cstr(s->mrb, p2));
         genop(s, MKOP_ABx(OP_STRING, cursp(), off));
         argc++;
-        pop();
       }
-      pop();
+      if (n->cdr->cdr) {
+        char *p2 = (char*)n->cdr->cdr;
+
+        push();
+        off = new_lit(s, mrb_str_new_cstr(s->mrb, p2));
+        genop(s, MKOP_ABx(OP_STRING, cursp(), off));
+        argc++;
+      }
+      pop_n(argc);
       sym = new_sym(s, mrb_intern_lit(s->mrb, "compile"));
       genop(s, MKOP_ABC(OP_SEND, cursp(), sym, argc));
       mrb_gc_arena_restore(s->mrb, ai);
@@ -2378,7 +2606,8 @@ codegen(codegen_scope *s, node *tree, int val)
       genop(s, MKOP_ABx(OP_LOADSYM, cursp(), b));
       push();
       genop(s, MKOP_A(OP_LOADNIL, cursp()));
-      pop_n(3);
+      push();
+      pop_n(4);
       genop(s, MKOP_ABC(OP_SEND, cursp(), c, 2));
       if (val) {
         push();
@@ -2395,13 +2624,31 @@ codegen(codegen_scope *s, node *tree, int val)
       genop(s, MKOP_A(OP_TCLASS, cursp()));
       push();
       while (t) {
-        int symbol = new_msym(s, sym(t->car));
+        int symbol;
+        if (num >= CALL_MAXARGS - 1) {
+          pop_n(num);
+          genop(s, MKOP_ABC(OP_ARRAY, cursp(), cursp(), num));
+          while (t) {
+            symbol = new_msym(s, sym(t->car));
+            push();
+            genop(s, MKOP_ABx(OP_LOADSYM, cursp(), symbol));
+            pop();
+            genop(s, MKOP_AB(OP_ARYPUSH, cursp(), cursp()+1));
+            t = t->cdr;
+          }
+          num = CALL_MAXARGS;
+          break;
+        }
+        symbol = new_msym(s, sym(t->car));
         genop(s, MKOP_ABx(OP_LOADSYM, cursp(), symbol));
         push();
         t = t->cdr;
         num++;
       }
-      pop_n(num + 1);
+      pop();
+      if (num < CALL_MAXARGS) {
+        pop_n(num);
+      }
       genop(s, MKOP_ABC(OP_SEND, cursp(), undef, num));
       if (val) {
         push();
@@ -2528,6 +2775,8 @@ codegen(codegen_scope *s, node *tree, int val)
   default:
     break;
   }
+ exit:
+  s->rlev = rlev;
 }
 
 static void
@@ -2569,13 +2818,13 @@ scope_new(mrb_state *mrb, codegen_scope *prev, node *lv)
 
   p->icapa = 1024;
   p->iseq = (mrb_code*)mrb_malloc(mrb, sizeof(mrb_code)*p->icapa);
-  p->irep->iseq = p->iseq;
+  p->irep->iseq = NULL;
 
   p->pcapa = 32;
   p->irep->pool = (mrb_value*)mrb_malloc(mrb, sizeof(mrb_value)*p->pcapa);
   p->irep->plen = 0;
 
-  p->scapa = 256;
+  p->scapa = MAXMSYMLEN;
   p->irep->syms = (mrb_sym*)mrb_malloc(mrb, sizeof(mrb_sym)*p->scapa);
   p->irep->slen = 0;
 
@@ -2619,6 +2868,8 @@ scope_new(mrb_state *mrb, codegen_scope *prev, node *lv)
   p->parser = prev->parser;
   p->filename_index = prev->filename_index;
 
+  p->rlev = prev->rlev+1;
+
   return p;
 }
 
@@ -2645,14 +2896,15 @@ scope_finish(codegen_scope *s)
   irep->syms = (mrb_sym*)codegen_realloc(s, irep->syms, sizeof(mrb_sym)*irep->slen);
   irep->reps = (mrb_irep**)codegen_realloc(s, irep->reps, sizeof(mrb_irep*)*irep->rlen);
   if (s->filename) {
-    s->irep->filename = mrb_parser_get_filename(s->parser, s->filename_index);
-    mrb_debug_info_append_file(mrb, s->irep, s->debug_start_pos, s->pc);
+    irep->filename = mrb_parser_get_filename(s->parser, s->filename_index);
+    mrb_debug_info_append_file(mrb, irep, s->debug_start_pos, s->pc);
 
     fname_len = strlen(s->filename);
     fname = (char*)codegen_malloc(s, fname_len + 1);
     memcpy(fname, s->filename, fname_len);
     fname[fname_len] = '\0';
     irep->filename = fname;
+    irep->own_filename = TRUE;
   }
 
   irep->nlocals = s->nlocals;
@@ -2688,8 +2940,7 @@ loop_break(codegen_scope *s, node *tree)
     struct loopinfo *loop;
 
     if (tree) {
-      codegen(s, tree, VAL);
-      pop();
+      gen_retval(s, tree);
     }
 
     loop = s->loop;
@@ -2697,11 +2948,12 @@ loop_break(codegen_scope *s, node *tree)
       genop_peep(s, MKOP_A(OP_POPERR, 1), NOVAL);
       loop = loop->prev;
     }
-    while (loop && loop->type == LOOP_RESCUE) {
+    while (loop && (loop->type == LOOP_RESCUE || loop->type == LOOP_BEGIN)) {
       loop = loop->prev;
     }
     if (!loop) {
-      codegen_error(s, "unexpected break");
+      raise_error(s, "unexpected break");
+      return;
     }
 
     if (loop->type == LOOP_NORMAL) {
@@ -2717,6 +2969,9 @@ loop_break(codegen_scope *s, node *tree)
       loop->pc3 = tmp;
     }
     else {
+      if (!tree) {
+        genop(s, MKOP_A(OP_LOADNIL, cursp()));
+      }
       genop(s, MKOP_AB(OP_RETURN, cursp(), OP_R_BREAK));
     }
   }
@@ -2738,6 +2993,7 @@ mrb_generate_code(mrb_state *mrb, parser_state *p)
 {
   codegen_scope *scope = scope_new(mrb, 0, 0);
   struct RProc *proc;
+  struct mrb_jmpbuf *prev_jmp = mrb->jmp;
 
   if (!scope) {
     return NULL;
@@ -2748,19 +3004,20 @@ mrb_generate_code(mrb_state *mrb, parser_state *p)
   scope->filename_index = p->current_filename_index;
 
   MRB_TRY(&scope->jmp) {
+    mrb->jmp = &scope->jmp; 
     /* prepare irep */
     codegen(scope, p->tree, NOVAL);
     proc = mrb_proc_new(mrb, scope->irep);
     mrb_irep_decref(mrb, scope->irep);
     mrb_pool_close(scope->mpool);
+    proc->c = NULL;
+    mrb->jmp = prev_jmp;
     return proc;
   }
   MRB_CATCH(&scope->jmp) {
-    if (scope->filename == scope->irep->filename) {
-      scope->irep->filename = NULL;
-    }
     mrb_irep_decref(mrb, scope->irep);
     mrb_pool_close(scope->mpool);
+    mrb->jmp = prev_jmp;
     return NULL;
   }
   MRB_END_EXC(&scope->jmp);
index 532a832..9636dd7 100644 (file)
@@ -4,8 +4,8 @@
 ** See Copyright Notice in mruby.h
 */
 
-#ifndef NODE_H
-#define NODE_H
+#ifndef MRUBY_COMPILER_NODE_H
+#define MRUBY_COMPILER_NODE_H
 
 enum node_type {
   NODE_METHOD,
@@ -38,6 +38,7 @@ enum node_type {
   NODE_CVDECL,
   NODE_OP_ASGN,
   NODE_CALL,
+  NODE_SCALL,
   NODE_FCALL,
   NODE_VCALL,
   NODE_SUPER,
@@ -114,4 +115,4 @@ enum node_type {
   NODE_LAST
 };
 
-#endif  /* NODE_H */
+#endif  /* MRUBY_COMPILER_NODE_H */
index 032e9e1..5d5ef29 100644 (file)
 #include <errno.h>
 #include <stdlib.h>
 #include <string.h>
-#include "mruby.h"
-#include "mruby/compile.h"
-#include "mruby/proc.h"
-#include "mruby/error.h"
+#include <mruby.h>
+#include <mruby/compile.h>
+#include <mruby/proc.h>
+#include <mruby/error.h>
+#include <mruby/throw.h>
 #include "node.h"
-#include "mruby/throw.h"
 
 #define YYLEX_PARAM p
 
@@ -41,6 +41,7 @@ static void yyerror(parser_state *p, const char *s);
 static void yywarn(parser_state *p, const char *s);
 static void yywarning(parser_state *p, const char *s);
 static void backref_error(parser_state *p, node *n);
+static void void_expr_error(parser_state *p, node *n);
 static void tokadd(parser_state *p, int32_t c);
 
 #define identchar(c) (ISALNUM(c) || (c) == '_' || !ISASCII(c))
@@ -72,6 +73,8 @@ typedef unsigned int stack_type;
 
 #define sym(x) ((mrb_sym)(intptr_t)(x))
 #define nsym(x) ((node*)(intptr_t)(x))
+#define nint(x) ((node*)(intptr_t)(x))
+#define intn(x) ((int)(intptr_t)(x))
 
 static inline mrb_sym
 intern_cstr_gen(parser_state *p, const char *s)
@@ -308,6 +311,12 @@ new_rescue(parser_state *p, node *body, node *resq, node *els)
   return list4((node*)NODE_RESCUE, body, resq, els);
 }
 
+static node*
+new_mod_rescue(parser_state *p, node *body, node *resq)
+{
+  return new_rescue(p, body, list1(list3(0, 0, resq)), 0);
+}
+
 /* (:ensure body ensure) */
 static node*
 new_ensure(parser_state *p, node *a, node *b)
@@ -347,6 +356,7 @@ new_alias(parser_state *p, mrb_sym a, mrb_sym b)
 static node*
 new_if(parser_state *p, node *a, node *b, node *c)
 {
+  void_expr_error(p, a);
   return list4((node*)NODE_IF, a, b, c);
 }
 
@@ -354,6 +364,7 @@ new_if(parser_state *p, node *a, node *b, node *c)
 static node*
 new_unless(parser_state *p, node *a, node *b, node *c)
 {
+  void_expr_error(p, a);
   return list4((node*)NODE_IF, a, c, b);
 }
 
@@ -361,6 +372,7 @@ new_unless(parser_state *p, node *a, node *b, node *c)
 static node*
 new_while(parser_state *p, node *a, node *b)
 {
+  void_expr_error(p, a);
   return cons((node*)NODE_WHILE, cons(a, b));
 }
 
@@ -368,6 +380,7 @@ new_while(parser_state *p, node *a, node *b)
 static node*
 new_until(parser_state *p, node *a, node *b)
 {
+  void_expr_error(p, a);
   return cons((node*)NODE_UNTIL, cons(a, b));
 }
 
@@ -375,6 +388,7 @@ new_until(parser_state *p, node *a, node *b)
 static node*
 new_for(parser_state *p, node *v, node *o, node *b)
 {
+  void_expr_error(p, o);
   return list4((node*)NODE_FOR, v, o, b);
 }
 
@@ -385,6 +399,7 @@ new_case(parser_state *p, node *a, node *b)
   node *n = list2((node*)NODE_CASE, a);
   node *n2 = n;
 
+  void_expr_error(p, a);
   while (n2->cdr) {
     n2 = n2->cdr;
   }
@@ -408,9 +423,10 @@ new_self(parser_state *p)
 
 /* (:call a b c) */
 static node*
-new_call(parser_state *p, node *a, mrb_sym b, node *c)
+new_call(parser_state *p, node *a, mrb_sym b, node *c, int pass)
 {
-  node *n = list4((node*)NODE_CALL, a, nsym(b), c);
+  node *n = list4(nint(pass?NODE_CALL:NODE_SCALL), a, nsym(b), c);
+  void_expr_error(p, a);
   NODE_LINENO(n, a);
   return n;
 }
@@ -506,6 +522,7 @@ new_dot3(parser_state *p, node *a, node *b)
 static node*
 new_colon2(parser_state *p, node *b, mrb_sym c)
 {
+  void_expr_error(p, b);
   return cons((node*)NODE_COLON2, cons(b, nsym(c)));
 }
 
@@ -613,6 +630,7 @@ new_undef(parser_state *p, mrb_sym sym)
 static node*
 new_class(parser_state *p, node *c, node *s, node *b)
 {
+  void_expr_error(p, s);
   return list4((node*)NODE_CLASS, c, s, cons(locals_node(p), b));
 }
 
@@ -620,6 +638,7 @@ new_class(parser_state *p, node *c, node *s, node *b)
 static node*
 new_sclass(parser_state *p, node *o, node *b)
 {
+  void_expr_error(p, o);
   return list3((node*)NODE_SCLASS, o, cons(locals_node(p), b));
 }
 
@@ -641,6 +660,7 @@ new_def(parser_state *p, mrb_sym m, node *a, node *b)
 static node*
 new_sdef(parser_state *p, node *o, mrb_sym m, node *a, node *b)
 {
+  void_expr_error(p, o);
   return list6((node*)NODE_SDEF, o, nsym(m), locals_node(p), a, b);
 }
 
@@ -693,6 +713,7 @@ new_lambda(parser_state *p, node *a, node *b)
 static node*
 new_asgn(parser_state *p, node *a, node *b)
 {
+  void_expr_error(p, b);
   return cons((node*)NODE_ASGN, cons(a, b));
 }
 
@@ -700,6 +721,7 @@ new_asgn(parser_state *p, node *a, node *b)
 static node*
 new_masgn(parser_state *p, node *a, node *b)
 {
+  void_expr_error(p, b);
   return cons((node*)NODE_MASGN, cons(a, b));
 }
 
@@ -707,6 +729,7 @@ new_masgn(parser_state *p, node *a, node *b)
 static node*
 new_op_asgn(parser_state *p, node *a, mrb_sym op, node *b)
 {
+  void_expr_error(p, b);
   return list4((node*)NODE_OP_ASGN, a, nsym(op), b);
 }
 
@@ -714,7 +737,7 @@ new_op_asgn(parser_state *p, node *a, mrb_sym op, node *b)
 static node*
 new_int(parser_state *p, const char *s, int base)
 {
-  return list3((node*)NODE_INT, (node*)strdup(s), (node*)(intptr_t)base);
+  return list3((node*)NODE_INT, (node*)strdup(s), nint(base));
 }
 
 /* (:float . i) */
@@ -728,7 +751,7 @@ new_float(parser_state *p, const char *s)
 static node*
 new_str(parser_state *p, const char *s, int len)
 {
-  return cons((node*)NODE_STR, cons((node*)strndup(s, len), (node*)(intptr_t)len));
+  return cons((node*)NODE_STR, cons((node*)strndup(s, len), nint(len)));
 }
 
 /* (:dstr . a) */
@@ -742,7 +765,7 @@ new_dstr(parser_state *p, node *a)
 static node*
 new_xstr(parser_state *p, const char *s, int len)
 {
-  return cons((node*)NODE_XSTR, cons((node*)strndup(s, len), (node*)(intptr_t)len));
+  return cons((node*)NODE_XSTR, cons((node*)strndup(s, len), nint(len)));
 }
 
 /* (:xstr . a) */
@@ -759,14 +782,14 @@ new_dsym(parser_state *p, node *a)
   return cons((node*)NODE_DSYM, new_dstr(p, a));
 }
 
-/* (:str . (a . a)) */
+/* (:regx . (s . (opt . enc))) */
 static node*
 new_regx(parser_state *p, const char *p1, const char* p2, const char* p3)
 {
   return cons((node*)NODE_REGX, cons((node*)p1, cons((node*)p2, (node*)p3)));
 }
 
-/* (:dregx . a) */
+/* (:dregx . (a . b)) */
 static node*
 new_dregx(parser_state *p, node *a, node *b)
 {
@@ -777,14 +800,14 @@ new_dregx(parser_state *p, node *a, node *b)
 static node*
 new_back_ref(parser_state *p, int n)
 {
-  return cons((node*)NODE_BACK_REF, (node*)(intptr_t)n);
+  return cons((node*)NODE_BACK_REF, nint(n));
 }
 
 /* (:nthref . n) */
 static node*
 new_nth_ref(parser_state *p, int n)
 {
-  return cons((node*)NODE_NTH_REF, (node*)(intptr_t)n);
+  return cons((node*)NODE_NTH_REF, nint(n));
 }
 
 /* (:heredoc . a) */
@@ -826,14 +849,15 @@ new_symbols(parser_state *p, node *a)
 static node*
 call_uni_op(parser_state *p, node *recv, const char *m)
 {
-  return new_call(p, recv, intern_cstr(m), 0);
+  void_expr_error(p, recv);
+  return new_call(p, recv, intern_cstr(m), 0, 1);
 }
 
 /* (:call a op b) */
 static node*
 call_bin_op(parser_state *p, node *recv, const char *m, node *arg1)
 {
-  return new_call(p, recv, intern_cstr(m), list1(list1(arg1)));
+  return new_call(p, recv, intern_cstr(m), list1(list1(arg1)), 1);
 }
 
 static void
@@ -852,19 +876,25 @@ call_with_block(parser_state *p, node *a, node *b)
 {
   node *n;
 
-  if (a->car == (node*)NODE_SUPER ||
-      a->car == (node*)NODE_ZSUPER) {
+  switch ((enum node_type)intn(a->car)) {
+  case NODE_SUPER:
+  case NODE_ZSUPER:
     if (!a->cdr) a->cdr = cons(0, b);
     else {
       args_with_block(p, a->cdr, b);
     }
-  }
-  else {
+    break;
+  case NODE_CALL:
+  case NODE_FCALL:
+  case NODE_SCALL:
     n = a->cdr->cdr->cdr;
     if (!n->car) n->car = cons(0, b);
     else {
       args_with_block(p, n->car, b);
     }
+    break;
+  default:
+    break;
   }
 }
 
@@ -894,7 +924,7 @@ ret_args(parser_state *p, node *n)
 static void
 assignable(parser_state *p, node *lhs)
 {
-  if ((int)(intptr_t)lhs->car == NODE_LVAR) {
+  if (intn(lhs->car) == NODE_LVAR) {
     local_add(p, sym(lhs->cdr));
   }
 }
@@ -904,7 +934,7 @@ var_reference(parser_state *p, node *lhs)
 {
   node *n;
 
-  if ((int)(intptr_t)lhs->car == NODE_LVAR) {
+  if (intn(lhs->car) == NODE_LVAR) {
     if (!local_var_p(p, sym(lhs->cdr))) {
       n = new_fcall(p, sym(lhs->cdr), 0);
       cons_free(lhs);
@@ -920,7 +950,7 @@ typedef enum mrb_string_type  string_type;
 static node*
 new_strterm(parser_state *p, string_type type, int term, int paren)
 {
-  return cons((node*)(intptr_t)type, cons((node*)0, cons((node*)(intptr_t)paren, (node*)(intptr_t)term)));
+  return cons(nint(type), cons((node*)0, cons(nint(paren), nint(term))));
 }
 
 static void
@@ -1001,10 +1031,10 @@ heredoc_end(parser_state *p)
   }
   else {
     /* next heredoc */
-    p->lex_strterm->car = (node*)(intptr_t)parsing_heredoc_inf(p)->type;
+    p->lex_strterm->car = nint(parsing_heredoc_inf(p)->type);
   }
 }
-#define is_strterm_type(p,str_func) ((int)(intptr_t)((p)->lex_strterm->car) & (str_func))
+#define is_strterm_type(p,str_func) (intn((p)->lex_strterm->car) & (str_func))
 
 /* xxx ----------------------------- */
 
@@ -1076,7 +1106,7 @@ heredoc_end(parser_state *p)
 
 %token <id>  tIDENTIFIER tFID tGVAR tIVAR tCONSTANT tCVAR tLABEL
 %token <nd>  tINTEGER tFLOAT tCHAR tXSTRING tREGEXP
-%token <nd>  tSTRING tSTRING_PART tSTRING_MID
+%token <nd>  tSTRING tSTRING_PART tSTRING_MID tLABEL_END
 %token <nd>  tNTH_REF tBACK_REF
 %token <num> tREGEXP_END
 
@@ -1084,12 +1114,12 @@ heredoc_end(parser_state *p)
 %type <nd> literal numeric cpath symbol
 %type <nd> top_compstmt top_stmts top_stmt
 %type <nd> bodystmt compstmt stmts stmt expr arg primary command command_call method_call
-%type <nd> expr_value arg_value primary_value
+%type <nd> expr_value arg_rhs primary_value
 %type <nd> if_tail opt_else case_body cases opt_rescue exc_list exc_var opt_ensure
 %type <nd> args call_args opt_call_args
 %type <nd> paren_args opt_paren_args variable
 %type <nd> command_args aref_args opt_block_arg block_arg var_ref var_lhs
-%type <nd> command_asgn mrhs superclass block_call block_command
+%type <nd> command_asgn command_rhs mrhs superclass block_call block_command
 %type <nd> f_block_optarg f_block_opt
 %type <nd> f_arglist f_args f_arg f_arg_item f_optarg f_marg f_marg_list f_margs
 %type <nd> assoc_list assocs assoc undef_list backref for_var
@@ -1100,6 +1130,7 @@ heredoc_end(parser_state *p)
 %type <id> fsym sym basic_symbol operation operation2 operation3
 %type <id> cname fname op f_rest_arg f_block_arg opt_f_block_arg f_norm_arg f_opt_asgn
 %type <nd> heredoc words symbols
+%type <num> call_op call_op2     /* 0:'&.', 1:'.', 2:'::' */
 
 %token tUPLUS             /* unary+ */
 %token tUMINUS            /* unary- */
@@ -1128,6 +1159,7 @@ heredoc_end(parser_state *p)
 %token tSTAR              /* * */
 %token tAMPER             /* & */
 %token tLAMBDA            /* -> */
+%token tANDDOT            /* &. */
 %token tSYMBEG tREGEXP_BEG tWORDS_BEG tSYMBOLS_BEG
 %token tSTRING_BEG tXSTRING_BEG tSTRING_DVAR tLAMBEG
 %token <nd> tHEREDOC_BEG  /* <<, <<- */
@@ -1291,11 +1323,11 @@ stmt            : keyword_alias fsym {p->lstate = EXPR_FNAME;} fsym
                     }
                 | stmt modifier_rescue stmt
                     {
-                      $$ = new_rescue(p, $1, list1(list3(0, 0, $3)), 0);
+                      $$ = new_mod_rescue(p, $1, $3);
                     }
                 | keyword_END '{' compstmt '}'
                     {
-                      yyerror(p, "END not suported");
+                      yyerror(p, "END not supported");
                       $$ = new_postexe(p, $3);
                     }
                 | command_asgn
@@ -1303,59 +1335,63 @@ stmt            : keyword_alias fsym {p->lstate = EXPR_FNAME;} fsym
                     {
                       $$ = new_masgn(p, $1, $3);
                     }
-                | var_lhs tOP_ASGN command_call
+                | lhs '=' mrhs
                     {
-                      $$ = new_op_asgn(p, $1, $2, $3);
+                      $$ = new_asgn(p, $1, new_array(p, $3));
                     }
-                | primary_value '[' opt_call_args rbracket tOP_ASGN command_call
+                | mlhs '=' arg
                     {
-                      $$ = new_op_asgn(p, new_call(p, $1, intern("[]",2), $3), $5, $6);
+                      $$ = new_masgn(p, $1, $3);
                     }
-                | primary_value '.' tIDENTIFIER tOP_ASGN command_call
+                | mlhs '=' mrhs
                     {
-                      $$ = new_op_asgn(p, new_call(p, $1, $3, 0), $4, $5);
+                      $$ = new_masgn(p, $1, new_array(p, $3));
                     }
-                | primary_value '.' tCONSTANT tOP_ASGN command_call
+                | expr
+                ;
+
+command_asgn    : lhs '=' command_rhs
                     {
-                      $$ = new_op_asgn(p, new_call(p, $1, $3, 0), $4, $5);
+                      $$ = new_asgn(p, $1, $3);
                     }
-                | primary_value tCOLON2 tCONSTANT tOP_ASGN command_call
+                | var_lhs tOP_ASGN command_rhs
                     {
-                      yyerror(p, "constant re-assignment");
-                      $$ = 0;
+                      $$ = new_op_asgn(p, $1, $2, $3);
                     }
-                | primary_value tCOLON2 tIDENTIFIER tOP_ASGN command_call
+                | primary_value '[' opt_call_args rbracket tOP_ASGN command_rhs
                     {
-                      $$ = new_op_asgn(p, new_call(p, $1, $3, 0), $4, $5);
+                      $$ = new_op_asgn(p, new_call(p, $1, intern("[]",2), $3, '.'), $5, $6);
                     }
-                | backref tOP_ASGN command_call
+                | primary_value call_op tIDENTIFIER tOP_ASGN command_rhs
                     {
-                      backref_error(p, $1);
-                      $$ = new_begin(p, 0);
+                      $$ = new_op_asgn(p, new_call(p, $1, $3, 0, $2), $4, $5);
                     }
-                | lhs '=' mrhs
+                | primary_value call_op tCONSTANT tOP_ASGN command_rhs
                     {
-                      $$ = new_asgn(p, $1, new_array(p, $3));
+                      $$ = new_op_asgn(p, new_call(p, $1, $3, 0, $2), $4, $5);
                     }
-                | mlhs '=' arg_value
+                | primary_value tCOLON2 tCONSTANT tOP_ASGN command_call
                     {
-                      $$ = new_masgn(p, $1, $3);
+                      yyerror(p, "constant re-assignment");
+                      $$ = 0;
                     }
-                | mlhs '=' mrhs
+                | primary_value tCOLON2 tIDENTIFIER tOP_ASGN command_rhs
                     {
-                      $$ = new_masgn(p, $1, new_array(p, $3));
+                      $$ = new_op_asgn(p, new_call(p, $1, $3, 0, tCOLON2), $4, $5);
                     }
-                | expr
-                ;
-
-command_asgn    : lhs '=' command_call
+                | backref tOP_ASGN command_rhs
                     {
-                      $$ = new_asgn(p, $1, $3);
+                      backref_error(p, $1);
+                      $$ = new_begin(p, 0);
                     }
-                | lhs '=' command_asgn
+               ;
+
+command_rhs     : command_call   %prec tOP_ASGN
+                | command_call modifier_rescue stmt
                     {
-                      $$ = new_asgn(p, $1, $3);
+                      $$ = new_mod_rescue(p, $1, $3);
                     }
+                | command_asgn
                 ;
 
 
@@ -1382,7 +1418,9 @@ expr            : command_call
 expr_value      : expr
                     {
                       if (!$1) $$ = new_nil(p);
-                      else $$ = $1;
+                      else {
+                        $$ = $1;
+                      }
                     }
                 ;
 
@@ -1391,7 +1429,10 @@ command_call    : command
                 ;
 
 block_command   : block_call
-                | block_call dot_or_colon operation2 command_args
+                | block_call call_op2 operation2 command_args
+                    {
+                      $$ = new_call(p, $1, $3, $4, $2);
+                    }
                 ;
 
 cmd_brace_block : tLBRACE_ARG
@@ -1416,23 +1457,23 @@ command         : operation command_args       %prec tLOWEST
                       args_with_block(p, $2, $3);
                       $$ = new_fcall(p, $1, $2);
                     }
-                | primary_value '.' operation2 command_args     %prec tLOWEST
+                | primary_value call_op operation2 command_args     %prec tLOWEST
                     {
-                      $$ = new_call(p, $1, $3, $4);
+                      $$ = new_call(p, $1, $3, $4, $2);
                     }
-                | primary_value '.' operation2 command_args cmd_brace_block
+                | primary_value call_op operation2 command_args cmd_brace_block
                     {
                       args_with_block(p, $4, $5);
-                      $$ = new_call(p, $1, $3, $4);
+                      $$ = new_call(p, $1, $3, $4, $2);
                    }
                 | primary_value tCOLON2 operation2 command_args %prec tLOWEST
                     {
-                      $$ = new_call(p, $1, $3, $4);
+                      $$ = new_call(p, $1, $3, $4, tCOLON2);
                     }
                 | primary_value tCOLON2 operation2 command_args cmd_brace_block
                     {
                       args_with_block(p, $4, $5);
-                      $$ = new_call(p, $1, $3, $4);
+                      $$ = new_call(p, $1, $3, $4, tCOLON2);
                     }
                 | keyword_super command_args
                     {
@@ -1548,19 +1589,19 @@ mlhs_node       : variable
                     }
                 | primary_value '[' opt_call_args rbracket
                     {
-                      $$ = new_call(p, $1, intern("[]",2), $3);
+                      $$ = new_call(p, $1, intern("[]",2), $3, '.');
                     }
-                | primary_value '.' tIDENTIFIER
+                | primary_value call_op tIDENTIFIER
                     {
-                      $$ = new_call(p, $1, $3, 0);
+                      $$ = new_call(p, $1, $3, 0, $2);
                     }
                 | primary_value tCOLON2 tIDENTIFIER
                     {
-                      $$ = new_call(p, $1, $3, 0);
+                      $$ = new_call(p, $1, $3, 0, tCOLON2);
                     }
-                | primary_value '.' tCONSTANT
+                | primary_value call_op tCONSTANT
                     {
-                      $$ = new_call(p, $1, $3, 0);
+                      $$ = new_call(p, $1, $3, 0, $2);
                     }
                 | primary_value tCOLON2 tCONSTANT
                     {
@@ -1587,19 +1628,19 @@ lhs             : variable
                     }
                 | primary_value '[' opt_call_args rbracket
                     {
-                      $$ = new_call(p, $1, intern("[]",2), $3);
+                      $$ = new_call(p, $1, intern("[]",2), $3, '.');
                     }
-                | primary_value '.' tIDENTIFIER
+                | primary_value call_op tIDENTIFIER
                     {
-                      $$ = new_call(p, $1, $3, 0);
+                      $$ = new_call(p, $1, $3, 0, $2);
                     }
                 | primary_value tCOLON2 tIDENTIFIER
                     {
-                      $$ = new_call(p, $1, $3, 0);
+                      $$ = new_call(p, $1, $3, 0, tCOLON2);
                     }
-                | primary_value '.' tCONSTANT
+                | primary_value call_op tCONSTANT
                     {
-                      $$ = new_call(p, $1, $3, 0);
+                      $$ = new_call(p, $1, $3, 0, $2);
                     }
                 | primary_value tCOLON2 tCONSTANT
                     {
@@ -1637,6 +1678,7 @@ cpath           : tCOLON3 cname
                     }
                 | primary_value tCOLON2 cname
                     {
+                      void_expr_error(p, $1);
                       $$ = cons($1, nsym($3));
                     }
                 ;
@@ -1715,49 +1757,41 @@ reswords        : keyword__LINE__ | keyword__FILE__ | keyword__ENCODING__
                 | keyword_while | keyword_until
                 ;
 
-arg             : lhs '=' arg
+arg             : lhs '=' arg_rhs
                     {
                       $$ = new_asgn(p, $1, $3);
                     }
-                | lhs '=' arg modifier_rescue arg
-                    {
-                      $$ = new_asgn(p, $1, new_rescue(p, $3, list1(list3(0, 0, $5)), 0));
-                    }
-                | var_lhs tOP_ASGN arg
+                | var_lhs tOP_ASGN arg_rhs
                     {
                       $$ = new_op_asgn(p, $1, $2, $3);
                     }
-                | var_lhs tOP_ASGN arg modifier_rescue arg
+                | primary_value '[' opt_call_args rbracket tOP_ASGN arg_rhs
                     {
-                      $$ = new_op_asgn(p, $1, $2, new_rescue(p, $3, list1(list3(0, 0, $5)), 0));
+                      $$ = new_op_asgn(p, new_call(p, $1, intern("[]",2), $3, '.'), $5, $6);
                     }
-                | primary_value '[' opt_call_args rbracket tOP_ASGN arg
+                | primary_value call_op tIDENTIFIER tOP_ASGN arg_rhs
                     {
-                      $$ = new_op_asgn(p, new_call(p, $1, intern("[]",2), $3), $5, $6);
+                      $$ = new_op_asgn(p, new_call(p, $1, $3, 0, $2), $4, $5);
                     }
-                | primary_value '.' tIDENTIFIER tOP_ASGN arg
+                | primary_value call_op tCONSTANT tOP_ASGN arg_rhs
                     {
-                      $$ = new_op_asgn(p, new_call(p, $1, $3, 0), $4, $5);
+                      $$ = new_op_asgn(p, new_call(p, $1, $3, 0, $2), $4, $5);
                     }
-                | primary_value '.' tCONSTANT tOP_ASGN arg
+                | primary_value tCOLON2 tIDENTIFIER tOP_ASGN arg_rhs
                     {
-                      $$ = new_op_asgn(p, new_call(p, $1, $3, 0), $4, $5);
+                      $$ = new_op_asgn(p, new_call(p, $1, $3, 0, tCOLON2), $4, $5);
                     }
-                | primary_value tCOLON2 tIDENTIFIER tOP_ASGN arg
-                    {
-                      $$ = new_op_asgn(p, new_call(p, $1, $3, 0), $4, $5);
-                    }
-                | primary_value tCOLON2 tCONSTANT tOP_ASGN arg
+                | primary_value tCOLON2 tCONSTANT tOP_ASGN arg_rhs
                     {
                       yyerror(p, "constant re-assignment");
                       $$ = new_begin(p, 0);
                     }
-                | tCOLON3 tCONSTANT tOP_ASGN arg
+                | tCOLON3 tCONSTANT tOP_ASGN arg_rhs
                     {
                       yyerror(p, "constant re-assignment");
                       $$ = new_begin(p, 0);
                     }
-                | backref tOP_ASGN arg
+                | backref tOP_ASGN arg_rhs
                     {
                       backref_error(p, $1);
                       $$ = new_begin(p, 0);
@@ -1896,20 +1930,13 @@ arg             : lhs '=' arg
                     }
                 ;
 
-arg_value       : arg
-                    {
-                      $$ = $1;
-                      if (!$$) $$ = new_nil(p);
-                    }
-                ;
-
 aref_args       : none
                 | args trailer
                     {
                       $$ = $1;
                       NODE_LINENO($$, $1);
                     }
-                | args ',' assocs trailer
+                | args comma assocs trailer
                     {
                       $$ = push($1, new_hash(p, $3));
                     }
@@ -1920,6 +1947,18 @@ aref_args       : none
                     }
                 ;
 
+arg_rhs         : arg %prec tOP_ASGN
+                    {
+                      $$ = $1;
+                    }
+                | arg modifier_rescue arg
+                    {
+                      void_expr_error(p, $1);
+                      void_expr_error(p, $3);
+                      $$ = new_mod_rescue(p, $1, $3);
+                    }
+                ;
+
 paren_args      : '(' opt_call_args rparen
                     {
                       $$ = $2;
@@ -1937,7 +1976,7 @@ opt_call_args   : none
                       $$ = cons($1,0);
                       NODE_LINENO($$, $1);
                     }
-                | args ',' assocs ','
+                | args comma assocs ','
                     {
                       $$ = cons(push($1, new_hash(p, $3)), 0);
                       NODE_LINENO($$, $1);
@@ -1951,6 +1990,7 @@ opt_call_args   : none
 
 call_args       : command
                     {
+                      void_expr_error(p, $1);
                       $$ = cons(list1($1), 0);
                       NODE_LINENO($$, $1);
                     }
@@ -1964,7 +2004,7 @@ call_args       : command
                       $$ = cons(list1(new_hash(p, $1)), $2);
                       NODE_LINENO($$, $1);
                     }
-                | args ',' assocs opt_block_arg
+                | args comma assocs opt_block_arg
                     {
                       $$ = cons(push($1, new_hash(p, $3)), $4);
                       NODE_LINENO($$, $1);
@@ -1987,13 +2027,13 @@ command_args    :  {
                     }
                 ;
 
-block_arg       : tAMPER arg_value
+block_arg       : tAMPER arg
                     {
                       $$ = new_block_arg(p, $2);
                     }
                 ;
 
-opt_block_arg   : ',' block_arg
+opt_block_arg   : comma block_arg
                     {
                       $$ = $2;
                     }
@@ -2003,44 +2043,47 @@ opt_block_arg   : ',' block_arg
                     }
                 ;
 
-args            : arg_value
+comma           : ','
+                | ','  heredoc_bodies
+                ;
+
+args            : arg
                     {
+                      void_expr_error(p, $1);
                       $$ = cons($1, 0);
                       NODE_LINENO($$, $1);
                     }
-                | tSTAR arg_value
+                | tSTAR arg
                     {
+                      void_expr_error(p, $2);
                       $$ = cons(new_splat(p, $2), 0);
                       NODE_LINENO($$, $2);
                     }
-                | args ',' arg_value
+                | args comma arg
                     {
+                      void_expr_error(p, $3);
                       $$ = push($1, $3);
                     }
-                | args ',' tSTAR arg_value
+                | args comma tSTAR arg
                     {
+                      void_expr_error(p, $4);
                       $$ = push($1, new_splat(p, $4));
                     }
-                | args ',' heredoc_bodies arg_value
-                    {
-                      $$ = push($1, $4);
-                    }
-                | args ',' heredoc_bodies tSTAR arg_value
-                    {
-                      $$ = push($1, new_splat(p, $5));
-                    }
                 ;
 
-mrhs            : args ',' arg_value
+mrhs            : args comma arg
                     {
+                      void_expr_error(p, $3);
                       $$ = push($1, $3);
                     }
-                | args ',' tSTAR arg_value
+                | args comma tSTAR arg
                     {
+                      void_expr_error(p, $4);
                       $$ = push($1, new_splat(p, $4));
                     }
-                | tSTAR arg_value
+                | tSTAR arg
                     {
+                      void_expr_error(p, $2);
                       $$ = list1(new_splat(p, $2));
                     }
                 ;
@@ -2072,14 +2115,14 @@ primary         : literal
                       $<stack>$ = p->cmdarg_stack;
                       p->cmdarg_stack = 0;
                     }
-                  expr {p->lstate = EXPR_ENDARG;} rparen
+                  stmt {p->lstate = EXPR_ENDARG;} rparen
                     {
                       p->cmdarg_stack = $<stack>2;
                       $$ = $3;
                     }
                 | tLPAREN_ARG {p->lstate = EXPR_ENDARG;} rparen
                     {
-                      $$ = 0;
+                      $$ = new_nil(p);
                     }
                 | tLPAREN compstmt ')'
                     {
@@ -2107,17 +2150,9 @@ primary         : literal
                     {
                       $$ = new_return(p, 0);
                     }
-                | keyword_yield '(' call_args rparen
-                    {
-                      $$ = new_yield(p, $3);
-                    }
-                | keyword_yield '(' rparen
-                    {
-                      $$ = new_yield(p, 0);
-                    }
-                | keyword_yield
+                | keyword_yield opt_paren_args
                     {
-                      $$ = new_yield(p, 0);
+                      $$ = new_yield(p, $2);
                     }
                 | keyword_not '(' expr rparen
                     {
@@ -2228,7 +2263,7 @@ primary         : literal
                     }
                   term
                     {
-                      $<nd>$ = cons(local_switch(p), (node*)(intptr_t)p->in_single);
+                      $<nd>$ = cons(local_switch(p), nint(p->in_single));
                       p->in_single = 0;
                     }
                   bodystmt
@@ -2238,7 +2273,7 @@ primary         : literal
                       SET_LINENO($$, $1);
                       local_resume(p, $<nd>6->car);
                       p->in_def = $<num>4;
-                      p->in_single = (int)(intptr_t)$<nd>6->cdr;
+                      p->in_single = intn($<nd>6->cdr);
                     }
                 | keyword_module
                   cpath
@@ -2432,7 +2467,7 @@ block_param     : f_arg ',' f_block_optarg ',' f_rest_arg opt_f_block_arg
                     }
                 | f_arg ','
                     {
-                      $$ = new_args(p, $1, 0, 1, 0, 0);
+                      $$ = new_args(p, $1, 0, 0, 0, 0);
                     }
                 | f_arg ',' f_rest_arg ',' f_arg opt_f_block_arg
                     {
@@ -2560,18 +2595,18 @@ block_call      : command do_block
                       }
                       $$ = $1;
                     }
-                | block_call dot_or_colon operation2 opt_paren_args
+                | block_call call_op2 operation2 opt_paren_args
                     {
-                      $$ = new_call(p, $1, $3, $4);
+                      $$ = new_call(p, $1, $3, $4, $2);
                     }
-                | block_call dot_or_colon operation2 opt_paren_args brace_block
+                | block_call call_op2 operation2 opt_paren_args brace_block
                     {
-                      $$ = new_call(p, $1, $3, $4);
+                      $$ = new_call(p, $1, $3, $4, $2);
                       call_with_block(p, $$, $5);
                     }
-                | block_call dot_or_colon operation2 command_args do_block
+                | block_call call_op2 operation2 command_args do_block
                     {
-                      $$ = new_call(p, $1, $3, $4);
+                      $$ = new_call(p, $1, $3, $4, $2);
                       call_with_block(p, $$, $5);
                     }
                 ;
@@ -2580,25 +2615,25 @@ method_call     : operation paren_args
                     {
                       $$ = new_fcall(p, $1, $2);
                     }
-                | primary_value '.' operation2 opt_paren_args
+                | primary_value call_op operation2 opt_paren_args
                     {
-                      $$ = new_call(p, $1, $3, $4);
+                      $$ = new_call(p, $1, $3, $4, $2);
                     }
                 | primary_value tCOLON2 operation2 paren_args
                     {
-                      $$ = new_call(p, $1, $3, $4);
+                      $$ = new_call(p, $1, $3, $4, tCOLON2);
                     }
                 | primary_value tCOLON2 operation3
                     {
-                      $$ = new_call(p, $1, $3, 0);
+                      $$ = new_call(p, $1, $3, 0, tCOLON2);
                     }
-                | primary_value '.' paren_args
+                | primary_value call_op paren_args
                     {
-                      $$ = new_call(p, $1, intern("call",4), $3);
+                      $$ = new_call(p, $1, intern("call",4), $3, $2);
                     }
                 | primary_value tCOLON2 paren_args
                     {
-                      $$ = new_call(p, $1, intern("call",4), $3);
+                      $$ = new_call(p, $1, intern("call",4), $3, tCOLON2);
                     }
                 | keyword_super paren_args
                     {
@@ -2610,7 +2645,7 @@ method_call     : operation paren_args
                     }
                 | primary_value '[' opt_call_args rbracket
                     {
-                      $$ = new_call(p, $1, intern("[]",2), $3);
+                      $$ = new_call(p, $1, intern("[]",2), $3, '.');
                     }
                 ;
 
@@ -2670,7 +2705,7 @@ opt_rescue      : keyword_rescue exc_list exc_var then
                 | none
                 ;
 
-exc_list        : arg_value
+exc_list        : arg
                     {
                         $$ = list1($1);
                     }
@@ -2765,10 +2800,6 @@ regexp          : tREGEXP_BEG tREGEXP
 heredoc         : tHEREDOC_BEG
                 ;
 
-opt_heredoc_bodies : /* none */
-                   | heredoc_bodies
-                   ;
-
 heredoc_bodies  : heredoc_body
                 | heredoc_bodies heredoc_body
                 ;
@@ -2824,7 +2855,7 @@ symbol          : basic_symbol
                     {
                       $$ = new_sym(p, $1);
                     }
-                | tSYMBEG tSTRING_BEG string_interp tSTRING
+                | tSYMBEG tSTRING_BEG string_rep tSTRING
                     {
                       p->lstate = EXPR_END;
                       $$ = new_dsym(p, push($3, $4));
@@ -2924,10 +2955,11 @@ var_ref         : variable
                     }
                 | keyword__FILE__
                     {
-                      if (!p->filename) {
-                        p->filename = "(null)";
+                      const char *fn = p->filename;
+                      if (!fn) {
+                        fn = "(null)";
                       }
-                      $$ = new_str(p, p->filename, strlen(p->filename));
+                      $$ = new_str(p, fn, strlen(fn));
                     }
                 | keyword__LINE__
                     {
@@ -2954,7 +2986,7 @@ superclass      : /* term */
                   expr_value term
                     {
                       $$ = $3;
-                    } /* 
+                    } /*
                 | error term
                     {
                       yyerrok;
@@ -3097,14 +3129,16 @@ f_opt_asgn      : tIDENTIFIER '='
                     }
                 ;
 
-f_opt           : f_opt_asgn arg_value
+f_opt           : f_opt_asgn arg
                     {
+                      void_expr_error(p, $2);
                       $$ = cons(nsym($1), $2);
                     }
                 ;
 
 f_block_opt     : f_opt_asgn primary_value
                     {
+                      void_expr_error(p, $2);
                       $$ = cons(nsym($1), $2);
                     }
                 ;
@@ -3178,7 +3212,7 @@ singleton       : var_ref
                         yyerror(p, "can't define singleton method for ().");
                       }
                       else {
-                        switch ((enum node_type)(int)(intptr_t)$3->car) {
+                        switch ((enum node_type)intn($3->car)) {
                         case NODE_STR:
                         case NODE_DSTR:
                         case NODE_XSTR:
@@ -3215,14 +3249,32 @@ assocs          : assoc
                     }
                 ;
 
-assoc           : arg_value tASSOC arg_value
+assoc           : arg tASSOC arg
                     {
+                      void_expr_error(p, $1);
+                      void_expr_error(p, $3);
                       $$ = cons($1, $3);
                     }
-                | tLABEL arg_value
+                | tLABEL arg
                     {
+                      void_expr_error(p, $2);
                       $$ = cons(new_sym(p, $1), $2);
                     }
+                | tLABEL_END arg
+                    {
+                      void_expr_error(p, $2);
+                      $$ = cons(new_sym(p, new_strsym(p, $1)), $2);
+                    }
+                | tSTRING_BEG tLABEL_END arg
+                    {
+                      void_expr_error(p, $3);
+                      $$ = cons(new_sym(p, new_strsym(p, $2)), $3);
+                    }
+                | tSTRING_BEG string_rep tLABEL_END arg
+                    {
+                      void_expr_error(p, $4);
+                      $$ = cons(new_dsym(p, push($2, $3)), $4);
+                    }
                 ;
 
 operation       : tIDENTIFIER
@@ -3245,6 +3297,23 @@ dot_or_colon    : '.'
                 | tCOLON2
                 ;
 
+call_op         : '.'
+                    {
+                      $$ = '.';
+                    }
+                | tANDDOT
+                    {
+                      $$ = 0;
+                    }
+                ;
+
+call_op2        : call_op
+                | tCOLON2
+                    {
+                      $$ = tCOLON2;
+                    }
+                ;
+
 opt_terms       : /* none */
                 | terms
                 ;
@@ -3261,11 +3330,12 @@ rbracket        : opt_nl ']'
 
 trailer         : /* none */
                 | nl
-                | ','
+                | comma
                 ;
 
 term            : ';' {yyerrok;}
                 | nl
+                | heredoc_body
                 ;
 
 nl              : '\n'
@@ -3273,10 +3343,10 @@ nl              : '\n'
                       p->lineno++;
                       p->column = 0;
                     }
-                  opt_heredoc_bodies
+                ;
 
 terms           : term
-                | terms ';' {yyerrok;}
+                | terms term
                 ;
 
 none            : /* none */
@@ -3285,7 +3355,7 @@ none            : /* none */
                     }
                 ;
 %%
-#define yylval  (*((YYSTYPE*)(p->ylval)))
+#define pylval  (*((YYSTYPE*)(p->ylval)))
 
 static void
 yyerror(parser_state *p, const char *s)
@@ -3373,7 +3443,7 @@ backref_error(parser_state *p, node *n)
   c = (int)(intptr_t)n->car;
 
   if (c == NODE_NTH_REF) {
-    yyerror_i(p, "can't set variable $%d", (int)(intptr_t)n->cdr);
+    yyerror_i(p, "can't set variable $%" MRB_PRId, (mrb_int)(intptr_t)n->cdr);
   }
   else if (c == NODE_BACK_REF) {
     yyerror_i(p, "can't set variable $%c", (int)(intptr_t)n->cdr);
@@ -3383,6 +3453,39 @@ backref_error(parser_state *p, node *n)
   }
 }
 
+static void
+void_expr_error(parser_state *p, node *n)
+{
+  int c;
+
+  if (n == NULL) return;
+  c = (int)(intptr_t)n->car;
+  switch (c) {
+  case NODE_BREAK:
+  case NODE_RETURN:
+  case NODE_NEXT:
+  case NODE_REDO:
+  case NODE_RETRY:
+    yyerror(p, "void value expression");
+    break;
+  case NODE_AND:
+  case NODE_OR:
+    void_expr_error(p, n->cdr->car);
+    void_expr_error(p, n->cdr->cdr);
+    break;
+  case NODE_BEGIN:
+    if (n->cdr) {
+      while (n->cdr) {
+        n = n->cdr;
+      }
+      void_expr_error(p, n->car);
+    }
+    break;
+  default:
+    break;
+  }
+}
+
 static void pushback(parser_state *p, int c);
 static mrb_bool peeks(parser_state *p, const char *s);
 static mrb_bool skips(parser_state *p, const char *s);
@@ -3468,6 +3571,7 @@ peekc_n(parser_state *p, int n)
   do {
     c0 = nextc(p);
     if (c0 == -1) return c0;    /* do not skip partial EOF */
+    if (c0 >= 0) --p->column;
     list = push(list, (node*)(intptr_t)c0);
   } while(n--);
   if (p->pb) {
@@ -3516,7 +3620,7 @@ skips(parser_state *p, const char *s)
     /* skip until first char */
     for (;;) {
       c = nextc(p);
-      if (c < 0) return c;
+      if (c < 0) return FALSE;
       if (c == '\n') {
         p->lineno++;
         p->column = 0;
@@ -3546,7 +3650,12 @@ skips(parser_state *p, const char *s)
 static int
 newtok(parser_state *p)
 {
-  p->bidx = 0;
+  if (p->tokbuf != p->buf) {
+    mrb_free(p->mrb, p->tokbuf);
+    p->tokbuf = p->buf;
+    p->tsiz = MRB_PARSER_TOKBUF_SIZE;
+  }
+  p->tidx = 0;
   return p->column - 1;
 }
 
@@ -3554,7 +3663,7 @@ static void
 tokadd(parser_state *p, int32_t c)
 {
   char utf8[4];
-  unsigned len;
+  int i, len;
 
   /* mrb_assert(-0x10FFFF <= c && c <= 0xFF); */
   if (c >= 0) {
@@ -3588,42 +3697,51 @@ tokadd(parser_state *p, int32_t c)
       len = 4;
     }
   }
-  if (p->bidx+len <= MRB_PARSER_BUF_SIZE) {
-    unsigned i;
-    for (i = 0; i < len; i++) {
-      p->buf[p->bidx++] = utf8[i];
+  if (p->tidx+len >= p->tsiz) {
+    if (p->tsiz >= MRB_PARSER_TOKBUF_MAX) {
+      p->tidx += len;
+      return;
     }
+    p->tsiz *= 2;
+    if (p->tokbuf == p->buf) {
+      p->tokbuf = (char*)mrb_malloc(p->mrb, p->tsiz);
+      memcpy(p->tokbuf, p->buf, MRB_PARSER_TOKBUF_SIZE);
+    }
+    else {
+      p->tokbuf = (char*)mrb_realloc(p->mrb, p->tokbuf, p->tsiz);
+    }
+  }
+  for (i = 0; i < len; i++) {
+    p->tokbuf[p->tidx++] = utf8[i];
   }
 }
 
 static int
 toklast(parser_state *p)
 {
-  return p->buf[p->bidx-1];
+  return p->tokbuf[p->tidx-1];
 }
 
 static void
 tokfix(parser_state *p)
 {
-  int i = p->bidx, imax = MRB_PARSER_BUF_SIZE - 1;
-
-  if (i > imax) {
-    i = imax;
+  if (p->tidx >= MRB_PARSER_TOKBUF_MAX) {
+    p->tidx = MRB_PARSER_TOKBUF_MAX-1;
     yyerror(p, "string too long (truncated)");
   }
-  p->buf[i] = '\0';
+  p->tokbuf[p->tidx] = '\0';
 }
 
 static const char*
 tok(parser_state *p)
 {
-  return p->buf;
+  return p->tokbuf;
 }
 
 static int
 toklen(parser_state *p)
 {
-  return p->bidx;
+  return p->tidx;
 }
 
 #define IS_ARG() (p->lstate == EXPR_ARG || p->lstate == EXPR_CMDARG)
@@ -3668,6 +3786,44 @@ scan_hex(const int *start, int len, int *retlen)
   return retval;
 }
 
+static int32_t
+read_escape_unicode(parser_state *p, int limit)
+{
+  int32_t c;
+  int buf[9];
+  int i;
+
+  /* Look for opening brace */
+  i = 0;
+  buf[0] = nextc(p);
+  if (buf[0] < 0) goto eof;
+  if (ISXDIGIT(buf[0])) {
+    /* \uxxxx form */
+    for (i=1; i<limit; i++) {
+      buf[i] = nextc(p);
+      if (buf[i] < 0) goto eof;
+      if (!ISXDIGIT(buf[i])) {
+        pushback(p, buf[i]);
+        break;
+      }
+    }
+  }
+  else {
+    pushback(p, buf[0]);
+  }
+  c = scan_hex(buf, i, &i);
+  if (i == 0) {
+  eof:
+    yyerror(p, "Invalid escape character syntax");
+    return -1;
+  }
+  if (c < 0 || c > 0x10FFFF || (c & 0xFFFFF800) == 0xD800) {
+    yyerror(p, "Invalid Unicode code point");
+    return -1;
+  }
+  return c;
+}
+
 /* Return negative to indicate Unicode code point */
 static int32_t
 read_escape(parser_state *p)
@@ -3740,53 +3896,17 @@ read_escape(parser_state *p)
   return c;
 
   case 'u':     /* Unicode */
-  {
-    int buf[9];
-    int i;
-
-    /* Look for opening brace */
-    i = 0;
-    buf[0] = nextc(p);
-    if (buf[0] < 0) goto eof;
-    if (buf[0] == '{') {
+    if (peek(p, '{')) {
       /* \u{xxxxxxxx} form */
-      for (i=0; i<9; i++) {
-        buf[i] = nextc(p);
-        if (buf[i] < 0) goto eof;
-        if (buf[i] == '}') {
-          break;
-        }
-        else if (!ISXDIGIT(buf[i])) {
-          yyerror(p, "Invalid escape character syntax");
-          pushback(p, buf[i]);
-          return 0;
-        }
-      }
-    }
-    else if (ISXDIGIT(buf[0])) {
-      /* \uxxxx form */
-      for (i=1; i<4; i++) {
-        buf[i] = nextc(p);
-        if (buf[i] < 0) goto eof;
-        if (!ISXDIGIT(buf[i])) {
-          pushback(p, buf[i]);
-          break;
-        }
-      }
+      nextc(p);
+      c = read_escape_unicode(p, 8);
+      if (c < 0) return 0;
+      if (nextc(p) != '}') goto eof;
     }
     else {
-      pushback(p, buf[0]);
-    }
-    c = scan_hex(buf, i, &i);
-    if (i == 0) {
-      yyerror(p, "Invalid escape character syntax");
-      return 0;
+      c = read_escape_unicode(p, 4);
+      if (c < 0) return 0;
     }
-    if (c < 0 || c > 0x10FFFF || (c & 0xFFFFF800) == 0xD800) {
-      yyerror(p, "Invalid Unicode code point");
-      return 0;
-    }
-  }
   return -c;
 
   case 'b':/* backspace */
@@ -3844,7 +3964,10 @@ parse_string(parser_state *p)
   int beg = (intptr_t)p->lex_strterm->cdr->cdr->car;
   int end = (intptr_t)p->lex_strterm->cdr->cdr->cdr;
   parser_heredoc_info *hinf = (type & STR_FUNC_HEREDOC) ? parsing_heredoc_inf(p) : NULL;
+  int cmd_state = p->cmd_start;
 
+  if (beg == 0) beg = -3;       /* should never happen */
+  if (end == 0) end = -3;
   newtok(p);
   while ((c = nextc(p)) != end || nest_level != 0) {
     if (hinf && (c == '\n' || c < 0)) {
@@ -3866,7 +3989,12 @@ parse_string(parser_state *p)
           }
         }
         if ((len-1 == hinf->term_len) && (strncmp(s, hinf->term, len-1) == 0)) {
-          return tHEREDOC_END;
+          if (c < 0) {
+            p->parsing_heredoc = NULL;
+          }
+          else {
+            return tHEREDOC_END;
+          }
         }
       }
       if (c < 0) {
@@ -3875,7 +4003,7 @@ parse_string(parser_state *p)
         yyerror(p, buf);
         return 0;
       }
-      yylval.nd = new_str(p, tok(p), toklen(p));
+      pylval.nd = new_str(p, tok(p), toklen(p));
       return tHD_STRING_MID;
     }
     if (c < 0) {
@@ -3907,6 +4035,20 @@ parse_string(parser_state *p)
           tokadd(p, '\\');
           tokadd(p, c);
         }
+        else if (c == 'u' && peek(p, '{')) {
+          /* \u{xxxx xxxx xxxx} form */
+          nextc(p);
+          while (1) {
+            do c = nextc(p); while (ISSPACE(c));
+            if (c == '}') break;
+            pushback(p, c);
+            c = read_escape_unicode(p, 8);
+            if (c < 0) break;
+            tokadd(p, -c);
+          }
+          if (hinf)
+            hinf->line_head = FALSE;
+        }
         else {
           pushback(p, c);
           tokadd(p, read_escape(p));
@@ -3934,7 +4076,7 @@ parse_string(parser_state *p)
         tokfix(p);
         p->lstate = EXPR_BEG;
         p->cmd_start = TRUE;
-        yylval.nd = new_str(p, tok(p), toklen(p));
+        pylval.nd = new_str(p, tok(p), toklen(p));
         if (hinf) {
           hinf->line_head = FALSE;
           return tHD_STRING_PART;
@@ -3964,10 +4106,14 @@ parse_string(parser_state *p)
       else {
         pushback(p, c);
         tokfix(p);
-        yylval.nd = new_str(p, tok(p), toklen(p));
+        pylval.nd = new_str(p, tok(p), toklen(p));
         return tSTRING_MID;
       }
     }
+    if (c == '\n') {
+      p->lineno++;
+      p->column = 0;
+    }
     tokadd(p, c);
   }
 
@@ -3976,7 +4122,7 @@ parse_string(parser_state *p)
   end_strterm(p);
 
   if (type & STR_FUNC_XQUOTE) {
-    yylval.nd = new_xstr(p, tok(p), toklen(p));
+    pylval.nd = new_xstr(p, tok(p), toklen(p));
     return tXSTRING;
   }
 
@@ -4018,20 +4164,29 @@ parse_string(parser_state *p)
     }
     if (flag > flags) {
       dup = strndup(flags, (size_t)(flag - flags));
-    } else {
+    }
+    else {
       dup = NULL;
     }
     if (enc) {
       encp = strndup(&enc, 1);
-    } else {
+    }
+    else {
       encp = NULL;
     }
-    yylval.nd = new_regx(p, s, dup, encp);
+    pylval.nd = new_regx(p, s, dup, encp);
 
     return tREGEXP;
   }
+  pylval.nd = new_str(p, tok(p), toklen(p));
+  if (IS_LABEL_POSSIBLE()) {
+    if (IS_LABEL_SUFFIX(0)) {
+      p->lstate = EXPR_BEG;
+      nextc(p);
+      return tLABEL_END;
+    }
+  }
 
-  yylval.nd = new_str(p, tok(p), toklen(p));
   return tSTRING;
 }
 
@@ -4101,7 +4256,7 @@ heredoc_identifier(parser_state *p)
   p->heredocs_from_nextline = push(p->heredocs_from_nextline, newnode);
   p->lstate = EXPR_END;
 
-  yylval.nd = newnode;
+  pylval.nd = newnode;
   return tHEREDOC_BEG;
 }
 
@@ -4205,7 +4360,7 @@ parser_yylex(parser_state *p)
   case '*':
     if ((c = nextc(p)) == '*') {
       if ((c = nextc(p)) == '=') {
-        yylval.id = intern("**",2);
+        pylval.id = intern("**",2);
         p->lstate = EXPR_BEG;
         return tOP_ASGN;
       }
@@ -4214,7 +4369,7 @@ parser_yylex(parser_state *p)
     }
     else {
       if (c == '=') {
-        yylval.id = intern_c('*');
+        pylval.id = intern_c('*');
         p->lstate = EXPR_BEG;
         return tOP_ASGN;
       }
@@ -4330,7 +4485,7 @@ parser_yylex(parser_state *p)
     }
     if (c == '<') {
       if ((c = nextc(p)) == '=') {
-        yylval.id = intern("<<",2);
+        pylval.id = intern("<<",2);
         p->lstate = EXPR_BEG;
         return tOP_ASGN;
       }
@@ -4352,7 +4507,7 @@ parser_yylex(parser_state *p)
     }
     if (c == '>') {
       if ((c = nextc(p)) == '=') {
-        yylval.id = intern(">>",2);
+        pylval.id = intern(">>",2);
         p->lstate = EXPR_BEG;
         return tOP_ASGN;
       }
@@ -4449,7 +4604,7 @@ parser_yylex(parser_state *p)
       tokadd(p, c);
     }
     tokfix(p);
-    yylval.nd = new_str(p, tok(p), toklen(p));
+    pylval.nd = new_str(p, tok(p), toklen(p));
     p->lstate = EXPR_END;
     return tCHAR;
 
@@ -4457,15 +4612,19 @@ parser_yylex(parser_state *p)
     if ((c = nextc(p)) == '&') {
       p->lstate = EXPR_BEG;
       if ((c = nextc(p)) == '=') {
-        yylval.id = intern("&&",2);
+        pylval.id = intern("&&",2);
         p->lstate = EXPR_BEG;
         return tOP_ASGN;
       }
       pushback(p, c);
       return tANDOP;
     }
+    else if (c == '.') {
+      p->lstate = EXPR_DOT;
+      return tANDDOT;
+    }
     else if (c == '=') {
-      yylval.id = intern_c('&');
+      pylval.id = intern_c('&');
       p->lstate = EXPR_BEG;
       return tOP_ASGN;
     }
@@ -4492,7 +4651,7 @@ parser_yylex(parser_state *p)
     if ((c = nextc(p)) == '|') {
       p->lstate = EXPR_BEG;
       if ((c = nextc(p)) == '=') {
-        yylval.id = intern("||",2);
+        pylval.id = intern("||",2);
         p->lstate = EXPR_BEG;
         return tOP_ASGN;
       }
@@ -4500,7 +4659,7 @@ parser_yylex(parser_state *p)
       return tOROP;
     }
     if (c == '=') {
-      yylval.id = intern_c('|');
+      pylval.id = intern_c('|');
       p->lstate = EXPR_BEG;
       return tOP_ASGN;
     }
@@ -4524,7 +4683,7 @@ parser_yylex(parser_state *p)
       return '+';
     }
     if (c == '=') {
-      yylval.id = intern_c('+');
+      pylval.id = intern_c('+');
       p->lstate = EXPR_BEG;
       return tOP_ASGN;
     }
@@ -4552,7 +4711,7 @@ parser_yylex(parser_state *p)
       return '-';
     }
     if (c == '=') {
-      yylval.id = intern_c('-');
+      pylval.id = intern_c('-');
       p->lstate = EXPR_BEG;
       return tOP_ASGN;
     }
@@ -4626,7 +4785,7 @@ parser_yylex(parser_state *p)
           no_digits();
         }
         else if (nondigit) goto trailing_uc;
-        yylval.nd = new_int(p, tok(p), 16);
+        pylval.nd = new_int(p, tok(p), 16);
         return tINTEGER;
       }
       if (c == 'b' || c == 'B') {
@@ -4650,7 +4809,7 @@ parser_yylex(parser_state *p)
           no_digits();
         }
         else if (nondigit) goto trailing_uc;
-        yylval.nd = new_int(p, tok(p), 2);
+        pylval.nd = new_int(p, tok(p), 2);
         return tINTEGER;
       }
       if (c == 'd' || c == 'D') {
@@ -4674,7 +4833,7 @@ parser_yylex(parser_state *p)
           no_digits();
         }
         else if (nondigit) goto trailing_uc;
-        yylval.nd = new_int(p, tok(p), 10);
+        pylval.nd = new_int(p, tok(p), 10);
         return tINTEGER;
       }
       if (c == '_') {
@@ -4707,7 +4866,7 @@ parser_yylex(parser_state *p)
           pushback(p, c);
           tokfix(p);
           if (nondigit) goto trailing_uc;
-          yylval.nd = new_int(p, tok(p), 8);
+          pylval.nd = new_int(p, tok(p), 8);
           return tINTEGER;
         }
         if (nondigit) {
@@ -4724,7 +4883,7 @@ parser_yylex(parser_state *p)
       }
       else {
         pushback(p, c);
-        yylval.nd = new_int(p, "0", 10);
+        pylval.nd = new_int(p, "0", 10);
         return tINTEGER;
       }
     }
@@ -4800,7 +4959,7 @@ parser_yylex(parser_state *p)
       char *endp;
 
       errno = 0;
-      d = strtod(tok(p), &endp);
+      d = mrb_float_read(tok(p), &endp);
       if (d == 0 && endp == tok(p)) {
         yywarning_s(p, "corrupted float value %s", tok(p));
       }
@@ -4808,10 +4967,10 @@ parser_yylex(parser_state *p)
         yywarning_s(p, "float %s out of range", tok(p));
         errno = 0;
       }
-      yylval.nd = new_float(p, tok(p));
+      pylval.nd = new_float(p, tok(p));
       return tFLOAT;
     }
-    yylval.nd = new_int(p, tok(p), 10);
+    pylval.nd = new_int(p, tok(p), 10);
     return tINTEGER;
   }
 
@@ -4853,7 +5012,7 @@ parser_yylex(parser_state *p)
       return tREGEXP_BEG;
     }
     if ((c = nextc(p)) == '=') {
-      yylval.id = intern_c('/');
+      pylval.id = intern_c('/');
       p->lstate = EXPR_BEG;
       return tOP_ASGN;
     }
@@ -4872,7 +5031,7 @@ parser_yylex(parser_state *p)
 
   case '^':
     if ((c = nextc(p)) == '=') {
-      yylval.id = intern_c('^');
+      pylval.id = intern_c('^');
       p->lstate = EXPR_BEG;
       return tOP_ASGN;
     }
@@ -5046,7 +5205,7 @@ parser_yylex(parser_state *p)
       }
     }
     if ((c = nextc(p)) == '=') {
-      yylval.id = intern_c('%');
+      pylval.id = intern_c('%');
       p->lstate = EXPR_BEG;
       return tOP_ASGN;
     }
@@ -5100,7 +5259,7 @@ parser_yylex(parser_state *p)
       tokadd(p, '$');
       tokadd(p, c);
       tokfix(p);
-      yylval.id = intern_cstr(tok(p));
+      pylval.id = intern_cstr(tok(p));
       return tGVAR;
 
     case '-':
@@ -5110,7 +5269,7 @@ parser_yylex(parser_state *p)
       pushback(p, c);
       gvar:
       tokfix(p);
-      yylval.id = intern_cstr(tok(p));
+      pylval.id = intern_cstr(tok(p));
       return tGVAR;
 
     case '&':     /* $&: last match */
@@ -5122,7 +5281,7 @@ parser_yylex(parser_state *p)
         tokadd(p, c);
         goto gvar;
       }
-      yylval.nd = new_back_ref(p, c);
+      pylval.nd = new_back_ref(p, c);
       return tBACK_REF;
 
     case '1': case '2': case '3':
@@ -5141,7 +5300,7 @@ parser_yylex(parser_state *p)
           yyerror_i(p, "capture group index must be <= %d", INT_MAX);
           return 0;
         }
-        yylval.nd = new_nth_ref(p, (int)n);
+        pylval.nd = new_nth_ref(p, (int)n);
       }
       return tNTH_REF;
 
@@ -5165,7 +5324,7 @@ parser_yylex(parser_state *p)
         c = nextc(p);
       }
       if (c < 0) {
-        if (p->bidx == 1) {
+        if (p->tidx == 1) {
           yyerror(p, "incomplete instance variable syntax");
         }
         else {
@@ -5174,7 +5333,7 @@ parser_yylex(parser_state *p)
         return 0;
       }
       else if (isdigit(c)) {
-        if (p->bidx == 1) {
+        if (p->tidx == 1) {
           yyerror_i(p, "'@%c' is not allowed as an instance variable name", c);
         }
         else {
@@ -5269,7 +5428,7 @@ parser_yylex(parser_state *p)
           p->lstate = EXPR_BEG;
           nextc(p);
           tokfix(p);
-          yylval.id = intern_cstr(tok(p));
+          pylval.id = intern_cstr(tok(p));
           return tLABEL;
         }
       }
@@ -5280,10 +5439,10 @@ parser_yylex(parser_state *p)
         kw = mrb_reserved_word(tok(p), toklen(p));
         if (kw) {
           enum mrb_lex_state_enum state = p->lstate;
-          yylval.num = p->lineno;
+          pylval.num = p->lineno;
           p->lstate = kw->state;
           if (state == EXPR_FNAME) {
-            yylval.id = intern_cstr(kw->name);
+            pylval.id = intern_cstr(kw->name);
             return kw->id[0];
           }
           if (p->lstate == EXPR_BEG) {
@@ -5330,7 +5489,7 @@ parser_yylex(parser_state *p)
     {
       mrb_sym ident = intern_cstr(tok(p));
 
-      yylval.id = ident;
+      pylval.id = ident;
 #if 0
       if (last_state != EXPR_DOT && islower(tok(p)[0]) && lvar_defined(ident)) {
         p->lstate = EXPR_END;
@@ -5395,10 +5554,11 @@ void mrb_parser_dump(mrb_state *mrb, node *tree, int offset);
 MRB_API void
 mrb_parser_parse(parser_state *p, mrbc_context *c)
 {
-  struct mrb_jmpbuf buf;
-  p->jmp = &buf;
+  struct mrb_jmpbuf buf1;
+  p->jmp = &buf1;
 
   MRB_TRY(p->jmp) {
+    int n = 1;
 
     p->cmd_start = TRUE;
     p->in_def = p->in_single = 0;
@@ -5406,7 +5566,27 @@ mrb_parser_parse(parser_state *p, mrbc_context *c)
     p->lex_strterm = NULL;
 
     parser_init_cxt(p, c);
-    yyparse(p);
+
+    if (p->mrb->jmp) {
+      n = yyparse(p);
+    }
+    else {
+      struct mrb_jmpbuf buf2;
+
+      p->mrb->jmp = &buf2;
+      MRB_TRY(p->mrb->jmp) {
+        n = yyparse(p);
+      }
+      MRB_CATCH(p->mrb->jmp) {
+        p->nerr++;
+      }
+      MRB_END_EXC(p->mrb->jmp);
+      p->mrb->jmp = 0;
+    }
+    if (n != 0 || p->nerr > 0) {
+      p->tree = 0;
+      return;
+    }
     if (!p->tree) {
       p->tree = new_nil(p);
     }
@@ -5414,7 +5594,6 @@ mrb_parser_parse(parser_state *p, mrbc_context *c)
     if (c && c->dump_result) {
       mrb_parser_dump(p->mrb, p->tree, 0);
     }
-
   }
   MRB_CATCH(p->jmp) {
     yyerror(p, "memory allocation error");
@@ -5442,7 +5621,7 @@ mrb_parser_new(mrb_state *mrb)
   p->pool = pool;
 
   p->s = p->send = NULL;
-#ifndef MRB_DISBLE_STDIO
+#ifndef MRB_DISABLE_STDIO
   p->f = NULL;
 #endif
 
@@ -5455,6 +5634,8 @@ mrb_parser_new(mrb_state *mrb)
 #if defined(PARSER_TEST) || defined(PARSER_DEBUG)
   yydebug = 1;
 #endif
+  p->tsiz = MRB_PARSER_TOKBUF_SIZE;
+  p->tokbuf = p->buf;
 
   p->lex_strterm = NULL;
   p->all_heredocs = p->parsing_heredoc = NULL;
@@ -5469,6 +5650,9 @@ mrb_parser_new(mrb_state *mrb)
 
 MRB_API void
 mrb_parser_free(parser_state *p) {
+  if (p->tokbuf != p->buf) {
+    mrb_free(p->mrb, p->tokbuf);
+  }
   mrb_pool_close(p->pool);
 }
 
@@ -5481,6 +5665,7 @@ mrbc_context_new(mrb_state *mrb)
 MRB_API void
 mrbc_context_free(mrb_state *mrb, mrbc_context *cxt)
 {
+  mrb_free(mrb, cxt->filename);
   mrb_free(mrb, cxt->syms);
   mrb_free(mrb, cxt);
 }
@@ -5490,9 +5675,12 @@ mrbc_filename(mrb_state *mrb, mrbc_context *c, const char *s)
 {
   if (s) {
     int len = strlen(s);
-    char *p = (char *)mrb_alloca(mrb, len + 1);
+    char *p = (char *)mrb_malloc(mrb, len + 1);
 
     memcpy(p, s, len + 1);
+    if (c->filename) {
+      mrb_free(mrb, c->filename);
+    }
     c->filename = p;
   }
   return c->filename;
@@ -5577,8 +5765,8 @@ mrb_parse_string(mrb_state *mrb, const char *s, mrbc_context *c)
   return mrb_parse_nstring(mrb, s, strlen(s), c);
 }
 
-static mrb_value
-load_exec(mrb_state *mrb, parser_state *p, mrbc_context *c)
+MRB_API mrb_value
+mrb_load_exec(mrb_state *mrb, struct mrb_parser_state *p, mrbc_context *c)
 {
   struct RClass *target = mrb->object_class;
   struct RProc *proc;
@@ -5589,6 +5777,7 @@ load_exec(mrb_state *mrb, parser_state *p, mrbc_context *c)
     return mrb_undef_value();
   }
   if (!p->tree || p->nerr) {
+    c->parser_nerr = p->nerr;
     if (p->capture_errors) {
       char buf[256];
       int n;
@@ -5600,7 +5789,9 @@ load_exec(mrb_state *mrb, parser_state *p, mrbc_context *c)
       return mrb_undef_value();
     }
     else {
-      mrb->exc = mrb_obj_ptr(mrb_exc_new_str_lit(mrb, E_SYNTAX_ERROR, "syntax error"));
+      if (mrb->exc == NULL) {
+        mrb->exc = mrb_obj_ptr(mrb_exc_new_str_lit(mrb, E_SYNTAX_ERROR, "syntax error"));
+      }
       mrb_parser_free(p);
       return mrb_undef_value();
     }
@@ -5608,7 +5799,9 @@ load_exec(mrb_state *mrb, parser_state *p, mrbc_context *c)
   proc = mrb_generate_code(mrb, p);
   mrb_parser_free(p);
   if (proc == NULL) {
-    mrb->exc = mrb_obj_ptr(mrb_exc_new_str_lit(mrb, E_SCRIPT_ERROR, "codegen error"));
+    if (mrb->exc == NULL) {
+      mrb->exc = mrb_obj_ptr(mrb_exc_new_str_lit(mrb, E_SCRIPT_ERROR, "codegen error"));
+    }
     return mrb_undef_value();
   }
   if (c) {
@@ -5628,7 +5821,7 @@ load_exec(mrb_state *mrb, parser_state *p, mrbc_context *c)
   if (mrb->c->ci) {
     mrb->c->ci->target_class = target;
   }
-  v = mrb_toplevel_run_keep(mrb, proc, keep);
+  v = mrb_top_run(mrb, proc, mrb_top_self(mrb), keep);
   if (mrb->exc) return mrb_nil_value();
   return v;
 }
@@ -5637,7 +5830,7 @@ load_exec(mrb_state *mrb, parser_state *p, mrbc_context *c)
 MRB_API mrb_value
 mrb_load_file_cxt(mrb_state *mrb, FILE *f, mrbc_context *c)
 {
-  return load_exec(mrb, mrb_parse_file(mrb, f, c), c);
+  return mrb_load_exec(mrb, mrb_parse_file(mrb, f, c), c);
 }
 
 MRB_API mrb_value
@@ -5650,7 +5843,7 @@ mrb_load_file(mrb_state *mrb, FILE *f)
 MRB_API mrb_value
 mrb_load_nstring_cxt(mrb_state *mrb, const char *s, int len, mrbc_context *c)
 {
-  return load_exec(mrb, mrb_parse_nstring(mrb, s, len, c), c);
+  return mrb_load_exec(mrb, mrb_parse_nstring(mrb, s, len, c), c);
 }
 
 MRB_API mrb_value
@@ -5804,10 +5997,9 @@ mrb_parser_dump(mrb_state *mrb, node *tree, int offset)
         printf("post mandatory args:\n");
         dump_recur(mrb, n->car, offset+2);
       }
-      n = n->cdr;
-      if (n) {
+      if (n->cdr) {
         dump_prefix(n, offset+1);
-        printf("blk=&%s\n", mrb_sym2name(mrb, sym(n)));
+        printf("blk=&%s\n", mrb_sym2name(mrb, sym(n->cdr)));
       }
     }
     dump_prefix(tree, offset+1);
@@ -5945,7 +6137,17 @@ mrb_parser_dump(mrb_state *mrb, node *tree, int offset)
 
   case NODE_FCALL:
   case NODE_CALL:
-    printf("NODE_CALL:\n");
+  case NODE_SCALL:
+    switch (nodetype) {
+    case NODE_FCALL:
+      printf("NODE_FCALL:\n"); break;
+    case NODE_CALL:
+      printf("NODE_CALL(.):\n"); break;
+    case NODE_SCALL:
+      printf("NODE_SCALL(&.):\n"); break;
+    default:
+      break;
+    }
     mrb_parser_dump(mrb, tree->car, offset+1);
     dump_prefix(tree, offset+1);
     printf("method='%s' (%d)\n",
@@ -6153,7 +6355,7 @@ mrb_parser_dump(mrb_state *mrb, node *tree, int offset)
     break;
 
   case NODE_NTH_REF:
-    printf("NODE_NTH_REF: $%d\n", (int)(intptr_t)tree);
+    printf("NODE_NTH_REF: $%" MRB_PRId "\n", (mrb_int)(intptr_t)tree);
     break;
 
   case NODE_ARG:
@@ -6205,12 +6407,19 @@ mrb_parser_dump(mrb_state *mrb, node *tree, int offset)
     dump_recur(mrb, tree->car, offset+1);
     dump_prefix(tree, offset);
     printf("tail: %s\n", (char*)tree->cdr->cdr->car);
-    dump_prefix(tree, offset);
-    printf("opt: %s\n", (char*)tree->cdr->cdr->cdr);
+    if (tree->cdr->cdr->cdr->car) {
+      dump_prefix(tree, offset);
+      printf("opt: %s\n", (char*)tree->cdr->cdr->cdr->car);
+    }
+    if (tree->cdr->cdr->cdr->cdr) {
+      dump_prefix(tree, offset);
+      printf("enc: %s\n", (char*)tree->cdr->cdr->cdr->cdr);
+    }
     break;
 
   case NODE_SYM:
-    printf("NODE_SYM :%s\n", mrb_sym2name(mrb, sym(tree)));
+    printf("NODE_SYM :%s (%d)\n", mrb_sym2name(mrb, sym(tree)),
+           (int)(intptr_t)tree);
     break;
 
   case NODE_SELF:
@@ -6424,8 +6633,8 @@ mrb_parser_dump(mrb_state *mrb, node *tree, int offset)
     break;
 
   case NODE_HEREDOC:
-    printf("NODE_HEREDOC:\n");
-    mrb_parser_dump(mrb, ((parser_heredoc_info*)tree)->doc, offset+1);
+    printf("NODE_HEREDOC (<<%s):\n", ((parser_heredoc_info*)tree)->term);
+    dump_recur(mrb, ((parser_heredoc_info*)tree)->doc, offset+1);
     break;
 
   default:
index 3a22762..3bf6d6a 100644 (file)
@@ -8,11 +8,11 @@ MRuby::Gem::Specification.new 'mruby-compiler' do |spec|
 
   lex_def = "#{current_dir}/core/lex.def"
   core_objs = Dir.glob("#{current_dir}/core/*.c").map { |f|
-    next nil if build.cxx_abi_enabled? and f =~ /(codegen).c$/
+    next nil if build.cxx_exception_enabled? and f =~ /(codegen).c$/
     objfile(f.pathmap("#{current_build_dir}/core/%n"))
   }.compact
 
-  if build.cxx_abi_enabled?
+  if build.cxx_exception_enabled?
     core_objs <<
       build.compile_as_cxx("#{current_build_dir}/core/y.tab.c", "#{current_build_dir}/core/y.tab.cxx",
                            objfile("#{current_build_dir}/y.tab"), ["#{current_dir}/core"]) <<
index ed1943f..7741e51 100644 (file)
@@ -58,13 +58,14 @@ module Enumerable
 
   def take(n)
     raise TypeError, "no implicit conversion of #{n.class} into Integer" unless n.respond_to?(:to_int)
-    raise ArgumentError, "attempt to take negative size" if n < 0
-
-    n = n.to_int
+    i = n.to_int
+    raise ArgumentError, "attempt to take negative size" if i < 0
     ary = []
+    return ary if i == 0
     self.each do |*val|
-      break if ary.size >= n
       ary << val.__svalue
+      i -= 1
+      break if i == 0
     end
     ary
   end
@@ -123,6 +124,7 @@ module Enumerable
       ary << val.__svalue
       block.call(ary.dup) if ary.size == n
     end
+    nil
   end
 
   ##
@@ -153,6 +155,7 @@ module Enumerable
       end
     end
     block.call(ary) unless ary.empty?
+    nil
   end
 
   ##
@@ -198,7 +201,7 @@ module Enumerable
       ary.push([block.call(e), i])
     }
     if ary.size > 1
-      __sort_sub__(ary, ::Array.new(ary.size), 0, 0, ary.size - 1) do |a,b|
+      __sort_sub__(ary, 0, ary.size - 1) do |a,b|
         a <=> b
       end
     end
@@ -214,21 +217,28 @@ module Enumerable
   # Returns the first element, or the first +n+ elements, of the enumerable.
   # If the enumerable is empty, the first form returns <code>nil</code>, and the
   # second form returns an empty array.
-  def first(n=NONE)
-    if n == NONE
+  def first(*args)
+    case args.length
+    when 0
       self.each do |*val|
         return val.__svalue
       end
       return nil
-    else
-      a = []
-      i = 0
+    when 1
+      n = args[0]
+      raise TypeError, "no implicit conversion of #{n.class} into Integer" unless n.respond_to?(:to_int)
+      i = n.to_int
+      raise ArgumentError, "attempt to take negative size" if i < 0
+      ary = []
+      return ary if i == 0
       self.each do |*val|
-        break if n<=i
-        a.push val.__svalue
-        i += 1
+        ary << val.__svalue
+        i -= 1
+        break if i == 0
       end
-      a
+      ary
+    else
+      raise ArgumentError, "wrong number of arguments (given #{args.length}, expected 0..1)"
     end
   end
 
@@ -384,11 +394,11 @@ module Enumerable
         min = val
         first = false
       else
+        val = val.__svalue
         if block
-          max = val.__svalue if block.call(*val, max) > 0
-          min = val.__svalue if block.call(*val, min) < 0
+          max = val if block.call(val, max) > 0
+          min = val if block.call(val, min) < 0
         else
-          val = val.__svalue
           max = val if (val <=> max) > 0
           min = val if (val <=> min) < 0
         end
@@ -573,35 +583,38 @@ module Enumerable
   #     a.cycle(2) { |x| puts x }  # print, a, b, c, a, b, c.
   #
 
-  def cycle(n=nil, &block)
-    return to_enum(:cycle, n) if !block && n.nil?
+  def cycle(nv = nil, &block)
+    return to_enum(:cycle, nv) unless block
 
-    ary = []
-    if n.nil?
-      self.each do|*val|
-        ary.push val
-        block.call(*val)
+    n = nil
+
+    if nv.nil?
+      n = -1
+    else
+      unless nv.respond_to?(:to_int)
+        raise TypeError, "no implicit conversion of #{nv.class} into Integer"
       end
-      loop do
-        ary.each do|e|
-          block.call(*e)
-        end
+      n = nv.to_int
+      unless n.kind_of?(Integer)
+        raise TypeError, "no implicit conversion of #{nv.class} into Integer"
       end
-    else
-      raise TypeError, "no implicit conversion of #{n.class} into Integer" unless n.respond_to?(:to_int)
+      return nil if n <= 0
+    end
 
-      n = n.to_int
-      self.each do|*val|
-        ary.push val
-      end
-      count = 0
-      while count < n
-        ary.each do|e|
-          block.call(*e)
-        end
-        count += 1
+    ary = []
+    each do |*i|
+      ary.push(i)
+      yield(*i)
+    end
+    return nil if ary.empty?
+
+    while n < 0 || 0 < (n -= 1)
+      ary.each do |i|
+        yield(*i)
       end
     end
+
+    nil
   end
 
   ##
index 08b553f..e772f85 100644 (file)
@@ -25,14 +25,16 @@ end
 
 assert("Enumerable#each_cons") do
   a = []
-  (1..5).each_cons(3){|e| a << e}
+  b = (1..5).each_cons(3){|e| a << e}
   assert_equal [[1, 2, 3], [2, 3, 4], [3, 4, 5]], a
+  assert_equal nil, b
 end
 
 assert("Enumerable#each_slice") do
   a = []
-  (1..10).each_slice(3){|e| a << e}
+  b = (1..10).each_slice(3){|e| a << e}
   assert_equal [[1, 2, 3], [4, 5, 6], [7, 8, 9], [10]], a
+  assert_equal nil, b
 end
 
 assert("Enumerable#group_by") do
@@ -128,6 +130,13 @@ assert("Enumerable#cycle") do
   ["a", "b", "c"].cycle(2) { |v| a << v }
   assert_equal ["a", "b", "c", "a", "b", "c"], a
   assert_raise(TypeError) { ["a", "b", "c"].cycle("a") { |v| a << v } }
+
+  empty = Class.new do
+    include Enumerable
+    def each
+    end
+  end
+  assert_nil empty.new.cycle { break :nope }
 end
 
 assert("Enumerable#find_index") do
index 219141e..682134c 100644 (file)
@@ -1,7 +1,7 @@
 MRuby::Gem::Specification.new('mruby-enum-lazy') do |spec|
   spec.license = 'MIT'
   spec.author  = 'mruby developers'
-  spec.summary = 'Enumerable::Lazy class'
+  spec.summary = 'Enumerator::Lazy class'
   spec.add_dependency('mruby-enumerator', :core => 'mruby-enumerator')
   spec.add_dependency('mruby-enum-ext', :core => 'mruby-enum-ext')
 end
index 8ce363c..8a7ac7f 100644 (file)
@@ -2,7 +2,7 @@ module Enumerable
 
   # = Enumerable#lazy implementation
   #
-  # Enumerable#lazy returns an instance of Enumerable::Lazy.
+  # Enumerable#lazy returns an instance of Enumerator::Lazy.
   # You can use it just like as normal Enumerable object,
   # except these methods act as 'lazy':
   #
@@ -16,9 +16,11 @@ module Enumerable
   #   - flat_map  collect_concat
   #   - zip
   def lazy
-    Lazy.new(self)
+    Enumerator::Lazy.new(self)
   end
+end
 
+class Enumerator
   # == Acknowledgements
   #
   #   Based on https://github.com/yhara/enumerable-lazy
@@ -40,6 +42,15 @@ module Enumerable
       }
     end
 
+    def to_enum(meth=:each, *args, &block)
+      lz = Lazy.new(self, &block)
+      lz.obj = self
+      lz.meth = meth
+      lz.args = args
+      lz
+    end
+    alias enum_for to_enum
+
     def map(&block)
       Lazy.new(self){|yielder, val|
         yielder << block.call(val)
@@ -58,7 +69,7 @@ module Enumerable
 
     def reject(&block)
       Lazy.new(self){|yielder, val|
-        if not block.call(val)
+        unless block.call(val)
           yielder << val
         end
       }
index ca009d3..940d070 100644 (file)
@@ -1,9 +1,9 @@
-assert("Enumerable::Lazy") do
+assert("Enumerator::Lazy") do
   a = [1, 2]
-  assert_equal Enumerable::Lazy, a.lazy.class
+  assert_equal Enumerator::Lazy, a.lazy.class
 end
 
-assert("Enumerable::Lazy laziness") do
+assert("Enumerator::Lazy laziness") do
   a = Object.new
   def a.each
     return to_enum :each unless block_given?
@@ -40,7 +40,13 @@ assert("Enumerable::Lazy laziness") do
   assert_equal [10,20], a.b
 end
 
-assert("Enumerable::Lazy#zip with cycle") do
+assert("Enumrator::Lazy#to_enum") do
+  lazy_enum = (0..Float::INFINITY).lazy.to_enum(:each_slice, 2)
+  assert_kind_of Enumerator::Lazy, lazy_enum
+  assert_equal [0*1, 2*3, 4*5, 6*7], lazy_enum.map { |a| a.first * a.last }.first(4)
+end
+
+assert("Enumerator::Lazy#zip with cycle") do
   e1 = [1, 2, 3].cycle
   e2 = [:a, :b].cycle
   assert_equal [[1,:a],[2,:b],[3,:a]], e1.lazy.zip(e2).first(3)
index 9abaca3..a60f19a 100644 (file)
@@ -153,12 +153,18 @@ class Enumerator
   #
   def with_index(offset=0)
     return to_enum :with_index, offset unless block_given?
-    raise TypeError, "no implicit conversion of #{offset.class} into Integer" unless offset.respond_to?(:to_int)
+    offset = if offset.nil?
+      0
+    elsif offset.respond_to?(:to_int)
+      offset.to_int
+    else
+      raise TypeError, "no implicit conversion of #{offset.class} into Integer"
+    end
 
-    n = offset.to_int - 1
-    enumerator_block_call do |i|
+    n = offset - 1
+    enumerator_block_call do |*i|
       n += 1
-      yield [i,n]
+      yield i.__svalue, n
     end
   end
 
@@ -171,8 +177,8 @@ class Enumerator
   #
   # If no block is given, a new Enumerator is returned that includes the index.
   #
-  def each_with_index
-    with_index
+  def each_with_index(&block)
+    with_index(0, &block)
   end
 
   ##
@@ -516,6 +522,7 @@ class Enumerator
 
   # just for internal
   class Generator
+    include Enumerable
     def initialize(&block)
       raise TypeError, "wrong argument type #{self.class} (expected Proc)" unless block.kind_of? Proc
 
@@ -605,7 +612,7 @@ module Kernel
   def to_enum(meth=:each, *args)
     Enumerator.new self, meth, *args
   end
-  alias :enum_for :to_enum
+  alias enum_for to_enum
 end
 
 module Enumerable
index 2e45dae..763cd36 100644 (file)
@@ -50,6 +50,9 @@ end
 assert 'Enumerator#with_index' do
   assert_equal([[1,0],[2,1],[3,2]], @obj.to_enum(:foo, 1, 2, 3).with_index.to_a)
   assert_equal([[1,5],[2,6],[3,7]], @obj.to_enum(:foo, 1, 2, 3).with_index(5).to_a)
+  a = []
+  @obj.to_enum(:foo, 1, 2, 3).with_index(10).with_index(20) { |*i| a << i }
+  assert_equal [[[1, 10], 20], [[2, 11], 21], [[3, 12], 22]], a
 end
 
 assert 'Enumerator#with_index nonnum offset' do
@@ -62,6 +65,13 @@ assert 'Enumerator#with_index string offset' do
   assert_raise(TypeError){ @obj.to_enum(:foo, 1, 2, 3).with_index('1').to_a }
 end
 
+assert 'Enumerator#each_with_index' do
+  assert_equal([[1,0],[2,1],[3,2]], @obj.to_enum(:foo, 1, 2, 3).each_with_index.to_a)
+  a = []
+  @obj.to_enum(:foo, 1, 2, 3).each_with_index {|*i| a << i}
+  assert_equal([[1, 0], [2, 1], [3, 2]], a)
+end
+
 assert 'Enumerator#with_object' do
   obj = [0, 1]
   ret = (1..10).each.with_object(obj) {|i, memo|
index b8281b1..30a4259 100644 (file)
@@ -3,7 +3,7 @@ MRuby::Gem::Specification.new('mruby-error') do |spec|
   spec.author  = 'mruby developers'
   spec.summary = 'extensional error handling'
 
-  if build.cxx_abi_enabled?
+  if build.cxx_exception_enabled?
     @objs << build.compile_as_cxx("#{spec.dir}/src/exception.c", "#{spec.build_dir}/src/exception.cxx")
     @objs.delete_if { |v| v == objfile("#{spec.build_dir}/src/exception") }
   end
index 911fde0..170abb6 100644 (file)
@@ -1,6 +1,6 @@
-#include "mruby.h"
-#include "mruby/throw.h"
-#include "mruby/error.h"
+#include <mruby.h>
+#include <mruby/throw.h>
+#include <mruby/error.h>
 
 MRB_API mrb_value
 mrb_protect(mrb_state *mrb, mrb_func_t body, mrb_value data, mrb_bool *state)
index 2a943aa..4de0e96 100644 (file)
@@ -1,6 +1,6 @@
-#include "mruby.h"
-#include "mruby/error.h"
-#include "mruby/array.h"
+#include <mruby.h>
+#include <mruby/error.h>
+#include <mruby/array.h>
 
 static mrb_value
 protect_cb(mrb_state *mrb, mrb_value b)
index 0bbc2a0..9084650 100644 (file)
@@ -5,7 +5,7 @@ assert 'mrb_protect' do
   end
   # failure in protect returns [exception, true]
   result = ExceptionTest.mrb_protect { raise 'test' }
-  assert_kind_of RuntimeError, result[0]    
+  assert_kind_of RuntimeError, result[0]
   assert_true result[1]
 end
 
index dd5fd50..146c6df 100644 (file)
@@ -1,9 +1,13 @@
-#include "mruby.h"
-#include "mruby/class.h"
-#include "mruby/compile.h"
-#include "mruby/irep.h"
-#include "mruby/proc.h"
-#include "mruby/opcode.h"
+#include <mruby.h>
+#include <mruby/class.h>
+#include <mruby/compile.h>
+#include <mruby/irep.h>
+#include <mruby/proc.h>
+#include <mruby/opcode.h>
+#include <mruby/error.h>
+
+mrb_value mrb_exec_irep(mrb_state *mrb, mrb_value self, struct RProc *p);
+mrb_value mrb_obj_instance_eval(mrb_state *mrb, mrb_value self);
 
 static struct mrb_irep *
 get_closure_irep(mrb_state *mrb, int level)
@@ -26,6 +30,8 @@ get_closure_irep(mrb_state *mrb, int level)
   }
 
   if (!e) return NULL;
+  if (!MRB_ENV_STACK_SHARED_P(e)) return NULL;
+  c = e->cxt.c;
   proc = c->cibase[e->cioff].proc;
 
   if (!proc || MRB_PROC_CFUNC_P(proc)) {
@@ -34,6 +40,25 @@ get_closure_irep(mrb_state *mrb, int level)
   return proc->body.irep;
 }
 
+/* search for irep lev above the bottom */
+static mrb_irep*
+search_irep(mrb_irep *top, int bnest, int lev, mrb_irep *bottom)
+{
+  size_t i;
+
+  for (i=0; i<top->rlen; i++) {
+    mrb_irep* tmp = top->reps[i];
+
+    if (tmp == bottom) return top;
+    tmp = search_irep(tmp, bnest-1, lev, bottom);
+    if (tmp) {
+      if (bnest == lev) return top;
+      return tmp;
+    }
+  }
+  return NULL;
+}
+
 static inline mrb_code
 search_variable(mrb_state *mrb, mrb_sym vsym, int bnest)
 {
@@ -55,6 +80,20 @@ search_variable(mrb_state *mrb, mrb_sym vsym, int bnest)
   return 0;
 }
 
+static int
+irep_argc(mrb_irep *irep)
+{
+  mrb_code c;
+
+  c = irep->iseq[0];
+  if (GET_OPCODE(c) == OP_ENTER) {
+    mrb_aspec ax = GETARG_Ax(c);
+    /* extra 1 means a slot for block */
+    return MRB_ASPEC_REQ(ax)+MRB_ASPEC_OPT(ax)+MRB_ASPEC_REST(ax)+MRB_ASPEC_POST(ax)+1;
+  }
+  return 0;
+}
+
 static mrb_bool
 potential_upvar_p(struct mrb_locals *lv, uint16_t v, int argc, uint16_t nlocals)
 {
@@ -65,32 +104,24 @@ potential_upvar_p(struct mrb_locals *lv, uint16_t v, int argc, uint16_t nlocals)
 }
 
 static void
-patch_irep(mrb_state *mrb, mrb_irep *irep, int bnest)
+patch_irep(mrb_state *mrb, mrb_irep *irep, int bnest, mrb_irep *top)
 {
   size_t i;
   mrb_code c;
-  int argc = 0;
+  int argc = irep_argc(irep);
 
   for (i = 0; i < irep->ilen; i++) {
     c = irep->iseq[i];
     switch(GET_OPCODE(c)){
-    case OP_ENTER:
-      {
-        mrb_aspec ax = GETARG_Ax(c);
-        /* extra 1 means a slot for block */
-        argc = MRB_ASPEC_REQ(ax)+MRB_ASPEC_OPT(ax)+MRB_ASPEC_REST(ax)+MRB_ASPEC_POST(ax)+1;
-      }
-      break;
-
     case OP_EPUSH:
-      patch_irep(mrb, irep->reps[GETARG_Bx(c)], bnest + 1);
+      patch_irep(mrb, irep->reps[GETARG_Bx(c)], bnest + 1, top);
       break;
 
     case OP_LAMBDA:
       {
         int arg_c = GETARG_c(c);
         if (arg_c & OP_L_CAPTURE) {
-          patch_irep(mrb, irep->reps[GETARG_b(c)], bnest + 1);
+          patch_irep(mrb, irep->reps[GETARG_b(c)], bnest + 1, top);
         }
       }
       break;
@@ -126,12 +157,48 @@ patch_irep(mrb_state *mrb, mrb_irep *irep, int bnest)
         }
       }
       break;
+
+    case OP_GETUPVAR:
+      {
+        int lev = GETARG_C(c)+1;
+        mrb_irep *tmp = search_irep(top, bnest, lev, irep);
+        if (potential_upvar_p(tmp->lv, GETARG_B(c), irep_argc(tmp), tmp->nlocals)) {
+          mrb_code arg = search_variable(mrb, tmp->lv[GETARG_B(c)-1].name, bnest);
+          if (arg != 0) {
+            /* must replace */
+            irep->iseq[i] = MKOPCODE(OP_GETUPVAR) | MKARG_A(GETARG_A(c)) | arg;
+          }
+        }
+      }
+      break;
+
+    case OP_SETUPVAR:
+      {
+        int lev = GETARG_C(c)+1;
+        mrb_irep *tmp = search_irep(top, bnest, lev, irep);
+        if (potential_upvar_p(tmp->lv, GETARG_B(c), irep_argc(tmp), tmp->nlocals)) {
+          mrb_code arg = search_variable(mrb, tmp->lv[GETARG_B(c)-1].name, bnest);
+          if (arg != 0) {
+            /* must replace */
+            irep->iseq[i] = MKOPCODE(OP_SETUPVAR) | MKARG_A(GETARG_A(c)) | arg;
+          }
+        }
+      }
+      break;
+
+    case OP_STOP:
+      if (mrb->c->ci->acc >= 0) {
+        irep->iseq[i] = MKOP_AB(OP_RETURN, irep->nlocals, OP_R_NORMAL);
+      }
+      break;
     }
   }
 }
 
+void mrb_codedump_all(mrb_state*, struct RProc*);
+
 static struct RProc*
-create_proc_from_string(mrb_state *mrb, char *s, int len, mrb_value binding, char *file, mrb_int line)
+create_proc_from_string(mrb_state *mrb, char *s, int len, mrb_value binding, const char *file, mrb_int line)
 {
   mrbc_context *cxt;
   struct mrb_parser_state *p;
@@ -145,9 +212,8 @@ create_proc_from_string(mrb_state *mrb, char *s, int len, mrb_value binding, cha
 
   cxt = mrbc_context_new(mrb);
   cxt->lineno = line;
-  if (file) {
-    mrbc_filename(mrb, cxt, file);
-  }
+
+  mrbc_filename(mrb, cxt, file ? file : "(eval)");
   cxt->capture_errors = TRUE;
   cxt->no_optimize = TRUE;
 
@@ -160,12 +226,22 @@ create_proc_from_string(mrb_state *mrb, char *s, int len, mrb_value binding, cha
 
   if (0 < p->nerr) {
     /* parse error */
-    char buf[256];
-    int n;
-    n = snprintf(buf, sizeof(buf), "line %d: %s\n", p->error_buffer[0].lineno, p->error_buffer[0].message);
+    mrb_value str;
+
+    if (file) {
+      str = mrb_format(mrb, " file %S line %S: %S",
+                       mrb_str_new_cstr(mrb, file),
+                       mrb_fixnum_value(p->error_buffer[0].lineno),
+                       mrb_str_new_cstr(mrb, p->error_buffer[0].message));
+    }
+    else {
+      str = mrb_format(mrb, " line %S: %S",
+                       mrb_fixnum_value(p->error_buffer[0].lineno),
+                       mrb_str_new_cstr(mrb, p->error_buffer[0].message));
+    }
     mrb_parser_free(p);
     mrbc_context_free(mrb, cxt);
-    mrb_exc_raise(mrb, mrb_exc_new(mrb, E_SYNTAX_ERROR, buf, n));
+    mrb_exc_raise(mrb, mrb_exc_new_str(mrb, E_SYNTAX_ERROR, str));
   }
 
   proc = mrb_generate_code(mrb, p);
@@ -181,13 +257,15 @@ create_proc_from_string(mrb_state *mrb, char *s, int len, mrb_value binding, cha
   e = c->ci[-1].proc->env;
   if (!e) e = c->ci[-1].env;
   e = (struct REnv*)mrb_obj_alloc(mrb, MRB_TT_ENV, (struct RClass*)e);
-  e->mid = c->ci[-1].mid;
-  e->cioff = c->ci - c->cibase - 1;
+  e->cxt.c = c;
+  e->cioff = c->ci - c->cibase;
   e->stack = c->ci->stackent;
-  MRB_SET_ENV_STACK_LEN(e, c->ci[-1].proc->body.irep->nlocals);
-  c->ci->env = e;
+  MRB_SET_ENV_STACK_LEN(e, c->ci->proc->body.irep->nlocals);
+  c->ci->target_class = proc->target_class;
+  c->ci->env = 0;
   proc->env = e;
-  patch_irep(mrb, proc->body.irep, 0);
+  patch_irep(mrb, proc->body.irep, 0, proc->body.irep);
+  /* mrb_codedump_all(mrb, proc); */
 
   mrb_parser_free(p);
   mrbc_context_free(mrb, cxt);
@@ -196,6 +274,19 @@ create_proc_from_string(mrb_state *mrb, char *s, int len, mrb_value binding, cha
 }
 
 static mrb_value
+exec_irep(mrb_state *mrb, mrb_value self, struct RProc *proc)
+{
+  if (mrb->c->ci->acc < 0) {
+    mrb_value ret = mrb_top_run(mrb, proc, mrb->c->stack[0], 0);
+    if (mrb->exc) {
+      mrb_exc_raise(mrb, mrb_obj_value(mrb->exc));
+    }
+    return ret;
+  }
+  return mrb_exec_irep(mrb, self, proc);
+}
+
+static mrb_value
 f_eval(mrb_state *mrb, mrb_value self)
 {
   char *s;
@@ -203,32 +294,22 @@ f_eval(mrb_state *mrb, mrb_value self)
   mrb_value binding = mrb_nil_value();
   char *file = NULL;
   mrb_int line = 1;
-  mrb_value ret;
   struct RProc *proc;
 
   mrb_get_args(mrb, "s|ozi", &s, &len, &binding, &file, &line);
 
   proc = create_proc_from_string(mrb, s, len, binding, file, line);
-  ret = mrb_toplevel_run(mrb, proc);
-  if (mrb->exc) {
-    mrb_exc_raise(mrb, mrb_obj_value(mrb->exc));
-  }
-
-  return ret;
+  mrb_assert(!MRB_PROC_CFUNC_P(proc));
+  return exec_irep(mrb, self, proc);
 }
 
-mrb_value mrb_obj_instance_eval(mrb_state *mrb, mrb_value self);
-
-#define CI_ACC_SKIP    -1
-
 static mrb_value
 f_instance_eval(mrb_state *mrb, mrb_value self)
 {
-  struct mrb_context *c = mrb->c;
   mrb_value b;
   mrb_int argc; mrb_value *argv;
 
-  mrb_get_args(mrb, "*&", &argv, &argc, &b);
+  mrb_get_args(mrb, "*!&", &argv, &argc, &b);
 
   if (mrb_nil_p(b)) {
     char *s;
@@ -236,12 +317,15 @@ f_instance_eval(mrb_state *mrb, mrb_value self)
     char *file = NULL;
     mrb_int line = 1;
     mrb_value cv;
+    struct RProc *proc;
 
     mrb_get_args(mrb, "s|zi", &s, &len, &file, &line);
-    c->ci->acc = CI_ACC_SKIP;
     cv = mrb_singleton_class(mrb, self);
-    c->ci->target_class = mrb_class_ptr(cv);
-    return mrb_run(mrb, create_proc_from_string(mrb, s, len, mrb_nil_value(), file, line), self);
+    proc = create_proc_from_string(mrb, s, len, mrb_nil_value(), file, line);
+    proc->target_class = mrb_class_ptr(cv);
+    mrb->c->ci->env = NULL;
+    mrb_assert(!MRB_PROC_CFUNC_P(proc));
+    return exec_irep(mrb, self, proc);
   }
   else {
     mrb_get_args(mrb, "&", &b);
index 17860b1..66ca1fc 100644 (file)
@@ -64,6 +64,8 @@ assert('String instance_eval') do
   assert_equal(['test.rb', 10]) { obj.instance_eval('[__FILE__, __LINE__]', 'test.rb', 10)}
   assert_equal('test') { obj.instance_eval('@test') }
   assert_equal('test') { obj.instance_eval { @test } }
+  o = Object.new
+  assert_equal ['', o, o], o.instance_eval("[''].each { |s| break [s, o, self] }")
 end
 
 assert('Kernel.#eval(string) context') do
index 726dfd7..3e147f8 100644 (file)
@@ -1,5 +1,5 @@
 #include <stdlib.h>
-#include "mruby.h"
+#include <mruby.h>
 
 static mrb_value
 f_exit(mrb_state *mrb, mrb_value self)
index 93b3f12..9de175f 100644 (file)
@@ -1,12 +1,13 @@
-#include "mruby.h"
-#include "mruby/array.h"
-#include "mruby/class.h"
-#include "mruby/proc.h"
+#include <mruby.h>
+#include <mruby/array.h>
+#include <mruby/class.h>
+#include <mruby/proc.h>
 
 #define fiber_ptr(o) ((struct RFiber*)mrb_ptr(o))
 
 #define FIBER_STACK_INIT_SIZE 64
 #define FIBER_CI_INIT_SIZE 8
+#define CI_ACC_RESUMED -3
 
 /*
  *  call-seq:
@@ -73,6 +74,9 @@ fiber_init(mrb_state *mrb, mrb_value self)
 
   mrb_get_args(mrb, "&", &blk);
 
+  if (f->cxt) {
+    mrb_raise(mrb, E_RUNTIME_ERROR, "cannot initialize twice");
+  }
   if (mrb_nil_p(blk)) {
     mrb_raise(mrb, E_ARGUMENT_ERROR, "tried to create Fiber object without a block");
   }
@@ -81,9 +85,9 @@ fiber_init(mrb_state *mrb, mrb_value self)
     mrb_raise(mrb, E_FIBER_ERROR, "tried to create Fiber from C defined method");
   }
 
-  f->cxt = (struct mrb_context*)mrb_malloc(mrb, sizeof(struct mrb_context));
-  *f->cxt = mrb_context_zero;
-  c = f->cxt;
+  c = (struct mrb_context*)mrb_malloc(mrb, sizeof(struct mrb_context));
+  *c = mrb_context_zero;
+  f->cxt = c;
 
   /* initialize VM stack */
   slen = FIBER_STACK_INIT_SIZE;
@@ -121,6 +125,7 @@ fiber_init(mrb_state *mrb, mrb_value self)
   ci = c->ci;
   ci->target_class = p->target_class;
   ci->proc = p;
+  mrb_field_write_barrier(mrb, (struct RBasic*)mrb_obj_ptr(self), (struct RBasic*)p);
   ci->pc = p->body.irep->iseq;
   ci->nregs = p->body.irep->nregs;
   ci[1] = ci[0];
@@ -155,10 +160,9 @@ fiber_result(mrb_state *mrb, const mrb_value *a, mrb_int len)
 /* mark return from context modifying method */
 #define MARK_CONTEXT_MODIFY(c) (c)->ci->target_class = NULL
 
-static mrb_value
-fiber_switch(mrb_state *mrb, mrb_value self, mrb_int len, const mrb_value *a, mrb_bool resume)
+static void
+fiber_check_cfunc(mrb_state *mrb, struct mrb_context *c)
 {
-  struct mrb_context *c = fiber_check(mrb, self);
   mrb_callinfo *ci;
 
   for (ci = c->ci; ci >= c->cibase; ci--) {
@@ -166,41 +170,65 @@ fiber_switch(mrb_state *mrb, mrb_value self, mrb_int len, const mrb_value *a, mr
       mrb_raise(mrb, E_FIBER_ERROR, "can't cross C function boundary");
     }
   }
+}
+
+static void
+fiber_switch_context(mrb_state *mrb, struct mrb_context *c)
+{
+  if (mrb->c->fib) {
+    mrb_write_barrier(mrb, (struct RBasic*)mrb->c->fib);
+  }
+  c->status = MRB_FIBER_RUNNING;
+  mrb->c = c;
+}
+
+static mrb_value
+fiber_switch(mrb_state *mrb, mrb_value self, mrb_int len, const mrb_value *a, mrb_bool resume, mrb_bool vmexec)
+{
+  struct mrb_context *c = fiber_check(mrb, self);
+  struct mrb_context *old_c = mrb->c;
+  mrb_value value;
+
+  fiber_check_cfunc(mrb, c);
   if (resume && c->status == MRB_FIBER_TRANSFERRED) {
     mrb_raise(mrb, E_FIBER_ERROR, "resuming transferred fiber");
   }
-  if (c->status == MRB_FIBER_RUNNING || c->status == MRB_FIBER_RESUMING) {
-    mrb_raise(mrb, E_FIBER_ERROR, "double resume");
+  if (c->status == MRB_FIBER_RUNNING || c->status == MRB_FIBER_RESUMED) {
+    mrb_raise(mrb, E_FIBER_ERROR, "double resume (fib)");
   }
   if (c->status == MRB_FIBER_TERMINATED) {
     mrb_raise(mrb, E_FIBER_ERROR, "resuming dead fiber");
   }
-  mrb->c->status = resume ? MRB_FIBER_RESUMING : MRB_FIBER_TRANSFERRED;
+  mrb->c->status = resume ? MRB_FIBER_RESUMED : MRB_FIBER_TRANSFERRED;
   c->prev = resume ? mrb->c : (c->prev ? c->prev : mrb->root_c);
   if (c->status == MRB_FIBER_CREATED) {
-    mrb_value *b = c->stack+1;
-    mrb_value *e = b + len;
+    mrb_value *b, *e;
 
+    if (len >= c->stend - c->stack) {
+      mrb_raise(mrb, E_FIBER_ERROR, "too many arguments to fiber");
+    }
+    b = c->stack+1;
+    e = b + len;
     while (b<e) {
       *b++ = *a++;
     }
     c->cibase->argc = len;
-    if (c->prev->fib)
-      mrb_field_write_barrier(mrb, (struct RBasic*)c->fib, (struct RBasic*)c->prev->fib);
-    mrb_write_barrier(mrb, (struct RBasic*)c->fib);
-    c->status = MRB_FIBER_RUNNING;
-    mrb->c = c;
+    value = c->stack[0] = c->ci->proc->env->stack[0];
+  }
+  else {
+    value = fiber_result(mrb, a, len);
+  }
+  fiber_switch_context(mrb, c);
 
+  if (vmexec) {
+    c->vmexec = TRUE;
+    value = mrb_vm_exec(mrb, c->ci[-1].proc, c->ci->pc);
+    mrb->c = old_c;
+  }
+  else {
     MARK_CONTEXT_MODIFY(c);
-    return c->ci->proc->env->stack[0];
   }
-  MARK_CONTEXT_MODIFY(c);
-  if (c->prev->fib)
-    mrb_field_write_barrier(mrb, (struct RBasic*)c->fib, (struct RBasic*)c->prev->fib);
-  mrb_write_barrier(mrb, (struct RBasic*)c->fib);
-  c->status = MRB_FIBER_RUNNING;
-  mrb->c = c;
-  return fiber_result(mrb, a, len);
+  return value;
 }
 
 /*
@@ -223,16 +251,20 @@ fiber_resume(mrb_state *mrb, mrb_value self)
 {
   mrb_value *a;
   mrb_int len;
+  mrb_bool vmexec = FALSE;
 
-  mrb_get_args(mrb, "*", &a, &len);
-  return fiber_switch(mrb, self, len, a, TRUE);
+  mrb_get_args(mrb, "*!", &a, &len);
+  if (mrb->c->ci->acc < 0) {
+    vmexec = TRUE;
+  }
+  return fiber_switch(mrb, self, len, a, TRUE, vmexec);
 }
 
 /* resume thread with given arguments */
 MRB_API mrb_value
 mrb_fiber_resume(mrb_state *mrb, mrb_value fib, mrb_int len, const mrb_value *a)
 {
-  return fiber_switch(mrb, fib, len, a, TRUE);
+  return fiber_switch(mrb, fib, len, a, TRUE, TRUE);
 }
 
 /*
@@ -280,14 +312,13 @@ fiber_transfer(mrb_state *mrb, mrb_value self)
   mrb_value* a;
   mrb_int len;
 
-  mrb_get_args(mrb, "*", &a, &len);
+  fiber_check_cfunc(mrb, mrb->c);
+  mrb_get_args(mrb, "*!", &a, &len);
 
   if (c == mrb->root_c) {
     mrb->c->status = MRB_FIBER_TRANSFERRED;
-    mrb->c = c;
-    c->status = MRB_FIBER_RUNNING;
+    fiber_switch_context(mrb, c);
     MARK_CONTEXT_MODIFY(c);
-    mrb_write_barrier(mrb, (struct RBasic*)c->fib);
     return fiber_result(mrb, a, len);
   }
 
@@ -295,7 +326,7 @@ fiber_transfer(mrb_state *mrb, mrb_value self)
     return fiber_result(mrb, a, len);
   }
 
-  return fiber_switch(mrb, self, len, a, FALSE);
+  return fiber_switch(mrb, self, len, a, FALSE, FALSE);
 }
 
 /* yield values to the caller fiber */
@@ -304,23 +335,21 @@ MRB_API mrb_value
 mrb_fiber_yield(mrb_state *mrb, mrb_int len, const mrb_value *a)
 {
   struct mrb_context *c = mrb->c;
-  mrb_callinfo *ci;
 
-  for (ci = c->ci; ci >= c->cibase; ci--) {
-    if (ci->acc < 0) {
-      mrb_raise(mrb, E_FIBER_ERROR, "can't cross C function boundary");
-    }
-  }
   if (!c->prev) {
     mrb_raise(mrb, E_FIBER_ERROR, "can't yield from root fiber");
   }
 
+  fiber_check_cfunc(mrb, c);
   c->prev->status = MRB_FIBER_RUNNING;
   c->status = MRB_FIBER_SUSPENDED;
-  mrb->c = c->prev;
+  fiber_switch_context(mrb, c->prev);
   c->prev = NULL;
+  if (c->vmexec) {
+    c->vmexec = FALSE;
+    mrb->c->ci->acc = CI_ACC_RESUMED;
+  }
   MARK_CONTEXT_MODIFY(mrb->c);
-  mrb_write_barrier(mrb, (struct RBasic*)c->fib);
   return fiber_result(mrb, a, len);
 }
 
@@ -332,7 +361,9 @@ mrb_fiber_yield(mrb_state *mrb, mrb_int len, const mrb_value *a)
  *  along any arguments that were passed to it. The fiber will resume
  *  processing at this point when <code>resume</code> is called next.
  *  Any arguments passed to the next <code>resume</code> will be the
- *  value that this <code>Fiber.yield</code> expression evaluates to.
+ *
+ *  mruby limitation: Fiber resume/yield cannot cross C function boundary.
+ *  thus you cannot yield from #initialize which is called by mrb_funcall().
  */
 static mrb_value
 fiber_yield(mrb_state *mrb, mrb_value self)
@@ -340,7 +371,7 @@ fiber_yield(mrb_state *mrb, mrb_value self)
   mrb_value *a;
   mrb_int len;
 
-  mrb_get_args(mrb, "*", &a, &len);
+  mrb_get_args(mrb, "*!", &a, &len);
   return mrb_fiber_yield(mrb, len, a);
 }
 
index f01033a..103410a 100644 (file)
@@ -2,6 +2,7 @@ MRuby::Gem::Specification.new('mruby-hash-ext') do |spec|
   spec.license = 'MIT'
   spec.author  = 'mruby developers'
   spec.summary = 'Hash class extension'
-  spec.add_dependency 'mruby-enum-ext', :core => 'mruby-enum-ext'
-  spec.add_dependency 'mruby-array-ext', :core => 'mruby-array-ext'
+  spec.add_dependency 'mruby-enum-ext', core: 'mruby-enum-ext'
+  spec.add_dependency 'mruby-array-ext', core: 'mruby-array-ext'
+  spec.add_test_dependency 'mruby-enumerator', core: 'mruby-enumerator'
 end
index f72cb54..87817f8 100644 (file)
@@ -28,11 +28,11 @@ class Hash
     if length == 1
       o = object[0]
       if o.respond_to?(:to_hash)
-        h = Hash.new
+        h = self.new
         object[0].to_hash.each { |k, v| h[k] = v }
         return h
       elsif o.respond_to?(:to_a)
-        h = Hash.new
+        h = self.new
         o.to_a.each do |i|
           raise ArgumentError, "wrong element type #{i.class} (expected array)" unless i.respond_to?(:to_a)
           k, v = nil
@@ -53,7 +53,7 @@ class Hash
     unless length % 2 == 0
       raise ArgumentError, 'odd number of arguments for Hash'
     end
-    h = Hash.new
+    h = self.new
     0.step(length - 2, 2) do |i|
       h[object[i]] = object[i + 1]
     end
@@ -62,6 +62,25 @@ class Hash
 
   ##
   # call-seq:
+  #     Hash.try_convert(obj) -> hash or nil
+  #
+  # Try to convert <i>obj</i> into a hash, using to_hash method.
+  # Returns converted hash or nil if <i>obj</i> cannot be converted
+  # for any reason.
+  #
+  #     Hash.try_convert({1=>2})   # => {1=>2}
+  #     Hash.try_convert("1=>2")   # => nil
+  #
+  def self.try_convert(obj)
+    if obj.respond_to?(:to_hash)
+      obj.to_hash
+    else
+      nil
+    end
+  end
+
+  ##
+  # call-seq:
   #     hsh.merge!(other_hash)                                 -> hsh
   #     hsh.merge!(other_hash){|key, oldval, newval| block}    -> hsh
   #
@@ -192,7 +211,7 @@ class Hash
   #
 
   def invert
-    h = Hash.new
+    h = self.class.new
     self.each {|k, v| h[v] = k }
     h
   end
@@ -346,4 +365,97 @@ class Hash
       key?(key) and self[key] == val
     }
   end
+
+  ##
+  # call-seq:
+  #   hsh.dig(key,...)                 -> object
+  #
+  # Extracts the nested value specified by the sequence of <i>key</i>
+  # objects by calling +dig+ at each step, returning +nil+ if any
+  # intermediate step is +nil+.
+  #
+  def dig(idx,*args)
+    n = self[idx]
+    if args.size > 0
+      n&.dig(*args)
+    else
+      n
+    end
+  end
+
+  ##
+  # call-seq:
+  #    hsh.transform_keys {|key| block } -> new_hash
+  #    hsh.transform_keys                -> an_enumerator
+  #
+  # Returns a new hash, with the keys computed from running the block
+  # once for each key in the hash, and the values unchanged.
+  #
+  # If no block is given, an enumerator is returned instead.
+  #
+  def transform_keys(&b)
+    return to_enum :transform_keys unless block_given?
+    hash = {}
+    self.keys.each do |k|
+      new_key = yield(k)
+      hash[new_key] = self[k]
+    end
+    hash
+  end
+  ##
+  # call-seq:
+  #    hsh.transform_keys! {|key| block } -> hsh
+  #    hsh.transform_keys!                -> an_enumerator
+  #
+  # Invokes the given block once for each key in <i>hsh</i>, replacing it
+  # with the new key returned by the block, and then returns <i>hsh</i>.
+  #
+  # If no block is given, an enumerator is returned instead.
+  #
+  def transform_keys!(&b)
+    return to_enum :transform_keys! unless block_given?
+    self.keys.each do |k|
+      value = self[k]
+      new_key = yield(k)
+      self.__delete(k)
+      self[new_key] = value
+    end
+    self
+  end
+  ##
+  # call-seq:
+  #    hsh.transform_values {|value| block } -> new_hash
+  #    hsh.transform_values                  -> an_enumerator
+  #
+  # Returns a new hash with the results of running the block once for
+  # every value.
+  # This method does not change the keys.
+  #
+  # If no block is given, an enumerator is returned instead.
+  #
+  def transform_values(&b)
+    return to_enum :transform_values unless block_given?
+    hash = {}
+    self.keys.each do |k|
+      hash[k] = yield(self[k])
+    end
+    hash
+  end
+  ##
+  # call-seq:
+  #    hsh.transform_values! {|key| block } -> hsh
+  #    hsh.transform_values!                -> an_enumerator
+  #
+  # Invokes the given block once for each value in the hash, replacing
+  # with the new value returned by the block, and then returns <i>hsh</i>.
+  #
+  # If no block is given, an enumerator is returned instead.
+  #
+  def transform_values!(&b)
+    return to_enum :transform_values! unless block_given?
+    self.keys.each do |k|
+      self[k] = yield(self[k])
+    end
+    self
+  end
 end
index bce842c..61abc08 100644 (file)
@@ -4,9 +4,9 @@
 ** See Copyright Notice in mruby.h
 */
 
-#include "mruby.h"
-#include "mruby/array.h"
-#include "mruby/hash.h"
+#include <mruby.h>
+#include <mruby/array.h>
+#include <mruby/hash.h>
 
 /*
  * call-seq:
@@ -26,7 +26,7 @@ hash_values_at(mrb_state *mrb, mrb_value hash)
   mrb_int argc, i;
   int ai;
 
-  mrb_get_args(mrb, "*", &argv, &argc);
+  mrb_get_args(mrb, "*!", &argv, &argc);
   result = mrb_ary_new_capa(mrb, argc);
   ai = mrb_gc_arena_save(mrb);
   for (i = 0; i < argc; i++) {
index 4a2251b..2ae88c3 100644 (file)
@@ -39,6 +39,18 @@ assert('Hash.[] "c_key", "c_value"') do
   end
 end
 
+assert('Hash.[] for sub class') do
+  sub_hash_class = Class.new(Hash)
+  sub_hash = sub_hash_class[]
+  assert_equal(sub_hash_class, sub_hash.class)
+end
+
+assert('Hash.try_convert') do
+  assert_nil Hash.try_convert(nil)
+  assert_nil Hash.try_convert("{1=>2}")
+  assert_equal({1=>2}, Hash.try_convert({1=>2}))
+end
+
 assert('Hash#merge!') do
   a = { 'abc_key' => 'abc_value', 'cba_key' => 'cba_value' }
   b = { 'cba_key' => 'XXX',  'xyz_key' => 'xyz_value' }
@@ -137,6 +149,12 @@ assert("Hash#invert") do
   assert_equal('b', h[2])
 end
 
+assert("Hash#invert with sub class") do
+  sub_hash_class = Class.new(Hash)
+  sub_hash = sub_hash_class.new
+  assert_equal(sub_hash_class, sub_hash.invert.class)
+end
+
 assert("Hash#keep_if") do
   h = { 1 => 2, 3 => 4, 5 => 6 }
   assert_equal({3=>4,5=>6}, h.keep_if {|k, v| k + v >= 7 })
@@ -230,3 +248,33 @@ assert('Hash#>') do
   assert_false(h2 > h1)
   assert_false(h2 > h2)
 end
+
+assert("Hash#dig") do
+  h = {a:{b:{c:1}}}
+  assert_equal(1, h.dig(:a, :b, :c))
+  assert_nil(h.dig(:d))
+end
+
+assert("Hash#transform_keys") do
+  h = {"1" => 100, "2" => 200}
+  assert_equal(h.transform_keys{|k| k+"!"},
+               {"1!" => 100, "2!" => 200})
+  assert_equal(h.transform_keys{|k|k.to_i},
+               {1 => 100, 2 => 200})
+  assert_equal(h.transform_keys.with_index{|k, i| "#{k}.#{i}"},
+               {"1.0" => 100, "2.1" => 200})
+  assert_equal(h.transform_keys!{|k|k.to_i}, h)
+  assert_equal(h, {1 => 100, 2 => 200})
+end
+
+assert("Hash#transform_values") do
+  h = {a: 1, b: 2, c: 3}
+  assert_equal(h.transform_values{|v| v * v + 1},
+               {a: 2, b: 5, c: 10})
+  assert_equal(h.transform_values{|v|v.to_s},
+               {a: "1", b: "2", c: "3"})
+  assert_equal(h.transform_values.with_index{|v, i| "#{v}.#{i}"},
+               {a: "1.0", b: "2.1", c: "3.2"})
+  assert_equal(h.transform_values!{|v|v.to_s}, h)
+  assert_equal(h, {a: "1", b: "2", c: "3"})
+end
diff --git a/third-party/mruby/mrbgems/mruby-inline-struct/mrbgem.rake b/third-party/mruby/mrbgems/mruby-inline-struct/mrbgem.rake
new file mode 100644 (file)
index 0000000..91ad9f4
--- /dev/null
@@ -0,0 +1,5 @@
+MRuby::Gem::Specification.new('mruby-inline-struct') do |spec|
+  spec.license = 'MIT'
+  spec.author  = 'mruby developers'
+  spec.summary = 'inline structure'
+end
diff --git a/third-party/mruby/mrbgems/mruby-inline-struct/test/inline.c b/third-party/mruby/mrbgems/mruby-inline-struct/test/inline.c
new file mode 100644 (file)
index 0000000..0baaab6
--- /dev/null
@@ -0,0 +1,83 @@
+#include <mruby.h>
+#include <mruby/class.h>
+#include <mruby/string.h>
+#include <mruby/istruct.h>
+
+static mrb_value
+istruct_test_initialize(mrb_state *mrb, mrb_value self)
+{
+  char *string = (char*)mrb_istruct_ptr(self);
+  mrb_int size = mrb_istruct_size();
+  mrb_value object;
+  mrb_get_args(mrb, "o", &object);
+
+  if (mrb_float_p(object))
+  {
+    snprintf(string, size, "float(%.3f)", mrb_float(object));
+  }
+  else if (mrb_fixnum_p(object))
+  {
+    snprintf(string, size, "fixnum(%" MRB_PRId ")", mrb_fixnum(object));
+  }
+  else if (mrb_string_p(object))
+  {
+    snprintf(string, size, "string(%s)", mrb_string_value_cstr(mrb, &object));
+  }
+
+  string[size - 1] = 0; // force NULL at the end
+  return self;
+}
+
+static mrb_value
+istruct_test_to_s(mrb_state *mrb, mrb_value self)
+{
+  return mrb_str_new_cstr(mrb, (const char*)mrb_istruct_ptr(self));
+}
+
+static mrb_value
+istruct_test_length(mrb_state *mrb, mrb_value self)
+{
+  return mrb_fixnum_value(mrb_istruct_size());
+}
+
+static mrb_value
+istruct_test_test_receive(mrb_state *mrb, mrb_value self)
+{
+  mrb_value object;
+  mrb_get_args(mrb, "o", &object);
+  if (mrb_obj_class(mrb, object) != mrb_class_get(mrb, "InlineStructTest"))
+  {
+    mrb_raisef(mrb, E_TYPE_ERROR, "Expected InlineStructTest");
+  }
+  return mrb_bool_value(((char*)mrb_istruct_ptr(object))[0] == 's');
+}
+
+static mrb_value
+istruct_test_test_receive_direct(mrb_state *mrb, mrb_value self)
+{
+  char *ptr;
+  mrb_get_args(mrb, "I", &ptr);
+  return mrb_bool_value(ptr[0] == 's');
+}
+
+static mrb_value
+istruct_test_mutate(mrb_state *mrb, mrb_value self)
+{
+  char *ptr = (char*)mrb_istruct_ptr(self);
+  memcpy(ptr, "mutate", 6);
+  return mrb_nil_value();
+}
+
+void mrb_mruby_inline_struct_gem_test(mrb_state *mrb)
+{
+  struct RClass *cls;
+
+  cls = mrb_define_class(mrb, "InlineStructTest", mrb->object_class);
+  MRB_SET_INSTANCE_TT(cls, MRB_TT_ISTRUCT);
+  mrb_define_method(mrb, cls, "initialize", istruct_test_initialize, MRB_ARGS_REQ(1));
+  mrb_define_method(mrb, cls, "to_s", istruct_test_to_s, MRB_ARGS_NONE());
+  mrb_define_method(mrb, cls, "mutate", istruct_test_mutate, MRB_ARGS_NONE());
+  mrb_define_class_method(mrb, cls, "length", istruct_test_length, MRB_ARGS_NONE());
+  mrb_define_class_method(mrb, cls, "test_receive", istruct_test_test_receive, MRB_ARGS_REQ(1));
+  mrb_define_class_method(mrb, cls, "test_receive_direct", istruct_test_test_receive_direct, MRB_ARGS_REQ(1));
+}
diff --git a/third-party/mruby/mrbgems/mruby-inline-struct/test/inline.rb b/third-party/mruby/mrbgems/mruby-inline-struct/test/inline.rb
new file mode 100644 (file)
index 0000000..4958592
--- /dev/null
@@ -0,0 +1,151 @@
+##
+# InlineStruct Test
+
+class InlineStructTest
+  def extra_method
+    :ok
+  end
+
+  def test_ivar_set
+    @var = :ivar
+  end
+
+  def test_ivar_get
+    @vat
+  end
+end
+
+assert('InlineStructTest#dup') do
+  obj = InlineStructTest.new(1)
+  assert_equal obj.to_s, 'fixnum(1)'
+  assert_equal obj.dup.to_s, 'fixnum(1)'
+end
+
+assert('InlineStructTest#clone') do
+  obj = InlineStructTest.new(1)
+  assert_equal obj.to_s, 'fixnum(1)'
+  assert_equal obj.clone.to_s, 'fixnum(1)'
+end
+
+assert('InlineStruct#object_id') do
+  obj1 = InlineStructTest.new(1)
+  obj2 = InlineStructTest.new(1)
+  assert_not_equal obj1, obj2
+  assert_not_equal obj1.object_id, obj2.object_id
+  assert_not_equal obj1.object_id, obj1.dup.object_id
+  assert_not_equal obj1.object_id, obj1.clone.object_id
+end
+
+assert('InlineStructTest#mutate (dup)') do
+  obj1 = InlineStructTest.new("foo")
+  assert_equal obj1.to_s, "string(foo)"
+  obj2 = obj1.dup
+  assert_equal obj2.to_s, "string(foo)"
+  obj1.mutate
+  assert_equal obj1.to_s, "mutate(foo)"
+  assert_equal obj2.to_s, "string(foo)"
+end
+
+assert('InlineStructTest#mutate (clone)') do
+  obj1 = InlineStructTest.new("foo")
+  assert_equal obj1.to_s, "string(foo)"
+  obj2 = obj1.clone
+  assert_equal obj2.to_s, "string(foo)"
+  obj1.mutate
+  assert_equal obj1.to_s, "mutate(foo)"
+  assert_equal obj2.to_s, "string(foo)"
+end
+
+assert('InlineStructTest#test_receive(string)') do
+  assert_equal InlineStructTest.test_receive(InlineStructTest.new('a')), true
+end
+
+assert('InlineStructTest#test_receive(float)') do
+  assert_equal InlineStructTest.test_receive(InlineStructTest.new(1.25)), false
+end
+
+assert('InlineStructTest#test_receive(invalid object)') do
+  assert_raise(TypeError) do
+    InlineStructTest.test_receive([])
+  end
+end
+
+assert('InlineStructTest#test_receive(string)') do
+  assert_equal InlineStructTest.test_receive_direct(InlineStructTest.new('a')), true
+end
+
+assert('InlineStructTest#test_receive(float)') do
+  assert_equal InlineStructTest.test_receive_direct(InlineStructTest.new(1.25)), false
+end
+
+assert('InlineStructTest#test_receive(invalid object)') do
+  assert_raise(TypeError) do
+    InlineStructTest.test_receive_direct([])
+  end
+end
+
+assert('InlineStructTest#extra_method') do
+  assert_equal InlineStructTest.new(1).extra_method, :ok
+end
+
+assert('InlineStructTest instance variable') do
+  obj = InlineStructTest.new(1)
+  assert_raise(ArgumentError) do
+    obj.test_ivar_set
+  end
+  assert_equal obj.test_ivar_get, nil
+end
+
+# 64-bit mode
+if InlineStructTest.length == 24
+  assert('InlineStructTest length [64 bit]') do
+    assert_equal InlineStructTest.length, 3 * 8
+  end
+
+  assert('InlineStructTest w/float [64 bit]') do
+    obj = InlineStructTest.new(1.25)
+    assert_equal obj.to_s, "float(1.250)"
+  end
+
+  assert('InlineStructTest w/fixnum [64 bit]') do
+    obj = InlineStructTest.new(42)
+    assert_equal obj.to_s, "fixnum(42)"
+  end
+
+  assert('InlineStructTest w/string [64 bit]') do
+    obj = InlineStructTest.new("hello")
+    assert_equal obj.to_s, "string(hello)"
+  end
+
+  assert('InlineStructTest w/long string [64 bit]') do
+    obj = InlineStructTest.new("this won't fit in 3 * 8 bytes available for the structure")
+    assert_equal obj.to_s, "string(this won't fit i"
+  end
+end
+
+# 32-bit mode
+if InlineStructTest.length == 12
+  assert('InlineStructTest length [32 bit]') do
+    assert_equal InlineStructTest.length, 3 * 4
+  end
+
+  assert('InlineStructTest w/float [32 bit]') do
+    obj = InlineStructTest.new(1.25)
+    assert_equal obj.to_s, "float(1.250"
+  end
+
+  assert('InlineStructTest w/fixnum [32 bit]') do
+    obj = InlineStructTest.new(42)
+    assert_equal obj.to_s, "fixnum(42)"
+  end
+
+  assert('InlineStructTest w/string [32 bit]') do
+    obj = InlineStructTest.new("hello")
+    assert_equal obj.to_s, "string(hell"
+  end
+
+  assert('InlineStructTest w/long string [32 bit]') do
+    obj = InlineStructTest.new("this won't fit in 3 * 4 bytes available for the structure")
+    assert_equal obj.to_s, "string(this"
+  end
+end
index c578b67..7e6fa28 100644 (file)
@@ -1,7 +1,65 @@
-#include "mruby.h"
-#include "mruby/error.h"
-#include "mruby/array.h"
-#include "mruby/hash.h"
+#include <mruby.h>
+#include <mruby/error.h>
+#include <mruby/array.h>
+#include <mruby/hash.h>
+#include <mruby/range.h>
+
+static mrb_value
+mrb_f_caller(mrb_state *mrb, mrb_value self)
+{
+  mrb_value bt, v, length;
+  mrb_int bt_len, argc, lev, n;
+
+  bt = mrb_get_backtrace(mrb);
+  bt_len = RARRAY_LEN(bt);
+  argc = mrb_get_args(mrb, "|oo", &v, &length);
+
+  switch (argc) {
+    case 0:
+      lev = 1;
+      n = bt_len - lev;
+      break;
+    case 1:
+      if (mrb_type(v) == MRB_TT_RANGE) {
+        mrb_int beg, len;
+        if (mrb_range_beg_len(mrb, v, &beg, &len, bt_len, TRUE) == 1) {
+          lev = beg;
+          n = len;
+        }
+        else {
+          return mrb_nil_value();
+        }
+      }
+      else {
+        v = mrb_to_int(mrb, v);
+        lev = mrb_fixnum(v);
+        if (lev < 0) {
+          mrb_raisef(mrb, E_ARGUMENT_ERROR, "negative level (%S)", v);
+        }
+        n = bt_len - lev;
+      }
+      break;
+    case 2:
+      lev = mrb_fixnum(mrb_to_int(mrb, v));
+      n = mrb_fixnum(mrb_to_int(mrb, length));
+      if (lev < 0) {
+        mrb_raisef(mrb, E_ARGUMENT_ERROR, "negative level (%S)", v);
+      }
+      if (n < 0) {
+        mrb_raisef(mrb, E_ARGUMENT_ERROR, "negative size (%S)", length);
+      }
+      break;
+    default:
+      lev = n = 0;
+      break;
+  }
+
+  if (n == 0) {
+    return mrb_ary_new(mrb);
+  }
+
+  return mrb_funcall(mrb, bt, "[]", 2, mrb_fixnum_value(lev), mrb_fixnum_value(n));
+}
 
 /*
  *  call-seq:
@@ -61,7 +119,7 @@ mrb_f_integer(mrb_state *mrb, mrb_value self)
  *     Float(arg)    -> float
  *
  *  Returns <i>arg</i> converted to a float. Numeric types are converted
- *  directly, the rest are converted using <i>arg</i>.to_f. 
+ *  directly, the rest are converted using <i>arg</i>.to_f.
  *
  *     Float(1)           #=> 1.0
  *     Float(123.456)     #=> 123.456
@@ -170,6 +228,7 @@ mrb_mruby_kernel_ext_gem_init(mrb_state *mrb)
   struct RClass *krn = mrb->kernel_module;
 
   mrb_define_module_function(mrb, krn, "fail", mrb_f_raise, MRB_ARGS_OPT(2));
+  mrb_define_module_function(mrb, krn, "caller", mrb_f_caller, MRB_ARGS_OPT(2));
   mrb_define_method(mrb, krn, "__method__", mrb_f_method, MRB_ARGS_NONE());
   mrb_define_module_function(mrb, krn, "Integer", mrb_f_integer, MRB_ARGS_ANY());
   mrb_define_module_function(mrb, krn, "Float", mrb_f_float, MRB_ARGS_REQ(1));
index c58fb46..89cedaf 100644 (file)
@@ -3,6 +3,37 @@ assert('Kernel.fail, Kernel#fail') do
   assert_raise(RuntimeError) { Kernel.fail }
 end
 
+assert('Kernel.caller, Kernel#caller') do
+  skip "backtrace isn't available" if caller(0).empty?
+
+  caller_lineno = __LINE__ + 3
+  c = Class.new do
+    def foo(*args)
+      caller(*args)
+    end
+
+    def bar(*args)
+      foo(*args)
+    end
+
+    def baz(*args)
+      bar(*args)
+    end
+  end
+  assert_equal "kernel.rb:#{caller_lineno}:in foo", c.new.baz(0)[0][-19..-1]
+  assert_equal "bar", c.new.baz[0][-3..-1]
+  assert_equal "foo", c.new.baz(0)[0][-3..-1]
+  assert_equal "bar", c.new.baz(1)[0][-3..-1]
+  assert_equal "baz", c.new.baz(2)[0][-3..-1]
+  assert_equal ["foo", "bar"], c.new.baz(0, 2).map { |i| i[-3..-1] }
+  assert_equal ["bar", "baz"], c.new.baz(1..2).map { |i| i[-3..-1] }
+  assert_nil c.new.baz(10..20)
+  assert_raise(ArgumentError) { c.new.baz(-1) }
+  assert_raise(ArgumentError) { c.new.baz(-1, 1) }
+  assert_raise(ArgumentError) { c.new.baz(1, -1) }
+  assert_raise(TypeError) { c.new.baz(nil) }
+end
+
 assert('Kernel#__method__') do
   assert_equal(:m, Class.new {def m; __method__; end}.new.m)
   assert_equal(:m, Class.new {define_method(:m) {__method__}}.new.m)
@@ -22,6 +53,8 @@ assert('Kernel#Integer') do
   assert_equal(26, Integer("0x1a"))
   assert_equal(930, Integer("0930", 10))
   assert_equal(7, Integer("111", 2))
+  assert_equal(0, Integer("0"))
+  assert_equal(0, Integer("00000"))
   assert_raise(TypeError) { Integer(nil) }
 end
 
index 79ad18b..7302b92 100644 (file)
@@ -4,8 +4,8 @@
 ** See Copyright Notice in mruby.h
 */
 
-#include "mruby.h"
-#include "mruby/array.h"
+#include <mruby.h>
+#include <mruby/array.h>
 
 #include <math.h>
 
@@ -19,7 +19,7 @@ domain_error(mrb_state *mrb, const char *func)
 }
 
 /* math functions not provided by Microsoft Visual C++ 2012 or older */
-#if defined _MSC_VER && _MSC_VER < 1800
+#if defined _MSC_VER && _MSC_VER <= 1700
 
 #include <float.h>
 
index dfc6ba8..0bf3c6a 100644 (file)
@@ -2,4 +2,16 @@ module Integral
   def div(other)
     self.divmod(other)[0]
   end
+
+  def zero?
+    self == 0
+  end
+
+  def nonzero?
+    if self == 0
+      nil
+    else
+      self
+    end
+  end
 end
index 22c1668..f71236a 100644 (file)
@@ -1,5 +1,5 @@
 #include <limits.h>
-#include "mruby.h"
+#include <mruby.h>
 
 static mrb_value
 mrb_int_chr(mrb_state *mrb, mrb_value x)
index 96644da..8bead24 100644 (file)
@@ -16,3 +16,13 @@ end
 assert('Float#div') do
   assert_float 52, 365.2425.div(7)
 end
+
+assert('Integer#zero?') do
+  assert_equal true, 0.zero?
+  assert_equal false, 1.zero?
+end
+
+assert('Integer#nonzero?') do
+  assert_equal nil, 0.nonzero?
+  assert_equal 1000, 1000.nonzero?
+end
index dc1924d..35a07b5 100644 (file)
@@ -1,6 +1,7 @@
-#include "mruby.h"
-#include "mruby/array.h"
-#include "mruby/class.h"
+#include <mruby.h>
+#include <mruby/array.h>
+#include <mruby/class.h>
+#include <mruby/proc.h>
 
 /*
  *  call-seq:
@@ -62,7 +63,7 @@ nil_to_i(mrb_state *mrb, mrb_value obj)
 static mrb_value
 mrb_obj_instance_exec(mrb_state *mrb, mrb_value self)
 {
-  mrb_value *argv;
+  const mrb_value *argv;
   mrb_int argc;
   mrb_value blk;
   struct RClass *c;
@@ -83,8 +84,8 @@ mrb_obj_instance_exec(mrb_state *mrb, mrb_value self)
     c = mrb_class_ptr(mrb_singleton_class(mrb, self));
     break;
   }
-
-  return mrb_yield_with_class(mrb, blk, argc, argv, self, c);
+  mrb->c->ci->target_class = c;
+  return mrb_yield_cont(mrb, blk, self, argc, argv);
 }
 
 void
@@ -96,7 +97,7 @@ mrb_mruby_object_ext_gem_init(mrb_state* mrb)
   mrb_define_method(mrb, n, "to_f", nil_to_f,       MRB_ARGS_NONE());
   mrb_define_method(mrb, n, "to_i", nil_to_i,       MRB_ARGS_NONE());
 
-  mrb_define_method(mrb, mrb->object_class, "instance_exec", mrb_obj_instance_exec, MRB_ARGS_ANY() | MRB_ARGS_BLOCK());
+  mrb_define_method(mrb, mrb->kernel_module, "instance_exec", mrb_obj_instance_exec, MRB_ARGS_ANY() | MRB_ARGS_BLOCK());
 }
 
 void
index fe56f1e..f0742f8 100644 (file)
@@ -23,3 +23,31 @@ assert('Object#tap') do
   ], ret
   assert_equal(:tap_ok, Class.new {def m; tap{return :tap_ok}; end}.new.m)
 end
+
+assert('instance_exec on primitives with class and module definition') do
+  begin
+    class A
+      1.instance_exec do
+        class B
+        end
+      end
+    end
+
+    assert_kind_of Class, A::B
+  ensure
+    Object.remove_const :A
+  end
+
+  begin
+    class A
+      1.instance_exec do
+        module B
+        end
+      end
+    end
+
+    assert_kind_of Module, A::B
+  ensure
+    Object.remove_const :A
+  end
+end
index d516735..3887091 100644 (file)
@@ -1,7 +1,7 @@
-#include "mruby.h"
-#include "mruby/gc.h"
-#include "mruby/hash.h"
-#include "mruby/class.h"
+#include <mruby.h>
+#include <mruby/gc.h>
+#include <mruby/hash.h>
+#include <mruby/class.h>
 
 struct os_count_struct {
   mrb_int total;
@@ -9,7 +9,7 @@ struct os_count_struct {
   mrb_int counts[MRB_TT_MAXDEFINE+1];
 };
 
-static void
+static int
 os_count_object_type(mrb_state *mrb, struct RBasic *obj, void *data)
 {
   struct os_count_struct *obj_count;
@@ -23,6 +23,7 @@ os_count_object_type(mrb_state *mrb, struct RBasic *obj, void *data)
   else {
     obj_count->counts[obj->tt]++;
   }
+  return MRB_EACH_OBJ_OK;
 }
 
 /*
@@ -49,7 +50,7 @@ static mrb_value
 os_count_objects(mrb_state *mrb, mrb_value self)
 {
   struct os_count_struct obj_count = { 0 };
-  enum mrb_vtype i;
+  mrb_int i;
   mrb_value hash;
 
   if (mrb_get_args(mrb, "|H", &hash) == 0) {
@@ -109,35 +110,36 @@ struct os_each_object_data {
   mrb_int count;
 };
 
-static void
+static int
 os_each_object_cb(mrb_state *mrb, struct RBasic *obj, void *ud)
 {
   struct os_each_object_data *d = (struct os_each_object_data*)ud;
 
   /* filter dead objects */
   if (mrb_object_dead_p(mrb, obj)) {
-    return;
+    return MRB_EACH_OBJ_OK;
   }
 
   /* filter internal objects */
   switch (obj->tt) {
   case MRB_TT_ENV:
   case MRB_TT_ICLASS:
-    return;
+    return MRB_EACH_OBJ_OK;
   default:
     break;
   }
 
   /* filter half baked (or internal) objects */
-  if (!obj->c) return;
+  if (!obj->c) return MRB_EACH_OBJ_OK;
 
   /* filter class kind if target module defined */
   if (d->target_module && !mrb_obj_is_kind_of(mrb, mrb_obj_value(obj), d->target_module)) {
-    return;
+    return MRB_EACH_OBJ_OK;
   }
 
   mrb_yield(mrb, d->block, mrb_obj_value(obj));
   ++d->count;
+  return MRB_EACH_OBJ_OK;
 }
 
 /*
index 9b09b12..fd8d152 100644 (file)
@@ -1,24 +1,28 @@
-#include "mruby.h"
-#include "mruby/string.h"
+#include <mruby.h>
+#include <mruby/string.h>
 #include <stdio.h>
 #include <string.h>
 #include <stdlib.h>
-#if defined(__MINGW32__) || defined(__MINGW64__)
+#if defined(_WIN32)
 # include <windows.h>
 # include <io.h>
+#ifdef _MSC_VER
+# define isatty(x) _isatty(x)
+# define fileno(x) _fileno(x)
+#endif
 #endif
 
 static void
 printstr(mrb_state *mrb, mrb_value obj)
 {
   if (mrb_string_p(obj)) {
-#if defined(__MINGW32__) || defined(__MINGW64__)
+#if defined(_WIN32)
     if (isatty(fileno(stdout))) {
       DWORD written;
       int mlen = RSTRING_LEN(obj);
       char* utf8 = RSTRING_PTR(obj);
       int wlen = MultiByteToWideChar(CP_UTF8, 0, utf8, mlen, NULL, 0);
-      wchar_t* utf16 = mrb_malloc(mrb, (wlen+1) * sizeof(wchar_t));
+      wchar_t* utf16 = (wchar_t*)mrb_malloc(mrb, (wlen+1) * sizeof(wchar_t));
       if (utf16 == NULL) return;
       if (MultiByteToWideChar(CP_UTF8, 0, utf8, mlen, utf16, wlen) > 0) {
         utf16[wlen] = 0;
index 73873a3..b13606f 100644 (file)
@@ -1,9 +1,9 @@
-#include "mruby.h"
-#include "mruby/proc.h"
-#include "mruby/opcode.h"
-#include "mruby/array.h"
-#include "mruby/string.h"
-#include "mruby/debug.h"
+#include <mruby.h>
+#include <mruby/proc.h>
+#include <mruby/opcode.h>
+#include <mruby/array.h>
+#include <mruby/string.h>
+#include <mruby/debug.h>
 
 static mrb_value
 mrb_proc_lambda(mrb_state *mrb, mrb_value self)
@@ -52,7 +52,7 @@ mrb_proc_inspect(mrb_state *mrb, mrb_value self)
 
     line = mrb_debug_get_line(irep, 0);
     if (line != -1) {
-      mrb_str_append(mrb, str, mrb_fixnum_value(line));
+      str = mrb_format(mrb, "%S:%S", str, mrb_fixnum_value(line));
     }
     else {
       mrb_str_cat_lit(mrb, str, "-");
@@ -107,13 +107,16 @@ mrb_proc_parameters(mrb_state *mrb, mrb_value self)
   const struct RProc *proc = mrb_proc_ptr(self);
   const struct mrb_irep *irep = proc->body.irep;
   mrb_aspec aspec;
-  mrb_value parameters;
+  mrb_value sname, parameters;
   int i, j;
 
   if (MRB_PROC_CFUNC_P(proc)) {
     // TODO cfunc aspec is not implemented yet
     return mrb_ary_new(mrb);
   }
+  if (!irep) {
+    return mrb_ary_new(mrb);
+  }
   if (!irep->lv) {
     return mrb_ary_new(mrb);
   }
@@ -134,14 +137,17 @@ mrb_proc_parameters(mrb_state *mrb, mrb_value self)
   parameters_list[4].size = MRB_ASPEC_BLOCK(aspec);
 
   parameters = mrb_ary_new_capa(mrb, irep->nlocals-1);
+
   for (i = 0, p = parameters_list; p->name; p++) {
-    mrb_value sname = mrb_symbol_value(mrb_intern_cstr(mrb, p->name));
+    if (p->size <= 0) continue;
+    sname = mrb_symbol_value(mrb_intern_cstr(mrb, p->name));
     for (j = 0; j < p->size; i++, j++) {
-      mrb_assert(i < (irep->nlocals-1));
-      mrb_ary_push(mrb, parameters, mrb_assoc_new(mrb,
-        sname,
-        mrb_symbol_value(irep->lv[i].name)
-      ));
+      mrb_value a = mrb_ary_new(mrb);
+      mrb_ary_push(mrb, a, sname);
+      if (irep->lv[i].name) {
+        mrb_ary_push(mrb, a, mrb_symbol_value(irep->lv[i].name));
+      }
+      mrb_ary_push(mrb, parameters, a);
     }
   }
   return parameters;
index fcf8e26..a77b68e 100644 (file)
@@ -1,6 +1,6 @@
-#include "mruby.h"
-#include "mruby/proc.h"
-#include "mruby/class.h"
+#include <mruby.h>
+#include <mruby/proc.h>
+#include <mruby/class.h>
 
 static mrb_value
 return_func_name(mrb_state *mrb, mrb_value self)
index 75e11dd..424c0bc 100644 (file)
@@ -53,6 +53,7 @@ assert('Proc#parameters') do
   assert_equal([[:req, :a]], lambda {|a|}.parameters)
   assert_equal([[:opt, :a]], lambda {|a=nil|}.parameters)
   assert_equal([[:req, :a]], ->(a){}.parameters)
+  assert_equal([[:rest]], lambda { |*| }.parameters)
   assert_equal([[:rest, :a]], Proc.new {|*a|}.parameters)
   assert_equal([[:opt, :a], [:opt, :b], [:opt, :c], [:opt, :d], [:rest, :e], [:opt, :f], [:opt, :g], [:block, :h]], Proc.new {|a,b,c=:c,d=:d,*e,f,g,&h|}.parameters)
   assert_equal([[:req, :a], [:req, :b], [:opt, :c], [:opt, :d], [:rest, :e], [:req, :f], [:req, :g], [:block, :h]], lambda {|a,b,c=:c,d=:d,*e,f,g,&h|}.parameters)
index 310a276..bee06b5 100644 (file)
@@ -1,10 +1,39 @@
 /*
 ** mt19937ar.c - MT Random functions
 **
-** See Copyright Notice in mruby.h
+** Copyright (C) 1997 - 2016, Makoto Matsumoto and Takuji Nishimura,
+** 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.
+**
+** [ MIT license: http://www.opensource.org/licenses/mit-license.php ]
+**
+** Any feedback is very welcome.
+** http://www.math.sci.hiroshima-u.ac.jp/~m-mat/MT/emt.html
+** email: m-mat @ math.sci.hiroshima-u.ac.jp (remove space)
+**
+** This version is modified by mruby developers. If you see any problem,
+** contact us first at https://github.com/mruby/mruby/issues
 */
 
-#include "mruby.h"
+#include <mruby.h>
 #include "mt19937ar.h"
 
 /* Period parameters */
index 59027c6..7d38232 100644 (file)
@@ -1,7 +1,36 @@
 /*
 ** mt19937ar.h - MT Random functions
 **
-** See Copyright Notice in mruby.h
+** Copyright (C) 1997 - 2016, Makoto Matsumoto and Takuji Nishimura,
+** 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.
+**
+** [ MIT license: http://www.opensource.org/licenses/mit-license.php ]
+**
+** Any feedback is very welcome.
+** http://www.math.sci.hiroshima-u.ac.jp/~m-mat/MT/emt.html
+** email: m-mat @ math.sci.hiroshima-u.ac.jp (remove space)
+**
+** This version is modified by mruby developers. If you see any problem,
+** contact us first at https://github.com/mruby/mruby/issues
 */
 
 #define N 624
index 3be3ac8..b865244 100644 (file)
@@ -4,11 +4,11 @@
 ** See Copyright Notice in mruby.h
 */
 
-#include "mruby.h"
-#include "mruby/variable.h"
-#include "mruby/class.h"
-#include "mruby/data.h"
-#include "mruby/array.h"
+#include <mruby.h>
+#include <mruby/variable.h>
+#include <mruby/class.h>
+#include <mruby/data.h>
+#include <mruby/array.h>
 #include "mt19937ar.h"
 
 #include <time.h>
@@ -79,10 +79,10 @@ get_opt(mrb_state* mrb)
   mrb_get_args(mrb, "|o", &arg);
 
   if (!mrb_nil_p(arg)) {
-    if (!mrb_fixnum_p(arg)) {
+    arg = mrb_check_convert_type(mrb, arg, MRB_TT_FIXNUM, "Fixnum", "to_int");
+    if (mrb_nil_p(arg)) {
       mrb_raise(mrb, E_ARGUMENT_ERROR, "invalid argument type");
     }
-    arg = mrb_check_convert_type(mrb, arg, MRB_TT_FIXNUM, "Fixnum", "to_int");
     if (mrb_fixnum(arg) < 0) {
       arg = mrb_fixnum_value(0 - mrb_fixnum(arg));
     }
@@ -124,6 +124,8 @@ mrb_random_init(mrb_state *mrb, mrb_value self)
   mrb_value seed;
   mt_state *t;
 
+  seed = get_opt(mrb);
+
   /* avoid memory leaks */
   t = (mt_state*)DATA_PTR(self);
   if (t) {
@@ -134,7 +136,6 @@ mrb_random_init(mrb_state *mrb, mrb_value self)
   t = (mt_state *)mrb_malloc(mrb, sizeof(mt_state));
   t->mti = N + 1;
 
-  seed = get_opt(mrb);
   seed = mrb_random_mt_srand(mrb, t, seed);
   if (mrb_nil_p(seed)) {
     t->has_seed = FALSE;
@@ -216,13 +217,15 @@ mrb_ary_shuffle_bang(mrb_state *mrb, mrb_value ary)
 
     for (i = RARRAY_LEN(ary) - 1; i > 0; i--)  {
       mrb_int j;
+      mrb_value *ptr = RARRAY_PTR(ary);
       mrb_value tmp;
+      
 
       j = mrb_fixnum(mrb_random_mt_rand(mrb, random, mrb_fixnum_value(RARRAY_LEN(ary))));
 
-      tmp = RARRAY_PTR(ary)[i];
-      mrb_ary_ptr(ary)->ptr[i] = RARRAY_PTR(ary)[j];
-      mrb_ary_ptr(ary)->ptr[j] = tmp;
+      tmp = ptr[i];
+      ptr[i] = ptr[j];
+      ptr[j] = tmp;
     }
   }
 
@@ -266,7 +269,7 @@ mrb_ary_sample(mrb_state *mrb, mrb_value ary)
   mrb_int n = 0;
   mrb_bool given;
   mt_state *random = NULL;
-  mrb_int len = RARRAY_LEN(ary);
+  mrb_int len;
 
   mrb_get_args(mrb, "|i?d", &n, &given, &random, &mt_state_type);
   if (random == NULL) {
@@ -274,6 +277,7 @@ mrb_ary_sample(mrb_state *mrb, mrb_value ary)
   }
   mrb_random_rand_seed(mrb, random);
   mt_rand(random);
+  len = RARRAY_LEN(ary);
   if (!given) {                 /* pick one element */
     switch (len) {
     case 0:
index af82d66..a4785ae 100644 (file)
@@ -4,8 +4,8 @@
 ** See Copyright Notice in mruby.h
 */
 
-#ifndef RANDOM_H
-#define RANDOM_H
+#ifndef MRUBY_RANDOM_H
+#define MRUBY_RANDOM_H
 
 void mrb_mruby_random_gem_init(mrb_state *mrb);
 
index 1653ae4..1c59be3 100644 (file)
@@ -74,3 +74,15 @@ assert('Array#shuffle!(random)') do
 
   ary1 != [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] and 10.times { |x| ary1.include? x } and ary1 == ary2
 end
+
+assert('Array#sample checks input length after reading arguments') do
+  $ary = [1, 2, 3]
+  class ArrayChange
+    def to_i
+      $ary << 4
+      4
+    end
+  end
+
+  assert_equal [1, 2, 3, 4], $ary.sample(ArrayChange.new).sort
+end
diff --git a/third-party/mruby/mrbgems/mruby-range-ext/mrblib/range.rb b/third-party/mruby/mrbgems/mruby-range-ext/mrblib/range.rb
new file mode 100644 (file)
index 0000000..e5d1fb0
--- /dev/null
@@ -0,0 +1,31 @@
+class Range
+  ##
+  # call-seq:
+  #    rng.first    -> obj
+  #    rng.first(n) -> an_array
+  #
+  # Returns the first object in the range, or an array of the first +n+
+  # elements.
+  #
+  #   (10..20).first     #=> 10
+  #   (10..20).first(3)  #=> [10, 11, 12]
+  #
+  def first(*args)
+    return self.begin if args.empty?
+
+    raise ArgumentError, "wrong number of arguments (given #{args.length}, expected 1)" unless args.length == 1
+    nv = args[0]
+    raise TypeError, "no implicit conversion from nil to integer" if nv.nil?
+    raise TypeError, "no implicit conversion of #{nv.class} into Integer" unless nv.respond_to?(:to_int)
+    n = nv.to_int
+    raise TypeError, "no implicit conversion of #{nv.class} into Integer" unless n.kind_of?(Integer)
+    raise ArgumentError, "negative array size (or size too big)" unless 0 <= n
+    ary = []
+    each do |i|
+      break if n <= 0
+      ary.push(i)
+      n -= 1
+    end
+    ary
+  end
+end
index 35632ad..3131192 100644 (file)
@@ -1,6 +1,7 @@
-#include "mruby.h"
-#include "mruby/range.h"
+#include <mruby.h>
+#include <mruby/range.h>
 #include <math.h>
+#include <float.h>
 
 static mrb_bool
 r_le(mrb_state *mrb, mrb_value a, mrb_value b)
@@ -43,7 +44,7 @@ static mrb_value
 mrb_range_cover(mrb_state *mrb, mrb_value range)
 {
   mrb_value val;
-  struct RRange *r = mrb_range_ptr(range);
+  struct RRange *r = mrb_range_ptr(mrb, range);
   mrb_value beg, end;
 
   mrb_get_args(mrb, "o", &val);
@@ -67,32 +68,6 @@ mrb_range_cover(mrb_state *mrb, mrb_value range)
 
 /*
  *  call-seq:
- *     rng.first    -> obj
- *     rng.first(n) -> an_array
- *
- *  Returns the first object in the range, or an array of the first +n+
- *  elements.
- *
- *    (10..20).first     #=> 10
- *    (10..20).first(3)  #=> [10, 11, 12]
- */
-static mrb_value
-mrb_range_first(mrb_state *mrb, mrb_value range)
-{
-  mrb_int num;
-  mrb_value array;
-  struct RRange *r = mrb_range_ptr(range);
-
-  if (mrb_get_args(mrb, "|i", &num) == 0) {
-    return r->edges->beg;
-  }
-
-  array = mrb_funcall(mrb, range, "to_a", 0);
-  return mrb_funcall(mrb, array, "first", 1, mrb_fixnum_value(num));
-}
-
-/*
- *  call-seq:
  *     rng.last    -> obj
  *     rng.last(n) -> an_array
  *
@@ -112,7 +87,7 @@ mrb_range_last(mrb_state *mrb, mrb_value range)
 {
   mrb_value num;
   mrb_value array;
-  struct RRange *r = mrb_range_ptr(range);
+  struct RRange *r = mrb_range_ptr(mrb, range);
 
   if (mrb_get_args(mrb, "|o", &num) == 0) {
     return r->edges->end;
@@ -136,15 +111,17 @@ mrb_range_last(mrb_state *mrb, mrb_value range)
 static mrb_value
 mrb_range_size(mrb_state *mrb, mrb_value range)
 {
-  struct RRange *r = mrb_range_ptr(range);
+  struct RRange *r = mrb_range_ptr(mrb, range);
   mrb_value beg, end;
-  double beg_f, end_f;
+  mrb_float beg_f, end_f;
   mrb_bool num_p = TRUE;
+  mrb_bool excl;
 
   beg = r->edges->beg;
   end = r->edges->end;
+  excl = r->excl;
   if (mrb_fixnum_p(beg)) {
-    beg_f = (double)mrb_fixnum(beg);
+    beg_f = (mrb_float)mrb_fixnum(beg);
   }
   else if (mrb_float_p(beg)) {
     beg_f = mrb_float(beg);
@@ -153,7 +130,7 @@ mrb_range_size(mrb_state *mrb, mrb_value range)
     num_p = FALSE;
   }
   if (mrb_fixnum_p(end)) {
-    end_f = (double)mrb_fixnum(end);
+    end_f = (mrb_float)mrb_fixnum(end);
   }
   else if (mrb_float_p(end)) {
     end_f = mrb_float(end);
@@ -162,14 +139,24 @@ mrb_range_size(mrb_state *mrb, mrb_value range)
     num_p = FALSE;
   }
   if (num_p) {
-    double f;
-
-    if (beg_f > end_f) return mrb_fixnum_value(0);
-    f = end_f - beg_f;
-    if (!r->excl) {
-      return mrb_fixnum_value((mrb_int)ceil(f + 1));
+    mrb_float n = end_f - beg_f;
+    mrb_float err = (fabs(beg_f) + fabs(end_f) + fabs(end_f-beg_f)) * MRB_FLOAT_EPSILON;
+
+    if (err>0.5) err=0.5;
+    if (excl) {
+      if (n<=0) return mrb_fixnum_value(0);
+      if (n<1)
+        n = 0;
+      else
+        n = floor(n - err);
+    }
+    else {
+      if (n<0) return mrb_fixnum_value(0);
+      n = floor(n + err);
     }
-    return mrb_fixnum_value((mrb_int)ceil(f));
+    if (isinf(n+1))
+      return mrb_float_value(mrb, INFINITY);
+    return mrb_fixnum_value((mrb_int)n+1);
   }
   return mrb_nil_value();
 }
@@ -180,7 +167,6 @@ mrb_mruby_range_ext_gem_init(mrb_state* mrb)
   struct RClass * s = mrb_class_get(mrb, "Range");
 
   mrb_define_method(mrb, s, "cover?", mrb_range_cover, MRB_ARGS_REQ(1));
-  mrb_define_method(mrb, s, "first",  mrb_range_first, MRB_ARGS_OPT(1));
   mrb_define_method(mrb, s, "last",   mrb_range_last,  MRB_ARGS_OPT(1));
   mrb_define_method(mrb, s, "size",   mrb_range_size,  MRB_ARGS_NONE());
 }
index 18e7daf..efcbdab 100644 (file)
@@ -10,6 +10,7 @@ end
 assert('Range#first') do
   assert_equal 10, (10..20).first
   assert_equal [10, 11, 12], (10..20).first(3)
+  assert_equal [0, 1, 2], (0..Float::INFINITY).first(3)
 end
 
 assert('Range#last') do
@@ -25,5 +26,7 @@ assert('Range#size') do
   assert_equal 6, (1...6.3).size
   assert_equal 5, (1...6.0).size
   assert_equal 5, (1.1...6).size
+  assert_equal 15, (1.0..15.9).size
+  assert_equal Float::INFINITY, (0..Float::INFINITY).size
   assert_nil ('a'..'z').size
 end
index bd4f2bc..946b43a 100644 (file)
@@ -4,7 +4,7 @@
 ** See Copyright Notice in mruby.h
 */
 
-#include "mruby.h"
+#include <mruby.h>
 
 mrb_value mrb_f_sprintf(mrb_state *mrb, mrb_value obj); /* in sprintf.c */
 
index 81b48b1..7b82017 100644 (file)
@@ -4,20 +4,20 @@
 ** See Copyright Notice in mruby.h
 */
 
-#include "mruby.h"
+#include <mruby.h>
 
 #include <limits.h>
 #include <stdio.h>
 #include <string.h>
-#include "mruby/string.h"
-#include "mruby/hash.h"
-#include "mruby/numeric.h"
+#include <mruby/string.h>
+#include <mruby/hash.h>
+#include <mruby/numeric.h>
 #include <math.h>
 #include <ctype.h>
 
 #define BIT_DIGITS(N)   (((N)*146)/485 + 1)  /* log2(10) =~ 146/485 */
 #define BITSPERDIG MRB_INT_BIT
-#define EXTENDSIGN(n, l) (((~0 << (n)) >> (((n)*(l)) % BITSPERDIG)) & ~(~0 << (n)))
+#define EXTENDSIGN(n, l) (((~0U << (n)) >> (((n)*(l)) % BITSPERDIG)) & ~(~0U << (n)))
 
 mrb_value mrb_str_format(mrb_state *, int, const mrb_value *, mrb_value);
 static void fmt_setup(char*,size_t,int,int,mrb_int,mrb_int);
@@ -71,7 +71,7 @@ sign_bits(int base, const char *p)
 static mrb_value
 mrb_fix2binstr(mrb_state *mrb, mrb_value x, int base)
 {
-  char buf[64], *b = buf + sizeof buf;
+  char buf[66], *b = buf + sizeof buf;
   mrb_int num = mrb_fixnum(x);
   uint64_t val = (uint64_t)num;
   char d;
@@ -79,10 +79,6 @@ mrb_fix2binstr(mrb_state *mrb, mrb_value x, int base)
   if (base != 2) {
     mrb_raisef(mrb, E_ARGUMENT_ERROR, "invalid radix %S", mrb_fixnum_value(base));
   }
-
-  if (val >= (1 << 10))
-    val &= 0x3ff;
-
   if (val == 0) {
     return mrb_str_new_lit(mrb, "0");
   }
@@ -120,8 +116,9 @@ mrb_fix2binstr(mrb_state *mrb, mrb_value x, int base)
 
 #define CHECK(l) do {\
 /*  int cr = ENC_CODERANGE(result);*/\
-  while (blen + (l) >= bsiz) {\
+  while ((l) >= bsiz - blen) {\
     bsiz*=2;\
+    if (bsiz < 0) mrb_raise(mrb, E_ARGUMENT_ERROR, "too big specifier"); \
   }\
   mrb_str_resize(mrb, result, bsiz);\
 /*  ENC_CODERANGE_SET(result, cr);*/\
@@ -140,29 +137,63 @@ mrb_fix2binstr(mrb_state *mrb, mrb_value x, int base)
   blen += (l);\
 } while (0)
 
-#define GETARG() (!mrb_undef_p(nextvalue) ? nextvalue : \
-  posarg == -1 ? \
-  (mrb_raisef(mrb, E_ARGUMENT_ERROR, "unnumbered(%S) mixed with numbered", mrb_fixnum_value(nextarg)), mrb_undef_value()) : \
-  posarg == -2 ? \
-  (mrb_raisef(mrb, E_ARGUMENT_ERROR, "unnumbered(%S) mixed with named", mrb_fixnum_value(nextarg)), mrb_undef_value()) : \
+static void
+check_next_arg(mrb_state *mrb, int posarg, int nextarg)
+{
+  switch (posarg) {
+  case -1:
+    mrb_raisef(mrb, E_ARGUMENT_ERROR, "unnumbered(%S) mixed with numbered", mrb_fixnum_value(nextarg));
+    break;
+  case -2:
+    mrb_raisef(mrb, E_ARGUMENT_ERROR, "unnumbered(%S) mixed with named", mrb_fixnum_value(nextarg));
+    break;
+  default:
+    break;
+  }
+}
+
+static void
+check_pos_arg(mrb_state *mrb, int posarg, int n)
+{
+  if (posarg > 0) {
+    mrb_raisef(mrb, E_ARGUMENT_ERROR, "numbered(%S) after unnumbered(%S)",
+               mrb_fixnum_value(n), mrb_fixnum_value(posarg));
+  }
+  if (posarg == -2) {
+    mrb_raisef(mrb, E_ARGUMENT_ERROR, "numbered(%S) after named", mrb_fixnum_value(n));
+  }
+  if (n < 1) {
+    mrb_raisef(mrb, E_ARGUMENT_ERROR, "invalid index - %S$", mrb_fixnum_value(n));
+  }
+}
+
+static void
+check_name_arg(mrb_state *mrb, int posarg, const char *name, int len)
+{
+  if (posarg > 0) {
+    mrb_raisef(mrb, E_ARGUMENT_ERROR, "named%S after unnumbered(%S)",
+               mrb_str_new(mrb, (name), (len)), mrb_fixnum_value(posarg));
+  }
+  if (posarg == -1) {
+    mrb_raisef(mrb, E_ARGUMENT_ERROR, "named%S after numbered", mrb_str_new(mrb, (name), (len)));
+  }
+}
+
+#define GETNEXTARG() (\
+  check_next_arg(mrb, posarg, nextarg),\
   (posarg = nextarg++, GETNTHARG(posarg)))
 
-#define GETPOSARG(n) (posarg > 0 ? \
-  (mrb_raisef(mrb, E_ARGUMENT_ERROR, "numbered(%S) after unnumbered(%S)", mrb_fixnum_value(n), mrb_fixnum_value(posarg)), mrb_undef_value()) : \
-  posarg == -2 ? \
-  (mrb_raisef(mrb, E_ARGUMENT_ERROR, "numbered(%S) after named", mrb_fixnum_value(n)), mrb_undef_value()) : \
-  ((n < 1) ? \
-  (mrb_raisef(mrb, E_ARGUMENT_ERROR, "invalid index - %S$", mrb_fixnum_value(n)), mrb_undef_value()) : \
-  (posarg = -1, GETNTHARG(n))))
+#define GETARG() (!mrb_undef_p(nextvalue) ? nextvalue : GETNEXTARG())
+
+#define GETPOSARG(n) (\
+  check_pos_arg(mrb, posarg, n),\
+  (posarg = -1, GETNTHARG(n)))
 
 #define GETNTHARG(nth) \
   ((nth >= argc) ? (mrb_raise(mrb, E_ARGUMENT_ERROR, "too few arguments"), mrb_undef_value()) : argv[nth])
 
-#define GETNAMEARG(id, name, len) ( \
-  posarg > 0 ? \
-  (mrb_raisef(mrb, E_ARGUMENT_ERROR, "named%S after unnumbered(%S)", mrb_str_new(mrb, (name), (len)), mrb_fixnum_value(posarg)), mrb_undef_value()) : \
-  posarg == -1 ? \
-  (mrb_raisef(mrb, E_ARGUMENT_ERROR, "named%S after numbered", mrb_str_new(mrb, (name), (len))), mrb_undef_value()) :    \
+#define GETNAMEARG(id, name, len) (\
+  check_name_arg(mrb, posarg, name, len),\
   (posarg = -2, mrb_hash_fetch(mrb, get_hash(mrb, &hash, argc, argv), id, mrb_undef_value())))
 
 #define GETNUM(n, val) \
@@ -186,10 +217,10 @@ mrb_fix2binstr(mrb_state *mrb, mrb_value x, int base)
     tmp_v = GETPOSARG(n); \
   } \
   else { \
-    tmp_v = GETARG(); \
+    tmp_v = GETNEXTARG(); \
     p = t; \
   } \
-  num = mrb_fixnum(tmp_v); \
+  num = mrb_int(mrb, tmp_v); \
 } while (0)
 
 static mrb_value
@@ -497,7 +528,6 @@ mrb_str_format(mrb_state *mrb, int argc, const mrb_value *argv, mrb_value fmt)
   mrb_int n;
   mrb_int width;
   mrb_int prec;
-  int flags = FNONE;
   int nextarg = 1;
   int posarg = 0;
   mrb_value nextvalue;
@@ -533,8 +563,10 @@ mrb_str_format(mrb_state *mrb, int argc, const mrb_value *argv, mrb_value fmt)
   for (; p < end; p++) {
     const char *t;
     mrb_sym id = 0;
+    int flags = FNONE;
 
     for (t = p; t < end && *t != '%'; t++) ;
+    if (t + 1 == end) ++t;
     PUSH(p, t - p);
     if (t >= end)
       goto sprint_exit; /* end of fmt string */
@@ -669,12 +701,23 @@ retry:
 
         tmp = mrb_check_string_type(mrb, val);
         if (!mrb_nil_p(tmp)) {
-          if (mrb_fixnum(mrb_funcall(mrb, tmp, "size", 0)) != 1 ) {
+          if (RSTRING_LEN(tmp) != 1) {
             mrb_raise(mrb, E_ARGUMENT_ERROR, "%c requires a character");
           }
         }
         else if (mrb_fixnum_p(val)) {
-          tmp = mrb_funcall(mrb, val, "chr", 0);
+          mrb_int n = mrb_fixnum(val);
+
+          if (n < 0x80) {
+            char buf[1];
+
+            buf[0] = (char)n;
+            tmp = mrb_str_new(mrb, buf, 1);
+          }
+          else {
+            tmp = mrb_funcall(mrb, val, "chr", 0);
+            mrb_check_type(mrb, tmp, MRB_TT_STRING);
+          }
         }
         else {
           mrb_raise(mrb, E_ARGUMENT_ERROR, "invalid character");
@@ -682,21 +725,15 @@ retry:
         c = RSTRING_PTR(tmp);
         n = RSTRING_LEN(tmp);
         if (!(flags & FWIDTH)) {
-          CHECK(n);
-          memcpy(buf+blen, c, n);
-          blen += n;
+          PUSH(c, n);
         }
         else if ((flags & FMINUS)) {
-          CHECK(n);
-          memcpy(buf+blen, c, n);
-          blen += n;
-          FILL(' ', width-1);
+          PUSH(c, n);
+          if (width>0) FILL(' ', width-1);
         }
         else {
-          FILL(' ', width-1);
-          CHECK(n);
-          memcpy(buf+blen, c, n);
-          blen += n;
+          if (width>0) FILL(' ', width-1);
+          PUSH(c, n);
         }
       }
       break;
@@ -716,7 +753,8 @@ retry:
           mrb_int tmp_n = len;
           RSTRING(result)->flags &= ~MRB_STR_EMBED_LEN_MASK;
           RSTRING(result)->flags |= tmp_n << MRB_STR_EMBED_LEN_SHIFT;
-        } else {
+        }
+        else {
           RSTRING(result)->as.heap.len = blen;
         }
         if (flags&(FPREC|FWIDTH)) {
@@ -733,19 +771,11 @@ retry:
           if ((flags&FWIDTH) && (width > slen)) {
             width -= (int)slen;
             if (!(flags&FMINUS)) {
-              CHECK(width);
-              while (width--) {
-                buf[blen++] = ' ';
-              }
+              FILL(' ', width);
             }
-            CHECK(len);
-            memcpy(&buf[blen], RSTRING_PTR(str), len);
-            blen += len;
+            PUSH(RSTRING_PTR(str), len);
             if (flags&FMINUS) {
-              CHECK(width);
-              while (width--) {
-                buf[blen++] = ' ';
-              }
+              FILL(' ', width);
             }
             break;
           }
@@ -763,29 +793,14 @@ retry:
       case 'B':
       case 'u': {
         mrb_value val = GETARG();
-        char fbuf[32], nbuf[64], *s;
+        char nbuf[68], *s;
         const char *prefix = NULL;
         int sign = 0, dots = 0;
         char sc = 0;
-        mrb_int v = 0, org_v = 0;
+        mrb_int v = 0;
         int base;
         mrb_int len;
 
-        switch (*p) {
-          case 'd':
-          case 'i':
-          case 'u':
-            sign = 1; break;
-          case 'o':
-          case 'x':
-          case 'X':
-          case 'b':
-          case 'B':
-            if (flags&(FPLUS|FSPACE)) sign = 1;
-            break;
-          default:
-            break;
-        }
         if (flags & FSHARP) {
           switch (*p) {
             case 'o': prefix = "0"; break;
@@ -800,10 +815,6 @@ retry:
   bin_retry:
         switch (mrb_type(val)) {
           case MRB_TT_FLOAT:
-            if (FIXABLE(mrb_float(val))) {
-              val = mrb_fixnum_value((mrb_int)mrb_float(val));
-              goto bin_retry;
-            }
             val = mrb_flo_to_fixnum(mrb, val);
             if (mrb_fixnum_p(val)) goto bin_retry;
             break;
@@ -830,52 +841,53 @@ retry:
           case 'u':
           case 'd':
           case 'i':
+            sign = 1;
           default:
             base = 10; break;
         }
 
-        if (base == 2) {
-          org_v = v;
-          if (v < 0 && !sign) {
-            val = mrb_fix2binstr(mrb, mrb_fixnum_value(v), base);
-            dots = 1;
+        if (sign) {
+          if (v >= 0) {
+            if (flags & FPLUS) {
+              sc = '+';
+              width--;
+            }
+            else if (flags & FSPACE) {
+              sc = ' ';
+              width--;
+            }
           }
           else {
-            val = mrb_fixnum_to_str(mrb, mrb_fixnum_value(v), base);
-          }
-          v = mrb_fixnum(mrb_str_to_inum(mrb, val, 10, FALSE));
-        }
-        if (sign) {
-          char c = *p;
-          if (c == 'i') c = 'd'; /* %d and %i are identical */
-          if (base == 2) c = 'd';
-          if (v < 0) {
-            v = -v;
             sc = '-';
             width--;
           }
-          else if (flags & FPLUS) {
-            sc = '+';
-            width--;
-          }
-          else if (flags & FSPACE) {
-            sc = ' ';
-            width--;
-          }
-          snprintf(fbuf, sizeof(fbuf), "%%l%c", c);
-          snprintf(nbuf, sizeof(nbuf), fbuf, v);
+          mrb_assert(base == 10);
+          snprintf(nbuf, sizeof(nbuf), "%" MRB_PRId, v);
           s = nbuf;
+          if (v < 0) s++;       /* skip minus sign */
         }
         else {
-          char c = *p;
-          if (c == 'X') c = 'x';
-          if (base == 2) c = 'd';
           s = nbuf;
           if (v < 0) {
             dots = 1;
           }
-          snprintf(fbuf, sizeof(fbuf), "%%l%c", c);
-          snprintf(++s, sizeof(nbuf) - 1, fbuf, v);
+          switch (base) {
+          case 2:
+            if (v < 0) {
+              val = mrb_fix2binstr(mrb, mrb_fixnum_value(v), base);
+            }
+            else {
+              val = mrb_fixnum_to_str(mrb, mrb_fixnum_value(v), base);
+            }
+            strncpy(++s, RSTRING_PTR(val), sizeof(nbuf)-1);
+            break;
+          case 8:
+            snprintf(++s, sizeof(nbuf)-1, "%" MRB_PRIo, v);
+            break;
+          case 16:
+            snprintf(++s, sizeof(nbuf)-1, "%" MRB_PRIx, v);
+            break;
+          }
           if (v < 0) {
             char d;
 
@@ -899,11 +911,6 @@ retry:
           len = (mrb_int)size;
         }
 
-        if (dots) {
-          prec -= 2;
-          width -= 2;
-        }
-
         if (*p == 'X') {
           char *pp = s;
           int c;
@@ -949,11 +956,9 @@ retry:
           width -= prec;
         }
 
-        if (!(flags&FMINUS)) {
-          CHECK(width);
-          while (width-- > 0) {
-            buf[blen++] = ' ';
-          }
+        if (!(flags&FMINUS) && width > 0) {
+          FILL(' ', width);
+          width = 0;
         }
 
         if (sc) PUSH(&sc, 1);
@@ -962,26 +967,25 @@ retry:
           int plen = (int)strlen(prefix);
           PUSH(prefix, plen);
         }
-        CHECK(prec - len);
-        if (dots) PUSH("..", 2);
-
-        if (v < 0 || (base == 2 && org_v < 0)) {
-          char c = sign_bits(base, p);
-          while (len < prec--) {
-            buf[blen++] = c;
-          }
+        if (dots) {
+          prec -= 2;
+          width -= 2;
+          PUSH("..", 2);
         }
-        else if ((flags & (FMINUS|FPREC)) != FMINUS) {
-          char c = '0';
-          while (len < prec--) {
-            buf[blen++] = c;
+
+        if (prec > len) {
+          CHECK(prec - len);
+          if ((flags & (FMINUS|FPREC)) != FMINUS) {
+            char c = '0';
+            FILL(c, prec - len);
+          } else if (v < 0) {
+            char c = sign_bits(base, p);
+            FILL(c, prec - len);
           }
         }
-
         PUSH(s, len);
-        CHECK(width);
-        while (width-- > 0) {
-          buf[blen++] = ' ';
+        if (width > 0) {
+          FILL(' ', width);
         }
       }
       break;
@@ -1002,6 +1006,7 @@ retry:
         if (!isfinite(fval)) {
           const char *expr;
           const int elen = 3;
+          char sign = '\0';
 
           if (isnan(fval)) {
             expr = "NaN";
@@ -1010,32 +1015,29 @@ retry:
             expr = "Inf";
           }
           need = elen;
-          if ((!isnan(fval) && fval < 0.0) || (flags & FPLUS))
-            need++;
+          if (!isnan(fval) && fval < 0.0)
+            sign = '-';
+          else if (flags & (FPLUS|FSPACE))
+            sign = (flags & FPLUS) ? '+' : ' ';
+          if (sign)
+           ++need;
           if ((flags & FWIDTH) && need < width)
             need = width;
 
-          CHECK(need + 1);
-          snprintf(&buf[blen], need + 1, "%*s", need, "");
+          if (need < 0) {
+            mrb_raise(mrb, E_ARGUMENT_ERROR, "width too big");
+          }
+          FILL(' ', need);
           if (flags & FMINUS) {
-            if (!isnan(fval) && fval < 0.0)
-              buf[blen++] = '-';
-            else if (flags & FPLUS)
-              buf[blen++] = '+';
-            else if (flags & FSPACE)
-              blen++;
-            memcpy(&buf[blen], expr, elen);
+            if (sign)
+              buf[blen - need--] = sign;
+            memcpy(&buf[blen - need], expr, elen);
           }
           else {
-            if (!isnan(fval) && fval < 0.0)
-              buf[blen + need - elen - 1] = '-';
-            else if (flags & FPLUS)
-              buf[blen + need - elen - 1] = '+';
-            else if ((flags & FSPACE) && need > width)
-              blen++;
-            memcpy(&buf[blen + need - elen], expr, elen);
+            if (sign)
+              buf[blen - elen - 1] = sign;
+            memcpy(&buf[blen - elen], expr, elen);
           }
-          blen += strlen(&buf[blen]);
           break;
         }
 
@@ -1051,9 +1053,16 @@ retry:
         if ((flags&FWIDTH) && need < width)
           need = width;
         need += 20;
+        if (need <= 0) {
+          mrb_raise(mrb, E_ARGUMENT_ERROR,
+                    (width > prec ? "width too big" : "prec too big"));
+        }
 
         CHECK(need);
         n = snprintf(&buf[blen], need, fbuf, fval);
+        if (n < 0) {
+          mrb_raise(mrb, E_RUNTIME_ERROR, "formatting error");
+        }
         blen += n;
       }
       break;
index 7007df1..84dba9d 100644 (file)
@@ -5,4 +5,105 @@ assert('String#%') do
   assert_equal "one=1", "one=%d" % 1
   assert_equal "1 one 1.0", "%d %s %3.1f" % [ 1, "one", 1.01 ]
   assert_equal "123 < 456", "%{num} < %<str>s" % { num: 123, str: "456" }
+  assert_equal 15, ("%b" % (1<<14)).size
+end
+
+assert('String#% with inf') do
+  inf = Float::INFINITY
+
+  assert_equal "Inf", "%f" % inf
+  assert_equal "Inf", "%2f" % inf
+  assert_equal "Inf", "%3f" % inf
+  assert_equal " Inf", "%4f" % inf
+  assert_equal "  Inf", "%5f" % inf
+
+  assert_equal "+Inf", "%+f" % inf
+  assert_equal "+Inf", "%+2f" % inf
+  assert_equal "+Inf", "%+3f" % inf
+  assert_equal "+Inf", "%+4f" % inf
+  assert_equal " +Inf", "%+5f" % inf
+
+  assert_equal "Inf", "%-f" % inf
+  assert_equal "Inf", "%-2f" % inf
+  assert_equal "Inf", "%-3f" % inf
+  assert_equal "Inf ", "%-4f" % inf
+  assert_equal "Inf  ", "%-5f" % inf
+
+  assert_equal " Inf", "% f" % inf
+  assert_equal " Inf", "% 2f" % inf
+  assert_equal " Inf", "% 3f" % inf
+  assert_equal " Inf", "% 4f" % inf
+  assert_equal "  Inf", "% 5f" % inf
+end
+
+assert('String#% with nan') do
+  nan = Float::NAN
+
+  assert_equal "NaN", "%f" % nan
+  assert_equal "NaN", "%2f" % nan
+  assert_equal "NaN", "%3f" % nan
+  assert_equal " NaN", "%4f" % nan
+  assert_equal "  NaN", "%5f" % nan
+
+  assert_equal "+NaN", "%+f" % nan
+  assert_equal "+NaN", "%+2f" % nan
+  assert_equal "+NaN", "%+3f" % nan
+  assert_equal "+NaN", "%+4f" % nan
+  assert_equal " +NaN", "%+5f" % nan
+
+  assert_equal "NaN", "%-f" % nan
+  assert_equal "NaN", "%-2f" % nan
+  assert_equal "NaN", "%-3f" % nan
+  assert_equal "NaN ", "%-4f" % nan
+  assert_equal "NaN  ", "%-5f" % nan
+
+  assert_equal " NaN", "% f" % nan
+  assert_equal " NaN", "% 2f" % nan
+  assert_equal " NaN", "% 3f" % nan
+  assert_equal " NaN", "% 4f" % nan
+  assert_equal "  NaN", "% 5f" % nan
+end
+
+assert("String#% with invalid chr") do
+  begin
+    class Fixnum
+      alias_method :chr_, :chr if method_defined?(:chr)
+
+      def chr
+        nil
+      end
+    end
+
+    assert_raise TypeError do
+      "%c" % 0x80
+    end
+  ensure
+    class Fixnum
+      if method_defined?(:chr_)
+        alias_method :chr, :chr_
+        remove_method :chr_
+      end
+    end
+  end
+end
+
+assert("String#% %b") do
+  assert_equal("..10115", "%0b5" % -5)
+end
+
+assert("String#% %d") do
+  assert_equal("  10",   "%4d" % 10)
+  assert_equal("1000",   "%4d" % 1000)
+  assert_equal("100000", "%4d" % 100000)
+end
+
+assert("String#% invalid format") do
+  assert_raise ArgumentError do
+    "%?" % ""
+  end
+end
+
+assert("String#% invalid format shared substring") do
+  fmt = ("x"*30+"%!")[0...-1]
+  assert_equal fmt, sprintf(fmt, "")
 end
index f2df5a7..9812f2c 100644 (file)
@@ -2,4 +2,5 @@ MRuby::Gem::Specification.new('mruby-string-ext') do |spec|
   spec.license = 'MIT'
   spec.author  = 'mruby developers'
   spec.summary = 'String class extension'
+  spec.add_test_dependency 'mruby-enumerator', core: 'mruby-enumerator'
 end
index 4c8a2ce..c3a7eb3 100644 (file)
@@ -1,6 +1,25 @@
 class String
 
   ##
+  #  call-seq:
+  #     String.try_convert(obj) -> string or nil
+  #
+  # Try to convert <i>obj</i> into a String, using to_str method.
+  # Returns converted string or nil if <i>obj</i> cannot be converted
+  # for any reason.
+  #
+  #     String.try_convert("str")     #=> "str"
+  #     String.try_convert(/re/)      #=> nil
+  #
+  def self.try_convert(obj)
+    if obj.respond_to?(:to_str)
+      obj.to_str
+    else
+      nil
+    end
+  end
+
+  ##
   # call-seq:
   #    string.clear    ->  string
   #
@@ -26,7 +45,7 @@ class String
   def lstrip
     a = 0
     z = self.size - 1
-    a += 1 while " \f\n\r\t\v".include?(self[a]) and a <= z
+    a += 1 while a <= z and " \f\n\r\t\v".include?(self[a])
     (z >= 0) ? self[a..z] : ""
   end
 
@@ -43,7 +62,7 @@ class String
   def rstrip
     a = 0
     z = self.size - 1
-    z -= 1 while " \f\n\r\t\v\0".include?(self[z]) and a <= z
+    z -= 1 while a <= z and " \f\n\r\t\v\0".include?(self[z])
     (z >= 0) ? self[a..z] : ""
   end
 
@@ -59,8 +78,8 @@ class String
   def strip
     a = 0
     z = self.size - 1
-    a += 1 while " \f\n\r\t\v".include?(self[a]) and a <= z
-    z -= 1 while " \f\n\r\t\v\0".include?(self[z]) and a <= z
+    a += 1 while a <= z and " \f\n\r\t\v".include?(self[a])
+    z -= 1 while a <= z and " \f\n\r\t\v\0".include?(self[z])
     (z >= 0) ? self[a..z] : ""
   end
 
@@ -76,6 +95,7 @@ class String
   #    "hello".lstrip!      #=> nil
   #
   def lstrip!
+    raise RuntimeError, "can't modify frozen String" if frozen?
     s = self.lstrip
     (s == self) ? nil : self.replace(s)
   end
@@ -92,6 +112,7 @@ class String
   #    "hello".rstrip!      #=> nil
   #
   def rstrip!
+    raise RuntimeError, "can't modify frozen String" if frozen?
     s = self.rstrip
     (s == self) ? nil : self.replace(s)
   end
@@ -104,6 +125,7 @@ class String
   #  <code>nil</code> if <i>str</i> was not altered.
   #
   def strip!
+    raise RuntimeError, "can't modify frozen String" if frozen?
     s = self.strip
     (s == self) ? nil : self.replace(s)
   end
@@ -164,6 +186,7 @@ class String
   #    string                  #=> "thsa sting"
   #
   def slice!(arg1, arg2=nil)
+    raise RuntimeError, "can't modify frozen String" if frozen?
     raise "wrong number of arguments (for 1..2)" if arg1.nil? && arg2.nil?
 
     if !arg1.nil? && !arg2.nil?
@@ -188,7 +211,7 @@ class String
       else
         idx = arg1
         idx += self.size if arg1 < 0
-        validated = true if idx >=0 && arg1 < self.size   
+        validated = true if idx >=0 && arg1 < self.size
       end
       if validated
         str = self[arg1]
@@ -235,14 +258,13 @@ class String
   #     "abcd".insert(-1, 'X')   #=> "abcdX"
   #
   def insert(idx, str)
-    pos = idx.to_i
-    pos += self.size + 1 if pos < 0
-
-    raise IndexError, "index #{idx.to_i} out of string" if pos < 0 || pos > self.size
-
-    return self + str if pos == -1
-    return str + self if pos == 0
-    return self[0..pos - 1] + str + self[pos..-1]
+    if idx == -1
+      return self << str
+    elsif idx < 0
+      idx += 1
+    end
+    self[idx, 0] = str
+    self
   end
 
   ##
@@ -257,63 +279,35 @@ class String
   #     "hello".ljust(20)           #=> "hello               "
   #     "hello".ljust(20, '1234')   #=> "hello123412341234123"
   def ljust(idx, padstr = ' ')
-    if idx <= self.size
-      return self
-    end
-    newstr = self.dup
-    newstr << padstr
-    while newstr.size <= idx
-      newstr << padstr
-    end
-    return newstr.slice(0,idx)
+    raise ArgumentError, 'zero width padding' if padstr == ''
+    return self if idx <= self.size
+    pad_repetitions = (idx / padstr.length).ceil
+    padding = (padstr * pad_repetitions)[0...(idx - self.length)]
+    self + padding
   end
 
-  #     str.upto(other_str, exclusive=false) {|s| block }   -> str
-  #     str.upto(other_str, exclusive=false)                -> an_enumerator
-  #
-  #  Iterates through successive values, starting at <i>str</i> and
-  #  ending at <i>other_str</i> inclusive, passing each value in turn to
-  #  the block. The <code>String#succ</code> method is used to generate
-  #  each value.  If optional second argument exclusive is omitted or is false,
-  #  the last value will be included; otherwise it will be excluded.
-  #
-  #  If no block is given, an enumerator is returned instead.
-  #
-  #     "a8".upto("b6") {|s| print s, ' ' }
-  #     for s in "a8".."b6"
-  #       print s, ' '
-  #     end
-  #
-  #  <em>produces:</em>
-  #
-  #     a8 a9 b0 b1 b2 b3 b4 b5 b6
-  #     a8 a9 b0 b1 b2 b3 b4 b5 b6
-  #
-  #  If <i>str</i> and <i>other_str</i> contains only ascii numeric characters,
-  #  both are recognized as decimal numbers. In addition, the width of
-  #  string (e.g. leading zeros) is handled appropriately.
+  ##
+  #  call-seq:
+  #     str.rjust(integer, padstr=' ')   -> new_str
   #
-  #     "9".upto("11").to_a   #=> ["9", "10", "11"]
-  #     "25".upto("5").to_a   #=> []
-  #     "07".upto("11").to_a  #=> ["07", "08", "09", "10", "11"]
+  #  If <i>integer</i> is greater than the length of <i>str</i>, returns a new
+  #  <code>String</code> of length <i>integer</i> with <i>str</i> right justified
+  #  and padded with <i>padstr</i>; otherwise, returns <i>str</i>.
   #
-  def upto(other_str, excl=false, &block)
-    return to_enum :upto, other_str, excl unless block
-
-    str = self
-    n = self.<=>other_str
-    return self if n > 0 || (self == other_str && excl)
-    while true
-      block.call(str)
-      return self if !excl && str == other_str
-      str = str.succ
-      return self if excl && str == other_str
-    end
+  #     "hello".rjust(4)            #=> "hello"
+  #     "hello".rjust(20)           #=> "               hello"
+  #     "hello".rjust(20, '1234')   #=> "123412341234123hello"
+  def rjust(idx, padstr = ' ')
+    raise ArgumentError, 'zero width padding' if padstr == ''
+    return self if idx <= self.size
+    pad_repetitions = (idx / padstr.length).ceil
+    padding = (padstr * pad_repetitions)[0...(idx - self.length)]
+    padding + self
   end
 
   def chars(&block)
     if block_given?
-      self.split('').map do |i|
+      self.split('').each do |i|
         block.call(i)
       end
       self
@@ -321,13 +315,21 @@ class String
       self.split('')
     end
   end
-  alias each_char chars
+
+  def each_char(&block)
+    return to_enum :each_char unless block
+
+    split('').each do |i|
+      block.call(i)
+    end
+    self
+  end
 
   def codepoints(&block)
     len = self.size
 
     if block_given?
-      self.split('').map do|x|
+      self.split('').each do|x|
         block.call(x.ord)
       end
       self
@@ -336,4 +338,18 @@ class String
     end
   end
   alias each_codepoint codepoints
+
+  ##
+  # call-seq:
+  #    str.prepend(other_str)  -> str
+  #
+  # Prepend---Prepend the given string to <i>str</i>.
+  #
+  #    a = "world"
+  #    a.prepend("hello ") #=> "hello world"
+  #    a                   #=> "hello world"
+  def prepend(arg)
+    self[0, 0] = arg
+    self
+  end
 end
index 0afc533..0d834b6 100644 (file)
@@ -1,9 +1,9 @@
 #include <string.h>
-#include "mruby.h"
-#include "mruby/array.h"
-#include "mruby/class.h"
-#include "mruby/string.h"
-#include "mruby/range.h"
+#include <mruby.h>
+#include <mruby/array.h>
+#include <mruby/class.h>
+#include <mruby/string.h>
+#include <mruby/range.h>
 
 static mrb_value
 mrb_str_getbyte(mrb_state *mrb, mrb_value str)
@@ -23,10 +23,11 @@ static mrb_value
 mrb_str_setbyte(mrb_state *mrb, mrb_value str)
 {
   mrb_int pos, byte;
-  long len = RSTRING_LEN(str);
+  long len;
 
   mrb_get_args(mrb, "ii", &pos, &byte);
 
+  len = RSTRING_LEN(str);
   if (pos < -len || len <= pos)
     mrb_raisef(mrb, E_INDEX_ERROR, "index %S is out of array", mrb_fixnum_value(pos));
   if (pos < 0)
@@ -55,8 +56,14 @@ mrb_str_byteslice(mrb_state *mrb, mrb_value str)
       mrb_int beg;
 
       len = RSTRING_LEN(str);
-      if (mrb_range_beg_len(mrb, a1, &beg, &len, len)) {
+      switch (mrb_range_beg_len(mrb, a1, &beg, &len, len, TRUE)) {
+      case 0:                   /* not range */
+        break;
+      case 1:                   /* range */
         return mrb_str_substr(mrb, str, beg, len);
+      case 2:                   /* out of range */
+        mrb_raisef(mrb, E_RANGE_ERROR, "%S out of range", a1);
+        break;
       }
       return mrb_nil_value();
     }
@@ -127,6 +134,8 @@ mrb_str_swapcase(mrb_state *mrb, mrb_value self)
   return str;
 }
 
+static mrb_value mrb_fixnum_chr(mrb_state *mrb, mrb_value num);
+
 /*
  *  call-seq:
  *     str << integer       -> str
@@ -146,7 +155,12 @@ static mrb_value
 mrb_str_concat2(mrb_state *mrb, mrb_value self)
 {
   mrb_value str;
-  mrb_get_args(mrb, "S", &str);
+
+  mrb_get_args(mrb, "o", &str);
+  if (mrb_fixnum_p(str))
+    str = mrb_fixnum_chr(mrb, str);
+  else
+    str = mrb_string_type(mrb, str);
   mrb_str_concat(mrb, self, str);
   return self;
 }
@@ -307,13 +321,14 @@ mrb_str_lines(mrb_state *mrb, mrb_value self)
   int ai;
   mrb_int len;
   mrb_value arg;
-  char *p = RSTRING_PTR(self), *t;
-  char *e = p + RSTRING_LEN(self);
+  char *b = RSTRING_PTR(self);
+  char *p = b, *t;
+  char *e = b + RSTRING_LEN(self);
 
   mrb_get_args(mrb, "&", &blk);
 
   result = mrb_ary_new(mrb);
-
+  ai = mrb_gc_arena_save(mrb);
   if (!mrb_nil_p(blk)) {
     while (p < e) {
       t = p;
@@ -322,11 +337,17 @@ mrb_str_lines(mrb_state *mrb, mrb_value self)
       len = (mrb_int) (p - t);
       arg = mrb_str_new(mrb, t, len);
       mrb_yield_argv(mrb, blk, 1, &arg);
+      mrb_gc_arena_restore(mrb, ai);
+      if (b != RSTRING_PTR(self)) {
+        ptrdiff_t diff = p - b;
+        b = RSTRING_PTR(self);
+        p = b + diff;
+      }
+      e = b + RSTRING_LEN(self);
     }
     return self;
   }
   while (p < e) {
-    ai = mrb_gc_arena_save(mrb);
     t = p;
     while (p < e && *p != '\n') p++;
     if (*p == '\n') p++;
@@ -353,7 +374,7 @@ mrb_str_succ_bang(mrb_state *mrb, mrb_value self)
   unsigned char *p, *e, *b, *t;
   const char *prepend;
   struct RString *s = mrb_str_ptr(self);
-  size_t l;
+  mrb_int l;
 
   if (RSTRING_LEN(self) == 0)
     return self;
@@ -373,7 +394,8 @@ mrb_str_succ_bang(mrb_state *mrb, mrb_value self)
   if (e < b) {
     e = p + l - 1;
     result = mrb_str_new_lit(mrb, "");
-  } else {
+  }
+  else {
     // find leading letter of the ascii/number
     b = e;
     while (b > p) {
@@ -391,7 +413,8 @@ mrb_str_succ_bang(mrb_state *mrb, mrb_value self)
       if (*e == 0xff) {
         mrb_str_cat_lit(mrb, result, "\x01");
         (*e) = 0;
-      } else
+      }
+      else
         (*e)++;
       break;
     }
@@ -399,13 +422,16 @@ mrb_str_succ_bang(mrb_state *mrb, mrb_value self)
     if (*e == '9') {
       if (e == b) prepend = "1";
       *e = '0';
-    } else if (*e == 'z') {
+    }
+    else if (*e == 'z') {
       if (e == b) prepend = "a";
       *e = 'a';
-    } else if (*e == 'Z') {
+    }
+    else if (*e == 'Z') {
       if (e == b) prepend = "A";
       *e = 'A';
-    } else {
+    }
+    else {
       (*e)++;
       break;
     }
@@ -429,44 +455,6 @@ mrb_str_succ(mrb_state *mrb, mrb_value self)
   return str;
 }
 
-/*
- *  call-seq:
- *     str.prepend(other_str)  -> str
- *
- *  Prepend---Prepend the given string to <i>str</i>.
- *
- *     a = "world"
- *     a.prepend("hello ") #=> "hello world"
- *     a                   #=> "hello world"
- */
-static mrb_value
-mrb_str_prepend(mrb_state *mrb, mrb_value self)
-{
-  struct RString *s1 = mrb_str_ptr(self), *s2, *temp_s;
-  mrb_int len;
-  mrb_value other, temp_str;
-
-  mrb_get_args(mrb, "S", &other);
-
-  mrb_str_modify(mrb, s1);
-  if (!mrb_string_p(other)) {
-    other = mrb_str_to_str(mrb, other);
-  }
-  s2 = mrb_str_ptr(other);
-  len = RSTR_LEN(s1) + RSTR_LEN(s2);
-  temp_str = mrb_str_new(mrb, NULL, RSTR_LEN(s1));
-  temp_s = mrb_str_ptr(temp_str);
-  memcpy(RSTR_PTR(temp_s), RSTR_PTR(s1), RSTR_LEN(s1));
-  if (RSTRING_CAPA(self) < len) {
-    mrb_str_resize(mrb, self, len);
-  }
-  memcpy(RSTR_PTR(s1), RSTR_PTR(s2), RSTR_LEN(s2));
-  memcpy(RSTR_PTR(s1) + RSTR_LEN(s2), RSTR_PTR(temp_s), RSTR_LEN(temp_s));
-  RSTR_SET_LEN(s1, len);
-  RSTR_PTR(s1)[len] = '\0';
-  return self;
-}
-
 #ifdef MRB_UTF8_STRING
 static const char utf8len_codepage_zero[256] =
 {
@@ -529,10 +517,139 @@ mrb_str_ord(mrb_state* mrb, mrb_value str)
 {
   if (RSTRING_LEN(str) == 0)
     mrb_raise(mrb, E_ARGUMENT_ERROR, "empty string");
-  return mrb_fixnum_value(RSTRING_PTR(str)[0]);
+  return mrb_fixnum_value((unsigned char)RSTRING_PTR(str)[0]);
 }
 #endif
 
+static mrb_bool
+all_digits_p(const char *s, mrb_int len)
+{
+  while (len-- > 0) {
+    if (!ISDIGIT(*s)) return FALSE;
+    s++;
+  }
+  return TRUE;
+}
+
+/*
+ *  call-seq:
+ *     str.upto(other_str, exclusive=false) {|s| block }   -> str
+ *     str.upto(other_str, exclusive=false)                -> an_enumerator
+ *
+ *  Iterates through successive values, starting at <i>str</i> and
+ *  ending at <i>other_str</i> inclusive, passing each value in turn to
+ *  the block. The <code>String#succ</code> method is used to generate
+ *  each value.  If optional second argument exclusive is omitted or is false,
+ *  the last value will be included; otherwise it will be excluded.
+ *
+ *  If no block is given, an enumerator is returned instead.
+ *
+ *     "a8".upto("b6") {|s| print s, ' ' }
+ *     for s in "a8".."b6"
+ *       print s, ' '
+ *     end
+ *
+ *  <em>produces:</em>
+ *
+ *     a8 a9 b0 b1 b2 b3 b4 b5 b6
+ *     a8 a9 b0 b1 b2 b3 b4 b5 b6
+ *
+ *  If <i>str</i> and <i>other_str</i> contains only ascii numeric characters,
+ *  both are recognized as decimal numbers. In addition, the width of
+ *  string (e.g. leading zeros) is handled appropriately.
+ *
+ *     "9".upto("11").to_a   #=> ["9", "10", "11"]
+ *     "25".upto("5").to_a   #=> []
+ *     "07".upto("11").to_a  #=> ["07", "08", "09", "10", "11"]
+ */
+static mrb_value
+mrb_str_upto(mrb_state *mrb, mrb_value beg)
+{
+  mrb_value end;
+  mrb_value exclusive = mrb_false_value();
+  mrb_value block = mrb_nil_value();
+  mrb_value current, after_end;
+  mrb_int n;
+  mrb_bool excl;
+
+  mrb_get_args(mrb, "o|o&", &end, &exclusive, &block);
+
+  if (mrb_nil_p(block)) {
+    return mrb_funcall(mrb, beg, "to_enum", 3, mrb_symbol_value(mrb_intern_lit(mrb, "upto")), end, exclusive);
+  }
+  end = mrb_string_type(mrb, end);
+  excl = mrb_test(exclusive);
+
+  /* single character */
+  if (RSTRING_LEN(beg) == 1 && RSTRING_LEN(end) == 1 &&
+  ISASCII(RSTRING_PTR(beg)[0]) && ISASCII(RSTRING_PTR(end)[0])) {
+    char c = RSTRING_PTR(beg)[0];
+    char e = RSTRING_PTR(end)[0];
+    int ai = mrb_gc_arena_save(mrb);
+
+    if (c > e || (excl && c == e)) return beg;
+    for (;;) {
+      mrb_yield(mrb, block, mrb_str_new(mrb, &c, 1));
+      mrb_gc_arena_restore(mrb, ai);
+      if (!excl && c == e) break;
+      c++;
+      if (excl && c == e) break;
+    }
+    return beg;
+  }
+  /* both edges are all digits */
+  if (ISDIGIT(RSTRING_PTR(beg)[0]) && ISDIGIT(RSTRING_PTR(end)[0]) &&
+      all_digits_p(RSTRING_PTR(beg), RSTRING_LEN(beg)) &&
+      all_digits_p(RSTRING_PTR(end), RSTRING_LEN(end))) {
+    mrb_int min_width = RSTRING_LEN(beg);
+    mrb_int bi = mrb_int(mrb, mrb_str_to_inum(mrb, beg, 10, FALSE));
+    mrb_int ei = mrb_int(mrb, mrb_str_to_inum(mrb, end, 10, FALSE));
+    int ai = mrb_gc_arena_save(mrb);
+
+    while (bi <= ei) {
+      mrb_value ns, str;
+
+      if (excl && bi == ei) break;
+      ns = mrb_format(mrb, "%S", mrb_fixnum_value(bi));
+      if (min_width > RSTRING_LEN(ns)) {
+        str = mrb_str_new(mrb, NULL, min_width);
+        memset(RSTRING_PTR(str), '0', min_width-RSTRING_LEN(ns));
+        memcpy(RSTRING_PTR(str)+min_width-RSTRING_LEN(ns),
+               RSTRING_PTR(ns), RSTRING_LEN(ns));
+      }
+      else {
+        str = ns;
+      }
+      mrb_yield(mrb, block, str);
+      mrb_gc_arena_restore(mrb, ai);
+      bi++;
+    }
+
+    return beg;
+  }
+  /* normal case */
+  n = mrb_int(mrb, mrb_funcall(mrb, beg, "<=>", 1, end));
+  if (n > 0 || (excl && n == 0)) return beg;
+
+  after_end = mrb_funcall(mrb, end, "succ", 0);
+  current = mrb_str_dup(mrb, beg);
+  while (!mrb_str_equal(mrb, current, after_end)) {
+    int ai = mrb_gc_arena_save(mrb);
+    mrb_value next = mrb_nil_value();
+    if (excl || !mrb_str_equal(mrb, current, end))
+      next = mrb_funcall(mrb, current, "succ", 0);
+    mrb_yield(mrb, block, current);
+    if (mrb_nil_p(next)) break;
+    current = mrb_str_to_str(mrb, next);
+    if (excl && mrb_str_equal(mrb, current, end)) break;
+    if (RSTRING_LEN(current) > RSTRING_LEN(end) || RSTRING_LEN(current) == 0)
+      break;
+    mrb_gc_arena_restore(mrb, ai);
+  }
+
+  return beg;
+}
+
 void
 mrb_mruby_string_ext_gem_init(mrb_state* mrb)
 {
@@ -554,10 +671,10 @@ mrb_mruby_string_ext_gem_init(mrb_state* mrb)
   mrb_define_method(mrb, s, "lines",           mrb_str_lines,           MRB_ARGS_NONE());
   mrb_define_method(mrb, s, "succ",            mrb_str_succ,            MRB_ARGS_NONE());
   mrb_define_method(mrb, s, "succ!",           mrb_str_succ_bang,       MRB_ARGS_NONE());
-  mrb_define_method(mrb, s, "prepend",         mrb_str_prepend,         MRB_ARGS_REQ(1));
   mrb_alias_method(mrb, s, mrb_intern_lit(mrb, "next"), mrb_intern_lit(mrb, "succ"));
   mrb_alias_method(mrb, s, mrb_intern_lit(mrb, "next!"), mrb_intern_lit(mrb, "succ!"));
   mrb_define_method(mrb, s, "ord", mrb_str_ord, MRB_ARGS_NONE());
+  mrb_define_method(mrb, s, "upto", mrb_str_upto, MRB_ARGS_ANY());
 
   mrb_define_method(mrb, mrb->fixnum_class, "chr", mrb_fixnum_chr, MRB_ARGS_NONE());
 }
index 8324a16..2a568c7 100644 (file)
@@ -3,6 +3,13 @@
 
 UTF8STRING = ("\343\201\202".size == 1)
 
+assert('String.try_convert') do
+  assert_nil String.try_convert(nil)
+  assert_nil String.try_convert(:foo)
+  assert_equal "", String.try_convert("")
+  assert_equal "1,2,3", String.try_convert("1,2,3")
+end
+
 assert('String#getbyte') do
   str1 = "hello"
   bytes1 = [104, 101, 108, 108, 111]
@@ -23,6 +30,18 @@ assert('String#setbyte') do
   assert_equal("Hello", str1)
 end
 
+assert("String#setbyte raises IndexError if arg conversion resizes String") do
+  $s = "01234\n"
+  class Tmp
+      def to_i
+          $s.chomp! ''
+          95
+      end
+  end
+  tmp = Tmp.new
+  assert_raise(IndexError) { $s.setbyte(5, tmp) }
+end
+
 assert('String#byteslice') do
   str1 = "hello"
   assert_equal("e", str1.byteslice(1))
@@ -103,12 +122,16 @@ assert('String#swapcase!') do
 end
 
 assert('String#concat') do
-  s = "Hello "
-  s.concat "World!"
-  t = "Hello "
-  t << "World!"
-  assert_equal "Hello World!", t
-  assert_equal "Hello World!", s
+  assert_equal "Hello World!", "Hello " << "World" << 33
+  assert_equal "Hello World!", "Hello ".concat("World").concat(33)
+
+  o = Object.new
+  def o.to_str
+    "to_str"
+  end
+  assert_equal "hi to_str", "hi " << o
+
+  assert_raise(TypeError) { "".concat(Object.new) }
 end
 
 assert('String#casecmp') do
@@ -399,6 +422,10 @@ assert('String#insert') do
   assert_equal "abcdX", "abcd".insert(-1, 'X')
   assert_raise(IndexError) { "abcd".insert(5, 'X') }
   assert_raise(IndexError) { "abcd".insert(-6, 'X') }
+
+  a = "abcd"
+  a.insert(0, 'X')
+  assert_equal "Xabcd", a
 end
 
 assert('String#prepend') do
@@ -410,11 +437,75 @@ end
 assert('String#ljust') do
   assert_equal "hello", "hello".ljust(4)
   assert_equal "hello               ", "hello".ljust(20)
+  assert_equal 20, "hello".ljust(20).length
   assert_equal "hello123412341234123", "hello".ljust(20, '1234')
   assert_equal "hello", "hello".ljust(-3)
 end
 
+assert('String#rjust') do
+  assert_equal "hello", "hello".rjust(4)
+  assert_equal "               hello", "hello".rjust(20)
+  assert_equal 20, "hello".rjust(20).length
+  assert_equal "123412341234123hello", "hello".rjust(20, '1234')
+  assert_equal "hello", "hello".rjust(-3)
+end
+
+if UTF8STRING
+  assert('String#ljust with UTF8') do
+    assert_equal "helloん              ", "helloん".ljust(20)
+    assert_equal "helloó                            ", "helloó".ljust(34)
+    assert_equal 34, "helloó".ljust(34).length
+    assert_equal "helloんんんんんんんんんんんんんん", "hello".ljust(19, 'ん')
+    assert_equal "helloんんんんんんんんんんんんんんん", "hello".ljust(20, 'ん')
+  end
+
+  assert('String#rjust with UTF8') do
+    assert_equal "              helloん", "helloん".rjust(20)
+    assert_equal "                            helloó", "helloó".rjust(34)
+    # assert_equal 34, "helloó".rjust(34).length
+    assert_equal "んんんんんんんんんんんんんんhello", "hello".rjust(19, 'ん')
+    assert_equal "んんんんんんんんんんんんんんんhello", "hello".rjust(20, 'ん')
+  end
+
+  assert('UTF8 byte counting') do
+    ret = '                                  '
+    ret[-6..-1] = "helloó"
+    assert_equal 34, ret.length
+  end
+end
+
+assert('String#ljust should not change string') do
+  a = "hello"
+  a.ljust(20)
+  assert_equal "hello", a
+end
+
+assert('String#rjust should not change string') do
+  a = "hello"
+  a.rjust(20)
+  assert_equal "hello", a
+end
+
+assert('String#ljust should raise on zero width padding') do
+  assert_raise(ArgumentError) { "foo".ljust(10, '') }
+end
+
+assert('String#rjust should raise on zero width padding') do
+  assert_raise(ArgumentError) { "foo".rjust(10, '') }
+end
+
 assert('String#upto') do
+  assert_equal %w(a8 a9 b0 b1 b2 b3 b4 b5 b6), "a8".upto("b6").to_a
+  assert_equal ["9", "10", "11"], "9".upto("11").to_a
+  assert_equal [], "25".upto("5").to_a
+  assert_equal ["07", "08", "09", "10", "11"], "07".upto("11").to_a
+
+if UTF8STRING
+  assert_equal ["あ", "ぃ", "い", "ぅ", "う", "ぇ", "え", "ぉ", "お"], "あ".upto("お").to_a
+end
+
+  assert_equal ["9", ":", ";", "<", "=", ">", "?", "@", "A"], "9".upto("A").to_a
+
   a     = "aa"
   start = "aa"
   count = 0
@@ -474,11 +565,17 @@ assert('String#upto') do
     count += 1
   })
   assert_equal(2, count)
+
+  assert_raise(TypeError) { "a".upto(:c) {} }
 end
 
 assert('String#ord') do
   got = "hello!".split('').map {|x| x.ord}
   expect = [104, 101, 108, 108, 111, 33]
+  unless UTF8STRING
+    got << "\xff".ord
+    expect << 0xff
+  end
   assert_equal expect, got
 end
 
index 1445392..7cf3dd3 100644 (file)
@@ -82,5 +82,22 @@ if Object.const_defined?(:Struct)
     #
     alias to_s inspect
   end
+
+  ##
+  # call-seq:
+  #   hsh.dig(key,...)                 -> object
+  #
+  # Extracts the nested value specified by the sequence of <i>key</i>
+  # objects by calling +dig+ at each step, returning +nil+ if any
+  # intermediate step is +nil+.
+  #
+  def dig(idx,*args)
+    n = self[idx]
+    if args.size > 0
+      n&.dig(*args)
+    else
+      n
+    end
+  end
 end
 
index 342e3eb..67762a9 100644 (file)
@@ -5,16 +5,16 @@
 */
 
 #include <string.h>
-#include "mruby.h"
-#include "mruby/array.h"
-#include "mruby/string.h"
-#include "mruby/class.h"
-#include "mruby/variable.h"
-#include "mruby/hash.h"
-#include "mruby/range.h"
+#include <mruby.h>
+#include <mruby/array.h>
+#include <mruby/string.h>
+#include <mruby/class.h>
+#include <mruby/variable.h>
+#include <mruby/hash.h>
+#include <mruby/range.h>
 
-#define RSTRUCT_LEN(st) mrb_ary_ptr(st)->len
-#define RSTRUCT_PTR(st) mrb_ary_ptr(st)->ptr
+#define RSTRUCT_LEN(st) RARRAY_LEN(st)
+#define RSTRUCT_PTR(st) RARRAY_PTR(st)
 
 static struct RClass *
 struct_class(mrb_state *mrb)
@@ -61,9 +61,14 @@ struct_members(mrb_state *mrb, mrb_value s)
     mrb_raise(mrb, E_TYPE_ERROR, "corrupted struct");
   }
   if (RSTRUCT_LEN(s) != RARRAY_LEN(members)) {
-    mrb_raisef(mrb, E_TYPE_ERROR,
-               "struct size differs (%S required %S given)",
-               mrb_fixnum_value(RARRAY_LEN(members)), mrb_fixnum_value(RSTRUCT_LEN(s)));
+    if (RSTRUCT_LEN(s) == 0) {  /* probably uninitialized */
+      mrb_ary_resize(mrb, s, RARRAY_LEN(members));
+    }
+    else {
+      mrb_raisef(mrb, E_TYPE_ERROR,
+                 "struct size differs (%S required %S given)",
+                 mrb_fixnum_value(RARRAY_LEN(members)), mrb_fixnum_value(RSTRUCT_LEN(s)));
+    }
   }
   return members;
 }
@@ -79,6 +84,16 @@ mrb_struct_s_members_m(mrb_state *mrb, mrb_value klass)
   return ary;
 }
 
+static void
+mrb_struct_modify(mrb_state *mrb, mrb_value strct)
+{
+  if (MRB_FROZEN_P(mrb_basic_ptr(strct))) {
+    mrb_raise(mrb, E_RUNTIME_ERROR, "can't modify frozen struct");
+  }
+
+  mrb_write_barrier(mrb, mrb_basic_ptr(strct));
+}
+
 /* 15.2.18.4.6  */
 /*
  *  call-seq:
@@ -98,59 +113,13 @@ mrb_struct_members(mrb_state *mrb, mrb_value obj)
   return mrb_struct_s_members_m(mrb, mrb_obj_value(mrb_obj_class(mrb, obj)));
 }
 
-static mrb_value
-mrb_struct_getmember(mrb_state *mrb, mrb_value obj, mrb_sym id)
-{
-  mrb_value members, slot, *ptr;
-  const mrb_value *ptr_members;
-  mrb_int i, len;
-
-  ptr = RSTRUCT_PTR(obj);
-  members = struct_members(mrb, obj);
-  ptr_members = RARRAY_PTR(members);
-  slot = mrb_symbol_value(id);
-  len = RARRAY_LEN(members);
-  for (i=0; i<len; i++) {
-    if (mrb_obj_equal(mrb, ptr_members[i], slot)) {
-      return ptr[i];
-    }
-  }
-  mrb_raisef(mrb, E_INDEX_ERROR, "'%S' is not a struct member", mrb_sym2str(mrb, id));
-  return mrb_nil_value();       /* not reached */
-}
+static mrb_value struct_aref_sym(mrb_state *mrb, mrb_value obj, mrb_sym id);
 
 static mrb_value
 mrb_struct_ref(mrb_state *mrb, mrb_value obj)
 {
-  return mrb_struct_getmember(mrb, obj, mrb->c->ci->mid);
-}
-
-static mrb_value mrb_struct_ref0(mrb_state* mrb, mrb_value obj) {return RSTRUCT_PTR(obj)[0];}
-static mrb_value mrb_struct_ref1(mrb_state* mrb, mrb_value obj) {return RSTRUCT_PTR(obj)[1];}
-static mrb_value mrb_struct_ref2(mrb_state* mrb, mrb_value obj) {return RSTRUCT_PTR(obj)[2];}
-static mrb_value mrb_struct_ref3(mrb_state* mrb, mrb_value obj) {return RSTRUCT_PTR(obj)[3];}
-static mrb_value mrb_struct_ref4(mrb_state* mrb, mrb_value obj) {return RSTRUCT_PTR(obj)[4];}
-static mrb_value mrb_struct_ref5(mrb_state* mrb, mrb_value obj) {return RSTRUCT_PTR(obj)[5];}
-static mrb_value mrb_struct_ref6(mrb_state* mrb, mrb_value obj) {return RSTRUCT_PTR(obj)[6];}
-static mrb_value mrb_struct_ref7(mrb_state* mrb, mrb_value obj) {return RSTRUCT_PTR(obj)[7];}
-static mrb_value mrb_struct_ref8(mrb_state* mrb, mrb_value obj) {return RSTRUCT_PTR(obj)[8];}
-static mrb_value mrb_struct_ref9(mrb_state* mrb, mrb_value obj) {return RSTRUCT_PTR(obj)[9];}
-
-#define numberof(array) (int)(sizeof(array) / sizeof((array)[0]))
-#define N_REF_FUNC numberof(ref_func)
-
-static const mrb_func_t ref_func[] = {
-  mrb_struct_ref0,
-  mrb_struct_ref1,
-  mrb_struct_ref2,
-  mrb_struct_ref3,
-  mrb_struct_ref4,
-  mrb_struct_ref5,
-  mrb_struct_ref6,
-  mrb_struct_ref7,
-  mrb_struct_ref8,
-  mrb_struct_ref9,
-};
+  return struct_aref_sym(mrb, obj, mrb->c->ci->mid);
+}
 
 static mrb_sym
 mrb_id_attrset(mrb_state *mrb, mrb_sym id)
@@ -171,40 +140,24 @@ mrb_id_attrset(mrb_state *mrb, mrb_sym id)
   return mid;
 }
 
+static mrb_value mrb_struct_aset_sym(mrb_state *mrb, mrb_value s, mrb_sym id, mrb_value val);
+
 static mrb_value
-mrb_struct_set(mrb_state *mrb, mrb_value obj, mrb_value val)
+mrb_struct_set_m(mrb_state *mrb, mrb_value obj)
 {
+  mrb_value val;
+
   const char *name;
-  mrb_int i, len, slen;
+  mrb_int slen;
   mrb_sym mid;
-  mrb_value members, slot, *ptr;
-  const mrb_value *ptr_members;
+
+  mrb_get_args(mrb, "o", &val);
 
   /* get base id */
   name = mrb_sym2name_len(mrb, mrb->c->ci->mid, &slen);
   mid = mrb_intern(mrb, name, slen-1); /* omit last "=" */
 
-  members = struct_members(mrb, obj);
-  ptr_members = RARRAY_PTR(members);
-  len = RARRAY_LEN(members);
-  ptr = RSTRUCT_PTR(obj);
-  for (i=0; i<len; i++) {
-    slot = ptr_members[i];
-    if (mrb_symbol(slot) == mid) {
-      return ptr[i] = val;
-    }
-  }
-  mrb_raisef(mrb, E_INDEX_ERROR, "'%S' is not a struct member", mrb_sym2str(mrb, mid));
-  return mrb_nil_value();       /* not reached */
-}
-
-static mrb_value
-mrb_struct_set_m(mrb_state *mrb, mrb_value obj)
-{
-  mrb_value val;
-
-  mrb_get_args(mrb, "o", &val);
-  return mrb_struct_set(mrb, obj, val);
+  return mrb_struct_aset_sym(mrb, obj, mid, val);
 }
 
 static mrb_bool
@@ -234,12 +187,7 @@ make_struct_define_accessors(mrb_state *mrb, mrb_value members, struct RClass *c
     const char *name = mrb_sym2name_len(mrb, id, NULL);
 
     if (is_local_id(mrb, name) || is_const_id(mrb, name)) {
-      if (i < N_REF_FUNC) {
-        mrb_define_method_id(mrb, c, id, ref_func[i], MRB_ARGS_NONE());
-      }
-      else {
-        mrb_define_method_id(mrb, c, id, mrb_struct_ref, MRB_ARGS_NONE());
-      }
+      mrb_define_method_id(mrb, c, id, mrb_struct_ref, MRB_ARGS_NONE());
       mrb_define_method_id(mrb, c, mrb_id_attrset(mrb, id), mrb_struct_set_m, MRB_ARGS_REQ(1));
       mrb_gc_arena_restore(mrb, ai);
     }
@@ -265,7 +213,7 @@ make_struct(mrb_state *mrb, mrb_value name, mrb_value members, struct RClass * k
     }
     if (mrb_const_defined_at(mrb, mrb_obj_value(klass), id)) {
       mrb_warn(mrb, "redefining constant Struct::%S", name);
-      /* ?rb_mod_remove_const(klass, mrb_sym2name(mrb, id)); */
+      mrb_const_remove(mrb, mrb_obj_value(klass), id);
     }
     c = mrb_define_class_under(mrb, klass, RSTRING_PTR(name), klass);
   }
@@ -328,40 +276,29 @@ mrb_struct_s_def(mrb_state *mrb, mrb_value klass)
   mrb_int argc;
 
   name = mrb_nil_value();
-  rest = mrb_nil_value();
   mrb_get_args(mrb, "*&", &argv, &argc, &b);
   if (argc == 0) { /* special case to avoid crash */
     rest = mrb_ary_new(mrb);
   }
   else {
     if (argc > 0) name = argv[0];
-    if (argc > 1) rest = argv[1];
-    if (mrb_array_p(rest)) {
-      if (!mrb_nil_p(name) && mrb_symbol_p(name)) {
-        /* 1stArgument:symbol -> name=nil rest=argv[0]-[n] */
-        mrb_ary_unshift(mrb, rest, name);
-        name = mrb_nil_value();
-      }
-    }
-    else {
-      pargv = &argv[1];
-      argcnt = argc-1;
-      if (!mrb_nil_p(name) && mrb_symbol_p(name)) {
-        /* 1stArgument:symbol -> name=nil rest=argv[0]-[n] */
-        name = mrb_nil_value();
-        pargv = &argv[0];
-        argcnt++;
-      }
-      rest = mrb_ary_new_from_values(mrb, argcnt, pargv);
+    pargv = &argv[1];
+    argcnt = argc-1;
+    if (!mrb_nil_p(name) && mrb_symbol_p(name)) {
+      /* 1stArgument:symbol -> name=nil rest=argv[0]-[n] */
+      name = mrb_nil_value();
+      pargv = &argv[0];
+      argcnt++;
     }
+    rest = mrb_ary_new_from_values(mrb, argcnt, pargv);
     for (i=0; i<RARRAY_LEN(rest); i++) {
       id = mrb_obj_to_sym(mrb, RARRAY_PTR(rest)[i]);
       mrb_ary_set(mrb, rest, i, mrb_symbol_value(id));
     }
   }
-  st = make_struct(mrb, name, rest, struct_class(mrb));
+  st = make_struct(mrb, name, rest, mrb_class_ptr(klass));
   if (!mrb_nil_p(b)) {
-    mrb_yield_with_class(mrb, b, 1, &st, st, mrb_class_ptr(klass));
+    mrb_yield_with_class(mrb, b, 1, &st, st, mrb_class_ptr(st));
   }
 
   return st;
@@ -408,7 +345,7 @@ mrb_struct_initialize(mrb_state *mrb, mrb_value self)
   mrb_value *argv;
   mrb_int argc;
 
-  mrb_get_args(mrb, "*", &argv, &argc);
+  mrb_get_args(mrb, "*!", &argv, &argc);
   return mrb_struct_initialize_withArg(mrb, argc, argv, self);
 }
 
@@ -418,7 +355,6 @@ static mrb_value
 mrb_struct_init_copy(mrb_state *mrb, mrb_value copy)
 {
   mrb_value s;
-  mrb_int i, len;
 
   mrb_get_args(mrb, "o", &s);
 
@@ -429,33 +365,28 @@ mrb_struct_init_copy(mrb_state *mrb, mrb_value copy)
   if (!mrb_array_p(s)) {
     mrb_raise(mrb, E_TYPE_ERROR, "corrupted struct");
   }
-  if (RSTRUCT_LEN(copy) != RSTRUCT_LEN(s)) {
-    mrb_raise(mrb, E_TYPE_ERROR, "struct size mismatch");
-  }
-  len = RSTRUCT_LEN(copy);
-  for (i = 0; i < len; i++) {
-    mrb_ary_set(mrb, copy, i, RSTRUCT_PTR(s)[i]);
-  }
+  mrb_ary_replace(mrb, copy, s);
   return copy;
 }
 
 static mrb_value
-struct_aref_sym(mrb_state *mrb, mrb_value s, mrb_sym id)
+struct_aref_sym(mrb_state *mrb, mrb_value obj, mrb_sym id)
 {
-  mrb_value *ptr, members;
+  mrb_value members, *ptr;
   const mrb_value *ptr_members;
   mrb_int i, len;
 
-  ptr = RSTRUCT_PTR(s);
-  members = struct_members(mrb, s);
+  members = struct_members(mrb, obj);
   ptr_members = RARRAY_PTR(members);
   len = RARRAY_LEN(members);
+  ptr = RSTRUCT_PTR(obj);
   for (i=0; i<len; i++) {
-    if (mrb_symbol(ptr_members[i]) == id) {
+    mrb_value slot = ptr_members[i];
+    if (mrb_symbol_p(slot) && mrb_symbol(slot) == id) {
       return ptr[i];
     }
   }
-  mrb_raisef(mrb, E_INDEX_ERROR, "no member '%S' in struct", mrb_sym2str(mrb, id));
+  mrb_raisef(mrb, E_INDEX_ERROR, "'%S' is not a struct member", mrb_sym2str(mrb, id));
   return mrb_nil_value();       /* not reached */
 }
 
@@ -503,7 +434,7 @@ mrb_struct_aref(mrb_state *mrb, mrb_value s)
     mrb_value sym = mrb_check_intern_str(mrb, idx);
 
     if (mrb_nil_p(sym)) {
-      mrb_raisef(mrb, E_INDEX_ERROR, "no member '%S' in struct", idx);
+      mrb_name_error(mrb, mrb_intern_str(mrb, idx), "no member '%S' in struct", idx);
     }
     idx = sym;
   }
@@ -522,20 +453,16 @@ mrb_struct_aset_sym(mrb_state *mrb, mrb_value s, mrb_sym id, mrb_value val)
 
   members = struct_members(mrb, s);
   len = RARRAY_LEN(members);
-  if (RSTRUCT_LEN(s) != len) {
-    mrb_raisef(mrb, E_TYPE_ERROR,
-               "struct size differs (%S required %S given)",
-               mrb_fixnum_value(len), mrb_fixnum_value(RSTRUCT_LEN(s)));
-  }
   ptr = RSTRUCT_PTR(s);
   ptr_members = RARRAY_PTR(members);
   for (i=0; i<len; i++) {
     if (mrb_symbol(ptr_members[i]) == id) {
+      mrb_struct_modify(mrb, s);
       ptr[i] = val;
       return val;
     }
   }
-  mrb_raisef(mrb, E_INDEX_ERROR, "no member '%S' in struct", mrb_sym2str(mrb, id));
+  mrb_name_error(mrb, id, "no member '%S' in struct", mrb_sym2str(mrb, id));
   return val;                   /* not reach */
 }
 
@@ -574,7 +501,7 @@ mrb_struct_aset(mrb_state *mrb, mrb_value s)
     mrb_value sym = mrb_check_intern_str(mrb, idx);
 
     if (mrb_nil_p(sym)) {
-      mrb_raisef(mrb, E_INDEX_ERROR, "no member '%S' in struct", idx);
+      mrb_name_error(mrb, mrb_intern_str(mrb, idx), "no member '%S' in struct", idx);
     }
     idx = sym;
   }
@@ -594,6 +521,7 @@ mrb_struct_aset(mrb_state *mrb, mrb_value s)
                "offset %S too large for struct(size:%S)",
                mrb_fixnum_value(i), mrb_fixnum_value(RSTRUCT_LEN(s)));
   }
+  mrb_struct_modify(mrb, s);
   return RSTRUCT_PTR(s)[i] = val;
 }
 
@@ -719,7 +647,7 @@ mrb_struct_to_h(mrb_state *mrb, mrb_value self)
   mrb_value members, ret;
   mrb_int i;
 
-  members = struct_s_members(mrb, mrb_class(mrb, self));
+  members = struct_members(mrb, self);
   ret = mrb_hash_new_capa(mrb, RARRAY_LEN(members));
 
   for (i = 0; i < RARRAY_LEN(members); ++i) {
@@ -760,6 +688,7 @@ mrb_mruby_struct_gem_init(mrb_state* mrb)
 {
   struct RClass *st;
   st = mrb_define_class(mrb, "Struct",  mrb->object_class);
+  MRB_SET_INSTANCE_TT(st, MRB_TT_ARRAY);
 
   mrb_define_class_method(mrb, st, "new",             mrb_struct_s_def,       MRB_ARGS_ANY());  /* 15.2.18.3.1  */
 
@@ -776,7 +705,7 @@ mrb_mruby_struct_gem_init(mrb_state* mrb)
   mrb_define_method(mrb, st,        "to_a",           mrb_struct_to_a,        MRB_ARGS_NONE());
   mrb_define_method(mrb, st,        "values",         mrb_struct_to_a,        MRB_ARGS_NONE());
   mrb_define_method(mrb, st,        "to_h",           mrb_struct_to_h,        MRB_ARGS_NONE());
-  mrb_define_method(mrb, st,        "values_at",      mrb_struct_values_at,   MRB_ARGS_NONE());
+  mrb_define_method(mrb, st,        "values_at",      mrb_struct_values_at,   MRB_ARGS_ANY());
 }
 
 void
index 2db8f5d..421fe4b 100644 (file)
@@ -2,42 +2,58 @@
 # Struct ISO Test
 
 assert('Struct', '15.2.18') do
-  Struct.class == Class
+  assert_equal Class, Struct.class
 end
 
 assert('Struct.new', '15.2.18.3.1') do
   c = Struct.new(:m1, :m2)
-  c.superclass == Struct and
-    c.members == [:m1,:m2]
+  assert_equal Struct, c.superclass
+  assert_equal [:m1, :m2], c.members
 end
 
 # Check crash bug with Struc.new and no params.
 assert('Struct.new', '15.2.18.3.1') do
   c = Struct.new()
-  c.superclass == Struct and c.members == []
+  assert_equal Struct, c.superclass
+  assert_equal [], c.members
 end
 
 assert('Struct#==', '15.2.18.4.1') do
   c = Struct.new(:m1, :m2)
   cc1 = c.new(1,2)
   cc2 = c.new(1,2)
-  cc1 == cc2
+  assert_true cc1 == cc2
+
+  Struct.new(:m1, :m2) { def foo; end }
+  assert_raise(NoMethodError) { Struct.new(:m1).new.foo }
 end
 
 assert('Struct#[]', '15.2.18.4.2') do
   c = Struct.new(:m1, :m2)
   cc = c.new(1,2)
-  cc[:m1] == 1 and cc["m2"] == 2
+  assert_equal 1, cc[:m1]
+  assert_equal 2, cc["m2"]
+  assert_equal 1, cc[0]
+  assert_equal 2, cc[-1]
+  assert_raise(TypeError) { cc[[]] }
+  assert_raise(IndexError) { cc[2] }
+  assert_raise(NameError) { cc['tama'] }
 end
 
 assert('Struct#[]=', '15.2.18.4.3') do
   c = Struct.new(:m1, :m2)
   cc = c.new(1,2)
   cc[:m1] = 3
-  cc[:m1] == 3
+  assert_equal 3, cc[:m1]
   cc["m2"] = 3
   assert_equal 3, cc["m2"]
+  cc[0] = 4
+  assert_equal 4, cc[0]
+  cc[-1] = 5
+  assert_equal 5, cc[-1]
   assert_raise(TypeError) { cc[[]] = 3 }
+  assert_raise(IndexError) { cc[2] = 7 }
+  assert_raise(NameError) { cc['pochi'] = 8 }
 end
 
 assert('Struct#each', '15.2.18.4.4') do
@@ -47,7 +63,7 @@ assert('Struct#each', '15.2.18.4.4') do
   cc.each{|x|
     a << x
   }
-  a[0] == 1 and a[1] == 2
+  assert_equal [1, 2], a
 end
 
 assert('Struct#each_pair', '15.2.18.4.5') do
@@ -57,19 +73,17 @@ assert('Struct#each_pair', '15.2.18.4.5') do
   cc.each_pair{|k,v|
     a << [k,v]
   }
-  a[0] == [:m1, 1] and a[1] == [:m2, 2]
+  assert_equal [[:m1, 1], [:m2, 2]], a
 end
 
 assert('Struct#members', '15.2.18.4.6') do
   c = Struct.new(:m1, :m2)
-  cc = c.new(1,2)
-  cc.members == [:m1,:m2]
+  assert_equal [:m1, :m2], c.new(1,2).members
 end
 
 assert('Struct#select', '15.2.18.4.7') do
   c = Struct.new(:m1, :m2)
-  cc = c.new(1,2)
-  cc.select{|v| v % 2 == 0} == [2]
+  assert_equal([2]) { c.new(1,2).select{|v| v % 2 == 0} }
 end
 
 assert('large struct') do
@@ -100,6 +114,14 @@ assert('wrong struct arg count') do
   end
 end
 
+assert('struct dup') do
+  c = Struct.new(:m1, :m2, :m3, :m4, :m5)
+  cc = c.new(1,2,3,4,5)
+  assert_nothing_raised {
+    assert_equal(cc, cc.dup)
+  }
+end
+
 assert('struct inspect') do
   c = Struct.new(:m1, :m2, :m3, :m4, :m5)
   cc = c.new(1,2,3,4,5)
@@ -129,3 +151,62 @@ assert('Struct#values_at') do
   assert_equal ['io', 'aki'], a.values_at(1, 0)
   assert_raise(IndexError) { a.values_at 2 }
 end
+
+assert("Struct#dig") do
+  a = Struct.new(:blue, :purple).new('aki', Struct.new(:red).new(1))
+  assert_equal 'aki', a.dig(:blue)
+  assert_equal 1, a.dig(:purple, :red)
+  assert_equal 1, a.dig(1, 0)
+end
+
+assert("Struct.new removes existing constant") do
+  skip "redefining Struct with same name cause warnings"
+  begin
+    assert_not_equal Struct.new("Test", :a), Struct.new("Test", :a, :b)
+  ensure
+    Struct.remove_const :Test
+  end
+end
+
+assert("Struct#initialize_copy requires struct to be the same type") do
+  begin
+    Struct.new("Test", :a)
+    a = Struct::Test.new("a")
+    Struct.remove_const :Test
+    Struct.new("Test", :a, :b)
+    assert_raise(TypeError) do
+      a.initialize_copy(Struct::Test.new("a", "b"))
+    end
+  ensure
+    Struct.remove_const :Test
+  end
+end
+
+assert("Struct.new does not allow array") do
+  assert_raise(TypeError) do
+    Struct.new("Test", [:a])
+  end
+end
+
+assert("Struct.new generates subclass of Struct") do
+  begin
+    original_struct = Struct
+    Struct = String
+    assert_equal original_struct, original_struct.new.superclass
+  ensure
+    Struct = original_struct
+  end
+end
+
+assert 'Struct#freeze' do
+  c = Struct.new :m
+
+  o = c.new
+  o.m = :test
+  assert_equal :test, o.m
+
+  o.freeze
+  assert_raise(RuntimeError) { o.m = :modify }
+  assert_raise(RuntimeError) { o[:m] = :modify }
+  assert_equal :test, o.m
+end
index a96c401..a992dbf 100644 (file)
@@ -1,6 +1,6 @@
-#include "mruby.h"
-#include "mruby/khash.h"
-#include "mruby/array.h"
+#include <mruby.h>
+#include <mruby/khash.h>
+#include <mruby/array.h>
 
 typedef struct symbol_name {
   size_t len;
index 7f06337..14e93ff 100644 (file)
 #include <stdlib.h>
 #include <string.h>
 
-#include "mruby.h"
-#include "mruby/proc.h"
-#include "mruby/data.h"
-#include "mruby/compile.h"
-#include "mruby/string.h"
-#include "mruby/variable.h"
-#include "mruby/array.h"
+#include <mruby.h>
+#include <mruby/proc.h>
+#include <mruby/data.h>
+#include <mruby/compile.h>
+#include <mruby/string.h>
+#include <mruby/variable.h>
+#include <mruby/array.h>
 
 void
 mrb_init_mrbtest(mrb_state *);
@@ -94,6 +94,12 @@ mrb_init_test_driver(mrb_state *mrb, mrb_bool verbose)
   mrb_define_const(mrb, mrbtest, "FIXNUM_MIN", mrb_fixnum_value(MRB_INT_MIN));
   mrb_define_const(mrb, mrbtest, "FIXNUM_BIT", mrb_fixnum_value(MRB_INT_BIT));
 
+#ifdef MRB_USE_FLOAT
+  mrb_define_const(mrb, mrbtest, "FLOAT_TOLERANCE", mrb_float_value(mrb, 1e-6));
+#else
+  mrb_define_const(mrb, mrbtest, "FLOAT_TOLERANCE", mrb_float_value(mrb, 1e-12));
+#endif
+
   if (verbose) {
     mrb_gv_set(mrb, mrb_intern_lit(mrb, "$mrbtest_verbose"), mrb_true_value());
   }
index 1e2ba92..17ac1bd 100644 (file)
@@ -1,10 +1,9 @@
 #include <stdlib.h>
-#include "mruby.h"
-#include "mruby/irep.h"
-#include "mruby/variable.h"
+#include <mruby.h>
+#include <mruby/irep.h>
+#include <mruby/variable.h>
 
 extern const uint8_t mrbtest_assert_irep[];
-extern const uint8_t mrbtest_irep[];
 
 void mrbgemtest_init(mrb_state* mrb);
 void mrb_init_test_driver(mrb_state* mrb, mrb_bool verbose);
@@ -24,7 +23,6 @@ mrb_init_mrbtest(mrb_state *mrb)
   }
   mrb_init_test_driver(core_test, mrb_test(mrb_gv_get(mrb, mrb_intern_lit(mrb, "$mrbtest_verbose"))));
   mrb_load_irep(core_test, mrbtest_assert_irep);
-  mrb_load_irep(core_test, mrbtest_irep);
   mrb_t_pass_result(mrb, core_test);
 
 #ifndef DISABLE_GEMS
index 2203958..ae4c2f1 100644 (file)
@@ -6,9 +6,10 @@ MRuby::Gem::Specification.new('mruby-test') do |spec|
   build.bins << 'mrbtest'
   spec.add_dependency('mruby-compiler', :core => 'mruby-compiler')
 
+  spec.test_rbfiles = Dir.glob("#{MRUBY_ROOT}/test/t/*.rb")
+
   clib = "#{build_dir}/mrbtest.c"
   mlib = clib.ext(exts.object)
-  mrbs = Dir.glob("#{MRUBY_ROOT}/test/t/*.rb")
   exec = exefile("#{build.build_dir}/bin/mrbtest")
 
   libmruby = libfile("#{build.build_dir}/lib/libmruby")
@@ -26,7 +27,7 @@ MRuby::Gem::Specification.new('mruby-test') do |spec|
   mrbtest_objs << assert_lib
 
   file assert_lib => assert_c
-  file assert_c => [build.mrbcfile, assert_rb] do |t|
+  file assert_c => assert_rb do |t|
     open(t.name, 'w') do |f|
       mrbc.run f, assert_rb, 'mrbtest_assert_irep'
     end
@@ -40,7 +41,7 @@ MRuby::Gem::Specification.new('mruby-test') do |spec|
     dep_list = build.gems.tsort_dependencies(g.test_dependencies, gem_table).select(&:generate_functions)
 
     file test_rbobj => g.test_rbireps
-    file g.test_rbireps => [g.test_rbfiles].flatten + [File.join(g.dir, 'mrbgem.rake'), g.build.mrbcfile, "#{MRUBY_ROOT}/tasks/mrbgem_spec.rake"] do |t|
+    file g.test_rbireps => [g.test_rbfiles].flatten do |t|
       FileUtils.mkdir_p File.dirname(t.name)
       open(t.name, 'w') do |f|
         g.print_gem_test_header(f)
@@ -145,8 +146,21 @@ MRuby::Gem::Specification.new('mruby-test') do |spec|
   end
 
   init = "#{spec.dir}/init_mrbtest.c"
+
+  # store the last gem selection and make the re-build
+  # of the test gem depending on a change to the gem
+  # selection
+  active_gems = "#{build_dir}/active_gems.lst"
+  FileUtils.mkdir_p File.dirname(active_gems)
+  open(active_gems, 'w+') do |f|
+    build.gems.each do |g|
+      f.puts g.name
+    end
+  end
+  file clib => active_gems
+
   file mlib => clib
-  file clib => [build.mrbcfile, init] + mrbs do |t|
+  file clib => init do |t|
     _pp "GEN", "*.rb", "#{clib.relative_path}"
     FileUtils.mkdir_p File.dirname(clib)
     open(clib, 'w') do |f|
@@ -160,7 +174,6 @@ MRuby::Gem::Specification.new('mruby-test') do |spec|
       f.puts %Q[ */]
       f.puts %Q[]
       f.puts IO.read(init)
-      mrbc.run f, mrbs, 'mrbtest_irep'
       build.gems.each do |g|
         f.puts %Q[void GENERATED_TMP_mrb_#{g.funcname}_gem_test(mrb_state *mrb);]
       end
index c18ac75..5e86248 100644 (file)
@@ -5,11 +5,24 @@
 */
 
 #include <math.h>
-#include <stdio.h>
 #include <time.h>
-#include "mruby.h"
-#include "mruby/class.h"
-#include "mruby/data.h"
+#include <mruby.h>
+#include <mruby/class.h>
+#include <mruby/data.h>
+
+#ifndef DISABLE_STDIO
+#include <stdio.h>
+#else
+#include <string.h>
+#endif
+
+#define NDIV(x,y) (-(-((x)+1)/(y))-1)
+
+#if defined(_MSC_VER) && _MSC_VER < 1800
+double round(double x) {
+  return floor(x + 0.5);
+}
+#endif
 
 #if !defined(__MINGW64__) && defined(_WIN32)
 # define llround(x) round(x)
 #endif
 #endif
 
+/* asctime(3) */
+/* mruby usually use its own implementation of struct tm to string conversion */
+/* except when DISABLE_STDIO is set. In that case, it uses asctime() or asctime_r(). */
+/* By default mruby tries to use asctime_r() which is reentrant. */
+/* Undef following macro on platforms that does not have asctime_r(). */
+/* #define NO_ASCTIME_R */
+
 /* timegm(3) */
 /* mktime() creates tm structure for localtime; timegm() is for UTC time */
 /* define following macro to use probably faster timegm() on the platform */
@@ -153,6 +173,7 @@ static const mrb_timezone_name timezone_names[] = {
   { "LOCAL", sizeof("LOCAL") - 1 },
 };
 
+#ifndef DISABLE_STDIO
 static const char mon_names[12][4] = {
   "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec",
 };
@@ -160,6 +181,7 @@ static const char mon_names[12][4] = {
 static const char wday_names[7][4] = {
   "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat",
 };
+#endif
 
 struct mrb_time {
   time_t              sec;
@@ -173,7 +195,7 @@ static const struct mrb_data_type mrb_time_type = { "Time", mrb_free };
 /** Updates the datetime of a mrb_time based on it's timezone and
 seconds setting. Returns self on success, NULL of failure. */
 static struct mrb_time*
-mrb_time_update_datetime(struct mrb_time *self)
+time_update_datetime(mrb_state *mrb, struct mrb_time *self)
 {
   struct tm *aid;
 
@@ -183,7 +205,11 @@ mrb_time_update_datetime(struct mrb_time *self)
   else {
     aid = localtime_r(&self->sec, &self->datetime);
   }
-  if (!aid) return NULL;
+  if (!aid) {
+    mrb_raisef(mrb, E_ARGUMENT_ERROR, "%S out of Time range", mrb_float_value(mrb, (mrb_float)self->sec));
+    /* not reached */
+    return NULL;
+  }
 #ifdef NO_GMTIME_R
   self->datetime = *aid; /* copy data */
 #endif
@@ -197,36 +223,44 @@ mrb_time_wrap(mrb_state *mrb, struct RClass *tc, struct mrb_time *tm)
   return mrb_obj_value(Data_Wrap_Struct(mrb, tc, &mrb_time_type, tm));
 }
 
+void mrb_check_num_exact(mrb_state *mrb, mrb_float num);
 
 /* Allocates a mrb_time object and initializes it. */
 static struct mrb_time*
 time_alloc(mrb_state *mrb, double sec, double usec, enum mrb_timezone timezone)
 {
   struct mrb_time *tm;
+  time_t tsec = 0;
+
+  mrb_check_num_exact(mrb, (mrb_float)sec);
+  mrb_check_num_exact(mrb, (mrb_float)usec);
 
-  tm = (struct mrb_time *)mrb_malloc(mrb, sizeof(struct mrb_time));
   if (sizeof(time_t) == 4 && (sec > (double)INT32_MAX || (double)INT32_MIN > sec)) {
     goto out_of_range;
   }
   if (sizeof(time_t) == 8 && (sec > (double)INT64_MAX || (double)INT64_MIN > sec)) {
     goto out_of_range;
   }
-  tm->sec  = (time_t)sec;
-  if ((sec > 0 && tm->sec < 0) || (sec < 0 && (double)tm->sec > sec)) {
+  tsec  = (time_t)sec;
+  if ((sec > 0 && tsec < 0) || (sec < 0 && (double)tsec > sec)) {
   out_of_range:
     mrb_raisef(mrb, E_ARGUMENT_ERROR, "%S out of Time range", mrb_float_value(mrb, sec));
   }
+  tm = (struct mrb_time *)mrb_malloc(mrb, sizeof(struct mrb_time));
+  tm->sec  = tsec;
   tm->usec = (time_t)llround((sec - tm->sec) * 1.0e6 + usec);
-  while (tm->usec < 0) {
-    tm->sec--;
-    tm->usec += 1000000;
+  if (tm->usec < 0) {
+    long sec2 = (long)NDIV(usec,1000000); /* negative div */
+    tm->usec -= sec2 * 1000000;
+    tm->sec += sec2;
   }
-  while (tm->usec >= 1000000) {
-    tm->sec++;
-    tm->usec -= 1000000;
+  else if (tm->usec >= 1000000) {
+    long sec2 = (long)(usec / 1000000);
+    tm->usec -= sec2 * 1000000;
+    tm->sec += sec2;
   }
   tm->timezone = timezone;
-  mrb_time_update_datetime(tm);
+  time_update_datetime(mrb, tm);
 
   return tm;
 }
@@ -278,7 +312,7 @@ current_mrb_time(mrb_state *mrb)
   }
 #endif
   tm->timezone = MRB_TIMEZONE_LOCAL;
-  mrb_time_update_datetime(tm);
+  time_update_datetime(mrb, tm);
 
   return tm;
 }
@@ -316,6 +350,15 @@ time_mktime(mrb_state *mrb, mrb_int ayear, mrb_int amonth, mrb_int aday,
   nowtime.tm_min   = (int)amin;
   nowtime.tm_sec   = (int)asec;
   nowtime.tm_isdst = -1;
+
+  if (nowtime.tm_mon  < 0 || nowtime.tm_mon  > 11
+      || nowtime.tm_mday < 1 || nowtime.tm_mday > 31
+      || nowtime.tm_hour < 0 || nowtime.tm_hour > 24
+      || (nowtime.tm_hour == 24 && (nowtime.tm_min > 0 || nowtime.tm_sec > 0))
+      || nowtime.tm_min  < 0 || nowtime.tm_min  > 59
+      || nowtime.tm_sec  < 0 || nowtime.tm_sec  > 60)
+    mrb_raise(mrb, E_RUNTIME_ERROR, "argument out of range");
+
   if (timezone == MRB_TIMEZONE_UTC) {
     nowsecs = timegm(&nowtime);
   }
@@ -356,6 +399,17 @@ mrb_time_local(mrb_state *mrb, mrb_value self)
           time_mktime(mrb, ayear, amonth, aday, ahour, amin, asec, ausec, MRB_TIMEZONE_LOCAL));
 }
 
+static struct mrb_time*
+time_get_ptr(mrb_state *mrb, mrb_value time)
+{
+  struct mrb_time *tm;
+
+  tm = DATA_GET_PTR(mrb, time, &mrb_time_type, struct mrb_time);
+  if (!tm) {
+    mrb_raise(mrb, E_ARGUMENT_ERROR, "uninitialized time");
+  }
+  return tm;
+}
 
 static mrb_value
 mrb_time_eq(mrb_state *mrb, mrb_value self)
@@ -365,7 +419,7 @@ mrb_time_eq(mrb_state *mrb, mrb_value self)
   mrb_bool eq_p;
 
   mrb_get_args(mrb, "o", &other);
-  tm1 = DATA_CHECK_GET_PTR(mrb, self, &mrb_time_type, struct mrb_time);
+  tm1 = DATA_GET_PTR(mrb, self, &mrb_time_type, struct mrb_time);
   tm2 = DATA_CHECK_GET_PTR(mrb, other, &mrb_time_type, struct mrb_time);
   eq_p = tm1 && tm2 && tm1->sec == tm2->sec && tm1->usec == tm2->usec;
 
@@ -379,7 +433,7 @@ mrb_time_cmp(mrb_state *mrb, mrb_value self)
   struct mrb_time *tm1, *tm2;
 
   mrb_get_args(mrb, "o", &other);
-  tm1 = DATA_CHECK_GET_PTR(mrb, self, &mrb_time_type, struct mrb_time);
+  tm1 = DATA_GET_PTR(mrb, self, &mrb_time_type, struct mrb_time);
   tm2 = DATA_CHECK_GET_PTR(mrb, other, &mrb_time_type, struct mrb_time);
   if (!tm1 || !tm2) return mrb_nil_value();
   if (tm1->sec > tm2->sec) {
@@ -405,7 +459,7 @@ mrb_time_plus(mrb_state *mrb, mrb_value self)
   struct mrb_time *tm;
 
   mrb_get_args(mrb, "f", &f);
-  tm = DATA_GET_PTR(mrb, self, &mrb_time_type, struct mrb_time);
+  tm = time_get_ptr(mrb, self);
   return mrb_time_make(mrb, mrb_obj_class(mrb, self), (double)tm->sec+f, (double)tm->usec, tm->timezone);
 }
 
@@ -417,8 +471,7 @@ mrb_time_minus(mrb_state *mrb, mrb_value self)
   struct mrb_time *tm, *tm2;
 
   mrb_get_args(mrb, "o", &other);
-  tm = DATA_GET_PTR(mrb, self, &mrb_time_type, struct mrb_time);
-
+  tm = time_get_ptr(mrb, self);
   tm2 = DATA_CHECK_GET_PTR(mrb, other, &mrb_time_type, struct mrb_time);
   if (tm2) {
     f = (mrb_float)(tm->sec - tm2->sec)
@@ -438,7 +491,7 @@ mrb_time_wday(mrb_state *mrb, mrb_value self)
 {
   struct mrb_time *tm;
 
-  tm = DATA_GET_PTR(mrb, self, &mrb_time_type, struct mrb_time);
+  tm = time_get_ptr(mrb, self);
   return mrb_fixnum_value(tm->datetime.tm_wday);
 }
 
@@ -449,7 +502,7 @@ mrb_time_yday(mrb_state *mrb, mrb_value self)
 {
   struct mrb_time *tm;
 
-  tm = DATA_GET_PTR(mrb, self, &mrb_time_type, struct mrb_time);
+  tm = time_get_ptr(mrb, self);
   return mrb_fixnum_value(tm->datetime.tm_yday + 1);
 }
 
@@ -460,7 +513,7 @@ mrb_time_year(mrb_state *mrb, mrb_value self)
 {
   struct mrb_time *tm;
 
-  tm = DATA_GET_PTR(mrb, self, &mrb_time_type, struct mrb_time);
+  tm = time_get_ptr(mrb, self);
   return mrb_fixnum_value(tm->datetime.tm_year + 1900);
 }
 
@@ -471,7 +524,7 @@ mrb_time_zone(mrb_state *mrb, mrb_value self)
 {
   struct mrb_time *tm;
 
-  tm = DATA_GET_PTR(mrb, self, &mrb_time_type, struct mrb_time);
+  tm = time_get_ptr(mrb, self);
   if (tm->timezone <= MRB_TIMEZONE_NONE) return mrb_nil_value();
   if (tm->timezone >= MRB_TIMEZONE_LAST) return mrb_nil_value();
   return mrb_str_new_static(mrb,
@@ -484,18 +537,28 @@ mrb_time_zone(mrb_state *mrb, mrb_value self)
 static mrb_value
 mrb_time_asctime(mrb_state *mrb, mrb_value self)
 {
-  struct mrb_time *tm;
-  struct tm *d;
-  char buf[256];
+  struct mrb_time *tm = time_get_ptr(mrb, self);
+  struct tm *d = &tm->datetime;
   int len;
 
-  tm = DATA_GET_PTR(mrb, self, &mrb_time_type, struct mrb_time);
-  d = &tm->datetime;
+#if defined(DISABLE_STDIO)
+  char *s;
+# ifdef NO_ASCTIME_R
+  s = asctime(d);
+# else
+  char buf[32];
+  s = asctime_r(d, buf);
+# endif
+  len = strlen(s)-1;            /* truncate the last newline */
+#else
+  char buf[256];
+
   len = snprintf(buf, sizeof(buf), "%s %s %02d %02d:%02d:%02d %s%d",
     wday_names[d->tm_wday], mon_names[d->tm_mon], d->tm_mday,
     d->tm_hour, d->tm_min, d->tm_sec,
     tm->timezone == MRB_TIMEZONE_UTC ? "UTC " : "",
     d->tm_year + 1900);
+#endif
   return mrb_str_new(mrb, buf, len);
 }
 
@@ -506,8 +569,7 @@ mrb_time_day(mrb_state *mrb, mrb_value self)
 {
   struct mrb_time *tm;
 
-  tm = DATA_GET_PTR(mrb, self, &mrb_time_type, struct mrb_time);
-  if (!tm) return mrb_nil_value();
+  tm = time_get_ptr(mrb, self);
   return mrb_fixnum_value(tm->datetime.tm_mday);
 }
 
@@ -519,7 +581,7 @@ mrb_time_dst_p(mrb_state *mrb, mrb_value self)
 {
   struct mrb_time *tm;
 
-  tm = DATA_GET_PTR(mrb, self, &mrb_time_type, struct mrb_time);
+  tm = time_get_ptr(mrb, self);
   return mrb_bool_value(tm->datetime.tm_isdst);
 }
 
@@ -531,11 +593,11 @@ mrb_time_getutc(mrb_state *mrb, mrb_value self)
 {
   struct mrb_time *tm, *tm2;
 
-  tm = DATA_GET_PTR(mrb, self, &mrb_time_type, struct mrb_time);
+  tm = time_get_ptr(mrb, self);
   tm2 = (struct mrb_time *)mrb_malloc(mrb, sizeof(*tm));
   *tm2 = *tm;
   tm2->timezone = MRB_TIMEZONE_UTC;
-  mrb_time_update_datetime(tm2);
+  time_update_datetime(mrb, tm2);
   return mrb_time_wrap(mrb, mrb_obj_class(mrb, self), tm2);
 }
 
@@ -546,11 +608,11 @@ mrb_time_getlocal(mrb_state *mrb, mrb_value self)
 {
   struct mrb_time *tm, *tm2;
 
-  tm = DATA_GET_PTR(mrb, self, &mrb_time_type, struct mrb_time);
+  tm = time_get_ptr(mrb, self);
   tm2 = (struct mrb_time *)mrb_malloc(mrb, sizeof(*tm));
   *tm2 = *tm;
   tm2->timezone = MRB_TIMEZONE_LOCAL;
-  mrb_time_update_datetime(tm2);
+  time_update_datetime(mrb, tm2);
   return mrb_time_wrap(mrb, mrb_obj_class(mrb, self), tm2);
 }
 
@@ -561,7 +623,7 @@ mrb_time_hour(mrb_state *mrb, mrb_value self)
 {
   struct mrb_time *tm;
 
-  tm = DATA_GET_PTR(mrb, self, &mrb_time_type, struct mrb_time);
+  tm = time_get_ptr(mrb, self);
   return mrb_fixnum_value(tm->datetime.tm_hour);
 }
 
@@ -575,14 +637,14 @@ mrb_time_initialize(mrb_state *mrb, mrb_value self)
   int n;
   struct mrb_time *tm;
 
+  n = mrb_get_args(mrb, "|iiiiiii",
+       &ayear, &amonth, &aday, &ahour, &amin, &asec, &ausec);
   tm = (struct mrb_time*)DATA_PTR(self);
   if (tm) {
     mrb_free(mrb, tm);
   }
   mrb_data_init(self, NULL, &mrb_time_type);
 
-  n = mrb_get_args(mrb, "|iiiiiii",
-       &ayear, &amonth, &aday, &ahour, &amin, &asec, &ausec);
   if (n == 0) {
     tm = current_mrb_time(mrb);
   }
@@ -599,16 +661,23 @@ static mrb_value
 mrb_time_initialize_copy(mrb_state *mrb, mrb_value copy)
 {
   mrb_value src;
+  struct mrb_time *t1, *t2;
 
   mrb_get_args(mrb, "o", &src);
   if (mrb_obj_equal(mrb, copy, src)) return copy;
   if (!mrb_obj_is_instance_of(mrb, src, mrb_obj_class(mrb, copy))) {
     mrb_raise(mrb, E_TYPE_ERROR, "wrong argument class");
   }
-  if (!DATA_PTR(copy)) {
-    mrb_data_init(copy, mrb_malloc(mrb, sizeof(struct mrb_time)), &mrb_time_type);
+  t1 = (struct mrb_time *)DATA_PTR(copy);
+  t2 = (struct mrb_time *)DATA_PTR(src);
+  if (!t2) {
+    mrb_raise(mrb, E_ARGUMENT_ERROR, "uninitialized time");
+  }
+  if (!t1) {
+    t1 = (struct mrb_time *)mrb_malloc(mrb, sizeof(struct mrb_time));
+    mrb_data_init(copy, t1, &mrb_time_type);
   }
-  *(struct mrb_time *)DATA_PTR(copy) = *(struct mrb_time *)DATA_PTR(src);
+  *t1 = *t2;
   return copy;
 }
 
@@ -619,9 +688,9 @@ mrb_time_localtime(mrb_state *mrb, mrb_value self)
 {
   struct mrb_time *tm;
 
-  tm = DATA_GET_PTR(mrb, self, &mrb_time_type, struct mrb_time);
+  tm = time_get_ptr(mrb, self);
   tm->timezone = MRB_TIMEZONE_LOCAL;
-  mrb_time_update_datetime(tm);
+  time_update_datetime(mrb, tm);
   return self;
 }
 
@@ -632,7 +701,7 @@ mrb_time_mday(mrb_state *mrb, mrb_value self)
 {
   struct mrb_time *tm;
 
-  tm = DATA_GET_PTR(mrb, self, &mrb_time_type, struct mrb_time);
+  tm = time_get_ptr(mrb, self);
   return mrb_fixnum_value(tm->datetime.tm_mday);
 }
 
@@ -643,7 +712,7 @@ mrb_time_min(mrb_state *mrb, mrb_value self)
 {
   struct mrb_time *tm;
 
-  tm = DATA_GET_PTR(mrb, self, &mrb_time_type, struct mrb_time);
+  tm = time_get_ptr(mrb, self);
   return mrb_fixnum_value(tm->datetime.tm_min);
 }
 
@@ -654,7 +723,7 @@ mrb_time_mon(mrb_state *mrb, mrb_value self)
 {
   struct mrb_time *tm;
 
-  tm = DATA_GET_PTR(mrb, self, &mrb_time_type, struct mrb_time);
+  tm = time_get_ptr(mrb, self);
   return mrb_fixnum_value(tm->datetime.tm_mon + 1);
 }
 
@@ -665,7 +734,7 @@ mrb_time_sec(mrb_state *mrb, mrb_value self)
 {
   struct mrb_time *tm;
 
-  tm = DATA_GET_PTR(mrb, self, &mrb_time_type, struct mrb_time);
+  tm = time_get_ptr(mrb, self);
   return mrb_fixnum_value(tm->datetime.tm_sec);
 }
 
@@ -677,7 +746,7 @@ mrb_time_to_f(mrb_state *mrb, mrb_value self)
 {
   struct mrb_time *tm;
 
-  tm = DATA_GET_PTR(mrb, self, &mrb_time_type, struct mrb_time);
+  tm = time_get_ptr(mrb, self);
   return mrb_float_value(mrb, (mrb_float)tm->sec + (mrb_float)tm->usec/1.0e6);
 }
 
@@ -688,7 +757,7 @@ mrb_time_to_i(mrb_state *mrb, mrb_value self)
 {
   struct mrb_time *tm;
 
-  tm = DATA_GET_PTR(mrb, self, &mrb_time_type, struct mrb_time);
+  tm = time_get_ptr(mrb, self);
   if (tm->sec > MRB_INT_MAX || tm->sec < MRB_INT_MIN) {
     return mrb_float_value(mrb, (mrb_float)tm->sec);
   }
@@ -702,7 +771,7 @@ mrb_time_usec(mrb_state *mrb, mrb_value self)
 {
   struct mrb_time *tm;
 
-  tm = DATA_GET_PTR(mrb, self, &mrb_time_type, struct mrb_time);
+  tm = time_get_ptr(mrb, self);
   if (tm->usec > MRB_INT_MAX || tm->usec < MRB_INT_MIN) {
     return mrb_float_value(mrb, (mrb_float)tm->usec);
   }
@@ -716,9 +785,9 @@ mrb_time_utc(mrb_state *mrb, mrb_value self)
 {
   struct mrb_time *tm;
 
-  tm = DATA_GET_PTR(mrb, self, &mrb_time_type, struct mrb_time);
+  tm = time_get_ptr(mrb, self);
   tm->timezone = MRB_TIMEZONE_UTC;
-  mrb_time_update_datetime(tm);
+  time_update_datetime(mrb, tm);
   return self;
 }
 
@@ -729,7 +798,7 @@ mrb_time_utc_p(mrb_state *mrb, mrb_value self)
 {
   struct mrb_time *tm;
 
-  tm = DATA_GET_PTR(mrb, self, &mrb_time_type, struct mrb_time);
+  tm = time_get_ptr(mrb, self);
   return mrb_bool_value(tm->timezone == MRB_TIMEZONE_UTC);
 }
 
index 759e288..52b9311 100644 (file)
@@ -10,7 +10,14 @@ assert('Time', '15.2.19') do
 end
 
 assert('Time.at', '15.2.19.6.1') do
-  Time.at(1300000000.0)
+  assert_kind_of(Time, Time.at(1300000000.0))
+
+  assert_raise(FloatDomainError) { Time.at(Float::NAN) }
+  assert_raise(FloatDomainError) { Time.at(Float::INFINITY) }
+  assert_raise(FloatDomainError) { Time.at(-Float::INFINITY) }
+  assert_raise(FloatDomainError) { Time.at(0, Float::NAN) }
+  assert_raise(FloatDomainError) { Time.at(0, Float::INFINITY) }
+  assert_raise(FloatDomainError) { Time.at(0, -Float::INFINITY) }
 end
 
 assert('Time.gm', '15.2.19.6.2') do
@@ -37,14 +44,22 @@ assert('Time#+', '15.2.19.7.1') do
   t1 = Time.at(1300000000.0)
   t2 = t1.+(60)
 
-  t2.utc.asctime == "Sun Mar 13 07:07:40 UTC 2011"
+  assert_equal(t2.utc.asctime, "Sun Mar 13 07:07:40 UTC 2011")
+
+  assert_raise(FloatDomainError) { Time.at(0) + Float::NAN }
+  assert_raise(FloatDomainError) { Time.at(0) + Float::INFINITY }
+  assert_raise(FloatDomainError) { Time.at(0) + -Float::INFINITY }
 end
 
 assert('Time#-', '15.2.19.7.2') do
   t1 = Time.at(1300000000.0)
   t2 = t1.-(60)
 
-  t2.utc.asctime == "Sun Mar 13 07:05:40 UTC 2011"
+  assert_equal(t2.utc.asctime, "Sun Mar 13 07:05:40 UTC 2011")
+
+  assert_raise(FloatDomainError) { Time.at(0) - Float::NAN }
+  assert_raise(FloatDomainError) { Time.at(0) - Float::INFINITY }
+  assert_raise(FloatDomainError) { Time.at(0) - -Float::INFINITY }
 end
 
 assert('Time#<=>', '15.2.19.7.3') do
diff --git a/third-party/mruby/mrblib/00class.rb b/third-party/mruby/mrblib/00class.rb
new file mode 100644 (file)
index 0000000..f3762e8
--- /dev/null
@@ -0,0 +1,26 @@
+class Module
+   # 15.2.2.4.12
+  def attr_accessor(*names)
+    attr_reader(*names)
+    attr_writer(*names)
+  end
+  # 15.2.2.4.11
+  def attr(name)
+    attr_reader(name)
+  end
+
+  # 15.2.2.4.27
+  def include(*args)
+    args.reverse.each do |m|
+      m.append_features(self)
+      m.included(self)
+    end
+  end
+
+  def prepend(*args)
+    args.reverse.each do |m|
+      m.prepend_features(self)
+      m.prepended(self)
+    end
+  end
+end
similarity index 89%
rename from third-party/mruby/mrblib/error.rb
rename to third-party/mruby/mrblib/10error.rb
index 2674af7..22a8d1a 100644 (file)
@@ -2,8 +2,8 @@
 class ArgumentError < StandardError
 end
 
-# ISO 15.2.25
-class LocalJumpError < StandardError
+# ISO 15.2.25 says "LocalJumpError < StandardError"
+class LocalJumpError < ScriptError
 end
 
 # ISO 15.2.26
index 65dd0d6..be98f21 100644 (file)
@@ -1,3 +1,4 @@
+# coding: utf-8
 ##
 # Array
 #
@@ -12,15 +13,10 @@ class Array
   def each(&block)
     return to_enum :each unless block_given?
 
-    idx, length = -1, self.length-1
-    while idx < length and length <= self.length and length = self.length-1
-      elm = self[idx += 1]
-      unless elm
-        if elm.nil? and length >= self.length
-          break
-        end
-      end
-      block.call(elm)
+    idx = 0
+    while idx < length
+      block.call(self[idx])
+      idx += 1
     end
     self
   end
@@ -198,9 +194,45 @@ class Array
   include Enumerable
 
   ##
+  # Quick sort
+  # a     : the array to sort
+  # left  : the beginning of sort region
+  # right : the end of sort region
+  def __sort_sub__(a, left, right, &block)
+    if left < right
+      i = left
+      j = right
+      pivot = a[i + (j - i) / 2]
+      while true
+        while ((block)? block.call(a[i], pivot): (a[i] <=> pivot)) < 0
+          i += 1
+        end
+        while ((block)? block.call(pivot, a[j]): (pivot <=> a[j])) < 0
+          j -= 1
+        end
+        break if (i >= j)
+        tmp = a[i]; a[i] = a[j]; a[j] = tmp;
+        i += 1
+        j -= 1
+      end
+      __sort_sub__(a, left, i-1, &block)
+      __sort_sub__(a, j+1, right, &block)
+    end
+  end
+  #  private :__sort_sub__
+
+  ##
   # Sort all elements and replace +self+ with these
   # elements.
   def sort!(&block)
-    self.replace(self.sort(&block))
+    size = self.size
+    if size > 1
+      __sort_sub__(self, 0, size - 1, &block)
+    end
+    self
+  end
+
+  def sort(&block)
+    self.dup.sort!(&block)
   end
 end
diff --git a/third-party/mruby/mrblib/class.rb b/third-party/mruby/mrblib/class.rb
deleted file mode 100644 (file)
index 39e0d50..0000000
+++ /dev/null
@@ -1,11 +0,0 @@
-class Module
-   # 15.2.2.4.12
-  def attr_accessor(*names)
-    attr_reader(*names)
-    attr_writer(*names)
-  end
-  # 15.2.2.4.11
-  def attr(name)
-    attr_reader(name)
-  end
-end
index 650d243..12bd1d3 100644 (file)
@@ -316,45 +316,6 @@ module Enumerable
   alias select find_all
 
   ##
-  # TODO
-  # Does this OK? Please test it.
-  def __sort_sub__(sorted, work, src_ary, head, tail, &block)
-    if head == tail
-      sorted[head] = work[head] if src_ary == 1
-      return
-    end
-
-    # on current step, which is a src ary?
-    if src_ary == 0
-      src, dst = sorted, work
-    else
-      src, dst = work, sorted
-    end
-
-    key = src[head]    # key value for dividing values
-    i, j = head, tail  # position to store on the dst ary
-
-    (head + 1).upto(tail){|idx|
-      if ((block)? block.call(src[idx], key): (src[idx] <=> key)) > 0
-        # larger than key
-        dst[j] = src[idx]
-        j -= 1
-      else
-        dst[i] = src[idx]
-        i += 1
-      end
-    }
-
-    sorted[i] = key
-
-    # sort each sub-array
-    src_ary = (src_ary + 1) % 2  # exchange a src ary
-    __sort_sub__(sorted, work, src_ary, head, i - 1, &block) if i > head
-    __sort_sub__(sorted, work, src_ary, i + 1, tail, &block) if i < tail
-  end
-#  private :__sort_sub__
-
-  ##
   # Return a sorted array of all elements
   # which are yield by +each+. If no block
   # is given <=> will be invoked on each
@@ -364,12 +325,7 @@ module Enumerable
   #
   # ISO 15.3.2.2.19
   def sort(&block)
-    ary = []
-    self.each{|*val| ary.push(val.__svalue)}
-    if ary.size > 1
-      __sort_sub__(ary, ::Array.new(ary.size), 0, 0, ary.size - 1, &block)
-    end
-    ary
+    self.map{|*val| val.__svalue}.sort
   end
 
   ##
@@ -383,7 +339,7 @@ module Enumerable
     h = 12347
     i = 0
     self.each do |e|
-      n = e.hash << (i % 16)
+      n = (e.hash & (0x7fffffff >> (i % 16))) << (i % 16)
       h ^= n
       i += 1
     end
index e3e7090..4727b08 100644 (file)
@@ -154,6 +154,7 @@ class Hash
   #
   # ISO 15.2.13.4.23
   def replace(hash)
+    raise TypeError, "can't convert argument into Hash" unless hash.respond_to?(:to_hash)
     self.clear
     hash = hash.to_hash
     hash.each_key{|k|
@@ -200,7 +201,7 @@ class Hash
     }.join(", ")+"}"
   end
   ##
-  # Return the contents of this hash as a string. 
+  # Return the contents of this hash as a string.
  #
   # ISO 15.2.13.4.30 (x)
   def inspect
index dc902e9..4d4bcd2 100644 (file)
@@ -1,5 +1,5 @@
-#include "mruby.h"
-#include "mruby/irep.h"
+#include <mruby.h>
+#include <mruby/irep.h>
 
 extern const uint8_t mrblib_irep[];
 
index 6e4c502..975ad97 100644 (file)
@@ -100,11 +100,18 @@ module Integral
   # Calls the given block from +self+ to +num+
   # incremented by +step+ (default 1).
   #
-  def step(num, step = 1, &block)
+  def step(num=nil, step=1, &block)
     raise ArgumentError, "step can't be 0" if step == 0
     return to_enum(:step, num, step) unless block_given?
 
     i = if num.kind_of? Float then self.to_f else self end
+    if num == nil
+      while true
+        block.call(i)
+        i+=step
+      end
+      return self
+    end
     if step > 0
       while i <= num
         block.call(i)
@@ -160,35 +167,7 @@ end
 #
 # ISO 15.2.9
 class Float
-  include Integral
   # mruby special - since mruby integers may be upgraded to floats,
   # floats should be compatible to integers.
-  def >> other
-    n = self.to_i
-    other = other.to_i
-    if other < 0
-      n << -other
-    else
-      other.times { n /= 2 }
-      if n.abs < 1
-        if n >= 0
-          0
-        else
-          -1
-        end
-      else
-        n.to_i
-      end
-    end
-  end
-  def << other
-    n = self.to_i
-    other = other.to_i
-    if other < 0
-      n >> -other
-    else
-      other.times { n *= 2 }
-      n
-    end
-  end
+  include Integral
 end
index 5e5fd9b..3322af5 100644 (file)
@@ -26,6 +26,14 @@ class Range
       return self
     end
 
+    if val.kind_of?(String) && last.kind_of?(String) # fixnums are special
+      if val.respond_to? :upto
+        return val.upto(last, exclude_end?, &block)
+      else
+        str_each = true
+      end
+    end
+
     raise TypeError, "can't iterate" unless val.respond_to? :succ
 
     return self if (val <=> last) > 0
@@ -33,6 +41,9 @@ class Range
     while (val <=> last) < 0
       block.call(val)
       val = val.succ
+      if str_each
+        break if val.size > last.size
+      end
     end
 
     block.call(val) if !exclude_end? && (val <=> last) == 0
index 37441ec..4c6114e 100644 (file)
@@ -9,13 +9,18 @@ class String
   # and pass the respective line.
   #
   # ISO 15.2.10.5.15
-  def each_line(&block)
+  def each_line(rs = "\n", &block)
+    return to_enum(:each_line, rs, &block) unless block
+    return block.call(self) if rs.nil?
+    rs = rs.to_str
     offset = 0
-    while pos = self.index("\n", offset)
-      block.call(self[offset, pos + 1 - offset])
-      offset = pos + 1
+    rs_len = rs.length
+    this = dup
+    while pos = this.index(rs, offset)
+      block.call(this[offset, pos + rs_len - offset])
+      offset = pos + rs_len
     end
-    block.call(self[offset, self.size - offset]) if self.size > offset
+    block.call(this[offset, this.size - offset]) if this.size > offset
     self
   end
 
@@ -34,6 +39,8 @@ class String
             m
           when "'"
             post
+          when "1", "2", "3", "4", "5", "6", "7", "8", "9"
+            ""
           else
             self[j, 2]
           end
@@ -51,23 +58,34 @@ class String
   #
   # ISO 15.2.10.5.18
   def gsub(*args, &block)
-    if args.size == 2
-      s = ""
-      i = 0
-      while j = index(args[0], i)
-        seplen = args[0].length
-        k = j + seplen
-        pre = self[0, j]
-        post = self[k, length-k]
-        s += self[i, j-i] + args[1].__sub_replace(pre, args[0], post)
-        i = k
+    return to_enum(:gsub, *args) if args.length == 1 && !block
+    raise ArgumentError, "wrong number of arguments" unless (1..2).include?(args.length)
+
+    pattern, replace = *args
+    plen = pattern.length
+    if args.length == 2 && block
+      block = nil
+    end
+    if !replace.nil? || !block
+      replace = replace.to_str
+    end
+    offset = 0
+    result = []
+    while found = index(pattern, offset)
+      result << self[offset, found - offset]
+      offset = found + plen
+      result << if block
+        block.call(pattern).to_s
+      else
+        replace.__sub_replace(self[0, found], pattern, self[offset..-1] || "")
+      end
+      if plen == 0
+        result << self[offset, 1]
+        offset += 1
       end
-      s + self[i, length-i]
-    elsif args.size == 1 && block
-      split(args[0], -1).join(block.call(args[0]))
-    else
-      raise ArgumentError, "wrong number of arguments"
     end
+    result << self[offset..-1] if offset < length
+    result.join
   end
 
   ##
@@ -78,6 +96,8 @@ class String
   #
   # ISO 15.2.10.5.19
   def gsub!(*args, &block)
+    raise RuntimeError, "can't modify frozen String" if frozen?
+    return to_enum(:gsub!, *args) if args.length == 1 && !block
     str = self.gsub(*args, &block)
     return nil if str == self
     self.replace(str)
@@ -104,15 +124,31 @@ class String
   #
   # ISO 15.2.10.5.36
   def sub(*args, &block)
-    if args.size == 2
-      pre, post = split(args[0], 2)
-      return self unless post # The sub target wasn't found in the string
-      pre + args[1].__sub_replace(pre, args[0], post) + post
-    elsif args.size == 1 && block
-      split(args[0], 2).join(block.call(args[0]))
+    unless (1..2).include?(args.length)
+      raise ArgumentError, "wrong number of arguments (given #{args.length}, expected 2)"
+    end
+
+    pattern, replace = *args
+    pattern = pattern.to_str
+    if args.length == 2 && block
+      block = nil
+    end
+    unless block
+      replace = replace.to_str
+    end
+    result = []
+    this = dup
+    found = index(pattern)
+    return this unless found
+    result << this[0, found]
+    offset = found + pattern.length
+    result << if block
+      block.call(pattern).to_s
     else
-      raise ArgumentError, "wrong number of arguments"
+      replace.__sub_replace(this[0, found], pattern, this[offset..-1] || "")
     end
+    result << this[offset..-1] if offset < length
+    result.join
   end
 
   ##
@@ -123,6 +159,7 @@ class String
   #
   # ISO 15.2.10.5.37
   def sub!(*args, &block)
+    raise RuntimeError, "can't modify frozen String" if frozen?
     str = self.sub(*args, &block)
     return nil if str == self
     self.replace(str)
@@ -159,16 +196,24 @@ class String
     anum = args.size
     if anum == 2
       pos, value = args
-      if pos.kind_of? String
+      case pos
+      when String
         posnum = self.index(pos)
         if posnum
           b = self[0, posnum.to_i]
           a = self[(posnum + pos.length)..-1]
           self.replace([b, value, a].join(''))
-          return value
         else
           raise IndexError, "string not matched"
         end
+      when Range
+        head = pos.begin
+        tail = pos.end
+        tail += self.length if tail < 0
+        unless pos.exclude_end?
+          tail += 1
+        end
+        return self[head, tail-head]=value
       else
         pos += self.length if pos < 0
         if pos < 0 || pos > self.length
@@ -177,8 +222,8 @@ class String
         b = self[0, pos.to_i]
         a = self[pos + 1..-1]
         self.replace([b, value, a].join(''))
-        return value
       end
+      return value
     elsif anum == 3
       pos, len, value = args
       pos += self.length if pos < 0
index 2ef9c2a..9124ff9 100644 (file)
@@ -4,11 +4,11 @@
 ** See Copyright Notice in mruby.h
 */
 
-#include "mruby.h"
-#include "mruby/array.h"
-#include "mruby/class.h"
-#include "mruby/string.h"
-#include "mruby/range.h"
+#include <mruby.h>
+#include <mruby/array.h>
+#include <mruby/class.h>
+#include <mruby/string.h>
+#include <mruby/range.h>
 #include "value_array.h"
 
 #define ARY_DEFAULT_LEN   4
 #define ARY_C_MAX_SIZE (SIZE_MAX / sizeof(mrb_value))
 #define ARY_MAX_SIZE ((ARY_C_MAX_SIZE < (size_t)MRB_INT_MAX) ? (mrb_int)ARY_C_MAX_SIZE : MRB_INT_MAX-1)
 
-static inline mrb_value
-ary_elt(mrb_value ary, mrb_int offset)
-{
-  if (offset < 0 || RARRAY_LEN(ary) <= offset) {
-    return mrb_nil_value();
-  }
-  return RARRAY_PTR(ary)[offset];
-}
-
 static struct RArray*
 ary_new_capa(mrb_state *mrb, mrb_int capa)
 {
   struct RArray *a;
-  mrb_int blen;
+  size_t blen;
 
   if (capa > ARY_MAX_SIZE) {
     mrb_raise(mrb, E_ARGUMENT_ERROR, "array size too big");
   }
   blen = capa * sizeof(mrb_value);
-  if (blen < capa) {
-    mrb_raise(mrb, E_ARGUMENT_ERROR, "array size too big");
-  }
 
   a = (struct RArray*)mrb_obj_alloc(mrb, MRB_TT_ARRAY, mrb->array_class);
-  a->ptr = (mrb_value *)mrb_malloc(mrb, blen);
-  a->aux.capa = capa;
-  a->len = 0;
+  if (capa <= MRB_ARY_EMBED_LEN_MAX) {
+    ARY_SET_EMBED_FLAG(a);
+    /* ARY_SET_EMBED_LEN(a, 0); */
+  }
+  else {
+    a->as.heap.ptr = (mrb_value *)mrb_malloc(mrb, blen);
+    a->as.heap.aux.capa = capa;
+    a->as.heap.len = 0;
+  }
 
   return a;
 }
@@ -89,8 +83,8 @@ mrb_ary_new_from_values(mrb_state *mrb, mrb_int size, const mrb_value *vals)
 {
   struct RArray *a = ary_new_capa(mrb, size);
 
-  array_copy(a->ptr, vals, size);
-  a->len = size;
+  array_copy(ARY_PTR(a), vals, size);
+  ARY_SET_LEN(a, size);
 
   return mrb_obj_value(a);
 }
@@ -101,9 +95,9 @@ mrb_assoc_new(mrb_state *mrb, mrb_value car, mrb_value cdr)
   struct RArray *a;
 
   a = ary_new_capa(mrb, 2);
-  a->ptr[0] = car;
-  a->ptr[1] = cdr;
-  a->len = 2;
+  ARY_PTR(a)[0] = car;
+  ARY_PTR(a)[1] = cdr;
+  ARY_SET_LEN(a, 2);
   return mrb_obj_value(a);
 }
 
@@ -118,28 +112,38 @@ ary_fill_with_nil(mrb_value *ptr, mrb_int size)
 }
 
 static void
+ary_modify_check(mrb_state *mrb, struct RArray *a)
+{
+  if (MRB_FROZEN_P(a)) {
+    mrb_raise(mrb, E_RUNTIME_ERROR, "can't modify frozen array");
+  }
+}
+
+static void
 ary_modify(mrb_state *mrb, struct RArray *a)
 {
+  ary_modify_check(mrb, a);
+
   if (ARY_SHARED_P(a)) {
-    mrb_shared_array *shared = a->aux.shared;
+    mrb_shared_array *shared = a->as.heap.aux.shared;
 
-    if (shared->refcnt == 1 && a->ptr == shared->ptr) {
-      a->ptr = shared->ptr;
-      a->aux.capa = a->len;
+    if (shared->refcnt == 1 && a->as.heap.ptr == shared->ptr) {
+      a->as.heap.ptr = shared->ptr;
+      a->as.heap.aux.capa = a->as.heap.len;
       mrb_free(mrb, shared);
     }
     else {
       mrb_value *ptr, *p;
       mrb_int len;
 
-      p = a->ptr;
-      len = a->len * sizeof(mrb_value);
+      p = a->as.heap.ptr;
+      len = a->as.heap.len * sizeof(mrb_value);
       ptr = (mrb_value *)mrb_malloc(mrb, len);
       if (p) {
-        array_copy(ptr, p, a->len);
+        array_copy(ptr, p, a->as.heap.len);
       }
-      a->ptr = ptr;
-      a->aux.capa = a->len;
+      a->as.heap.ptr = ptr;
+      a->as.heap.aux.capa = a->as.heap.len;
       mrb_ary_decref(mrb, shared);
     }
     ARY_UNSET_SHARED_FLAG(a);
@@ -156,18 +160,20 @@ mrb_ary_modify(mrb_state *mrb, struct RArray* a)
 static void
 ary_make_shared(mrb_state *mrb, struct RArray *a)
 {
-  if (!ARY_SHARED_P(a)) {
+  if (!ARY_SHARED_P(a) && !ARY_EMBED_P(a)) {
     mrb_shared_array *shared = (mrb_shared_array *)mrb_malloc(mrb, sizeof(mrb_shared_array));
+    mrb_value *ptr = a->as.heap.ptr;
+    mrb_int len = a->as.heap.len;
 
     shared->refcnt = 1;
-    if (a->aux.capa > a->len) {
-      a->ptr = shared->ptr = (mrb_value *)mrb_realloc(mrb, a->ptr, sizeof(mrb_value)*a->len+1);
+    if (a->as.heap.aux.capa > len) {
+      a->as.heap.ptr = shared->ptr = (mrb_value *)mrb_realloc(mrb, ptr, sizeof(mrb_value)*len+1);
     }
     else {
-      shared->ptr = a->ptr;
+      shared->ptr = ptr;
     }
-    shared->len = a->len;
-    a->aux.shared = shared;
+    shared->len = len;
+    a->as.heap.aux.shared = shared;
     ARY_SET_SHARED_FLAG(a);
   }
 }
@@ -175,36 +181,58 @@ ary_make_shared(mrb_state *mrb, struct RArray *a)
 static void
 ary_expand_capa(mrb_state *mrb, struct RArray *a, mrb_int len)
 {
-  mrb_int capa = a->aux.capa;
+  mrb_int capa = ARY_CAPA(a);
 
-  if (len > ARY_MAX_SIZE) {
+  if (len > ARY_MAX_SIZE || len < 0) {
+  size_error:
     mrb_raise(mrb, E_ARGUMENT_ERROR, "array size too big");
   }
 
-  if (capa == 0) {
+  if (capa < ARY_DEFAULT_LEN) {
     capa = ARY_DEFAULT_LEN;
   }
   while (capa < len) {
-    capa *= 2;
+    if (capa <= ARY_MAX_SIZE / 2) {
+      capa *= 2;
+    }
+    else {
+      capa = len;
+    }
+  }
+  if (capa < len || capa > ARY_MAX_SIZE) {
+    goto size_error;
   }
 
-  if (capa > ARY_MAX_SIZE) capa = ARY_MAX_SIZE; /* len <= capa <= ARY_MAX_SIZE */
+  if (ARY_EMBED_P(a)) {
+    mrb_value *ptr = ARY_EMBED_PTR(a);
+    mrb_int len = ARY_EMBED_LEN(a);
+    mrb_value *expanded_ptr = (mrb_value *)mrb_malloc(mrb, sizeof(mrb_value)*capa);
 
-  if (capa > a->aux.capa) {
-    mrb_value *expanded_ptr = (mrb_value *)mrb_realloc(mrb, a->ptr, sizeof(mrb_value)*capa);
+    ARY_UNSET_EMBED_FLAG(a);
+    array_copy(expanded_ptr, ptr, len);
+    a->as.heap.len = len;
+    a->as.heap.aux.capa = capa;
+    a->as.heap.ptr = expanded_ptr;
+  }
+  else if (capa > a->as.heap.aux.capa) {
+    mrb_value *expanded_ptr = (mrb_value *)mrb_realloc(mrb, a->as.heap.ptr, sizeof(mrb_value)*capa);
 
-    a->aux.capa = capa;
-    a->ptr = expanded_ptr;
+    a->as.heap.aux.capa = capa;
+    a->as.heap.ptr = expanded_ptr;
   }
 }
 
 static void
 ary_shrink_capa(mrb_state *mrb, struct RArray *a)
 {
-  mrb_int capa = a->aux.capa;
+  
+  mrb_int capa;
+
+  if (ARY_EMBED_P(a)) return;
 
+  capa = a->as.heap.aux.capa;
   if (capa < ARY_DEFAULT_LEN * 2) return;
-  if (capa <= a->len * ARY_SHRINK_RATIO) return;
+  if (capa <= a->as.heap.len * ARY_SHRINK_RATIO) return;
 
   do {
     capa /= 2;
@@ -212,11 +240,11 @@ ary_shrink_capa(mrb_state *mrb, struct RArray *a)
       capa = ARY_DEFAULT_LEN;
       break;
     }
-  } while (capa > a->len * ARY_SHRINK_RATIO);
+  } while (capa > a->as.heap.len * ARY_SHRINK_RATIO);
 
-  if (capa > a->len && capa < a->aux.capa) {
-    a->aux.capa = capa;
-    a->ptr = (mrb_value *)mrb_realloc(mrb, a->ptr, sizeof(mrb_value)*capa);
+  if (capa > a->as.heap.len && capa < a->as.heap.aux.capa) {
+    a->as.heap.aux.capa = capa;
+    a->as.heap.ptr = (mrb_value *)mrb_realloc(mrb, a->as.heap.ptr, sizeof(mrb_value)*capa);
   }
 }
 
@@ -229,13 +257,13 @@ mrb_ary_resize(mrb_state *mrb, mrb_value ary, mrb_int new_len)
   ary_modify(mrb, a);
   old_len = RARRAY_LEN(ary);
   if (old_len != new_len) {
-    a->len = new_len;
+    ARY_SET_LEN(a, new_len);
     if (new_len < old_len) {
       ary_shrink_capa(mrb, a);
     }
     else {
       ary_expand_capa(mrb, a, new_len);
-      ary_fill_with_nil(a->ptr + old_len, new_len - old_len);
+      ary_fill_with_nil(ARY_PTR(a) + old_len, new_len - old_len);
     }
   }
 
@@ -243,26 +271,38 @@ mrb_ary_resize(mrb_state *mrb, mrb_value ary, mrb_int new_len)
 }
 
 static mrb_value
-mrb_ary_s_create(mrb_state *mrb, mrb_value self)
+mrb_ary_s_create(mrb_state *mrb, mrb_value klass)
 {
+  mrb_value ary;
   mrb_value *vals;
   mrb_int len;
+  struct RArray *a;
 
-  mrb_get_args(mrb, "*", &vals, &len);
+  mrb_get_args(mrb, "*!", &vals, &len);
+  ary = mrb_ary_new_from_values(mrb, len, vals);
+  a = mrb_ary_ptr(ary);
+  a->c = mrb_class_ptr(klass);
 
-  return mrb_ary_new_from_values(mrb, len, vals);
+  return ary;
 }
 
 static void
-ary_concat(mrb_state *mrb, struct RArray *a, mrb_value *ptr, mrb_int blen)
+ary_concat(mrb_state *mrb, struct RArray *a, struct RArray *a2)
 {
-  mrb_int len = a->len + blen;
+  mrb_int len;
+
+  if (ARY_LEN(a2) > ARY_MAX_SIZE - ARY_LEN(a)) {
+    mrb_raise(mrb, E_ARGUMENT_ERROR, "array size too big");
+  }
+  len = ARY_LEN(a) + ARY_LEN(a2);
 
   ary_modify(mrb, a);
-  if (a->aux.capa < len) ary_expand_capa(mrb, a, len);
-  array_copy(a->ptr+a->len, ptr, blen);
+  if (ARY_CAPA(a) < len) {
+    ary_expand_capa(mrb, a, len);
+  }
+  array_copy(ARY_PTR(a)+ARY_LEN(a), ARY_PTR(a2), ARY_LEN(a2));
   mrb_write_barrier(mrb, (struct RBasic*)a);
-  a->len = len;
+  ARY_SET_LEN(a, len);
 }
 
 MRB_API void
@@ -270,17 +310,16 @@ mrb_ary_concat(mrb_state *mrb, mrb_value self, mrb_value other)
 {
   struct RArray *a2 = mrb_ary_ptr(other);
 
-  ary_concat(mrb, mrb_ary_ptr(self), a2->ptr, a2->len);
+  ary_concat(mrb, mrb_ary_ptr(self), a2);
 }
 
 static mrb_value
 mrb_ary_concat_m(mrb_state *mrb, mrb_value self)
 {
-  mrb_value *ptr;
-  mrb_int blen;
+  mrb_value ary;
 
-  mrb_get_args(mrb, "a", &ptr, &blen);
-  ary_concat(mrb, mrb_ary_ptr(self), ptr, blen);
+  mrb_get_args(mrb, "A", &ary);
+  mrb_ary_concat(mrb, self, ary);
   return self;
 }
 
@@ -290,16 +329,17 @@ mrb_ary_plus(mrb_state *mrb, mrb_value self)
   struct RArray *a1 = mrb_ary_ptr(self);
   struct RArray *a2;
   mrb_value *ptr;
-  mrb_int blen;
+  mrb_int blen, len1;
 
   mrb_get_args(mrb, "a", &ptr, &blen);
-  if (ARY_MAX_SIZE - blen < a1->len) {
+  if (ARY_MAX_SIZE - blen < ARY_LEN(a1)) {
     mrb_raise(mrb, E_ARGUMENT_ERROR, "array size too big");
   }
-  a2 = ary_new_capa(mrb, a1->len + blen);
-  array_copy(a2->ptr, a1->ptr, a1->len);
-  array_copy(a2->ptr + a1->len, ptr, blen);
-  a2->len = a1->len + blen;
+  len1 = ARY_LEN(a1);
+  a2 = ary_new_capa(mrb, len1 + blen);
+  array_copy(ARY_PTR(a2), ARY_PTR(a1), len1);
+  array_copy(ARY_PTR(a2) + len1, ptr, blen);
+  ARY_SET_LEN(a2, len1+blen);
 
   return mrb_obj_value(a2);
 }
@@ -308,19 +348,22 @@ static void
 ary_replace(mrb_state *mrb, struct RArray *a, mrb_value *argv, mrb_int len)
 {
   ary_modify(mrb, a);
-  if (a->aux.capa < len)
+  if (ARY_CAPA(a) < len)
     ary_expand_capa(mrb, a, len);
-  array_copy(a->ptr, argv, len);
+  array_copy(ARY_PTR(a), argv, len);
   mrb_write_barrier(mrb, (struct RBasic*)a);
-  a->len = len;
+  ARY_SET_LEN(a, len);
 }
 
 MRB_API void
 mrb_ary_replace(mrb_state *mrb, mrb_value self, mrb_value other)
 {
+  struct RArray *a1 = mrb_ary_ptr(self);
   struct RArray *a2 = mrb_ary_ptr(other);
 
-  ary_replace(mrb, mrb_ary_ptr(self), a2->ptr, a2->len);
+  if (a1 != a2) {
+    ary_replace(mrb, a1, ARY_PTR(a2), ARY_LEN(a2));
+  }
 }
 
 static mrb_value
@@ -340,22 +383,23 @@ mrb_ary_times(mrb_state *mrb, mrb_value self)
   struct RArray *a1 = mrb_ary_ptr(self);
   struct RArray *a2;
   mrb_value *ptr;
-  mrb_int times;
+  mrb_int times, len1;
 
   mrb_get_args(mrb, "i", &times);
   if (times < 0) {
     mrb_raise(mrb, E_ARGUMENT_ERROR, "negative argument");
   }
   if (times == 0) return mrb_ary_new(mrb);
-  if (ARY_MAX_SIZE / times < a1->len) {
+  if (ARY_MAX_SIZE / times < ARY_LEN(a1)) {
     mrb_raise(mrb, E_ARGUMENT_ERROR, "array size too big");
   }
-  a2 = ary_new_capa(mrb, a1->len * times);
-  ptr = a2->ptr;
+  len1 = ARY_LEN(a1);
+  a2 = ary_new_capa(mrb, len1 * times);
+  ARY_SET_LEN(a2, len1 * times);
+  ptr = ARY_PTR(a2);
   while (times--) {
-    array_copy(ptr, a1->ptr, a1->len);
-    ptr += a1->len;
-    a2->len += a1->len;
+    array_copy(ptr, ARY_PTR(a1), len1);
+    ptr += len1;
   }
 
   return mrb_obj_value(a2);
@@ -365,13 +409,14 @@ static mrb_value
 mrb_ary_reverse_bang(mrb_state *mrb, mrb_value self)
 {
   struct RArray *a = mrb_ary_ptr(self);
+  mrb_int len = ARY_LEN(a);
 
-  if (a->len > 1) {
+  if (len > 1) {
     mrb_value *p1, *p2;
 
     ary_modify(mrb, a);
-    p1 = a->ptr;
-    p2 = a->ptr + a->len - 1;
+    p1 = ARY_PTR(a);
+    p2 = p1 + len - 1;
 
     while (p1 < p2) {
       mrb_value tmp = *p1;
@@ -385,18 +430,19 @@ mrb_ary_reverse_bang(mrb_state *mrb, mrb_value self)
 static mrb_value
 mrb_ary_reverse(mrb_state *mrb, mrb_value self)
 {
-  struct RArray *a = mrb_ary_ptr(self), *b = ary_new_capa(mrb, a->len);
+  struct RArray *a = mrb_ary_ptr(self), *b = ary_new_capa(mrb, ARY_LEN(a));
+  mrb_int len = ARY_LEN(a);
 
-  if (a->len > 0) {
+  if (len > 0) {
     mrb_value *p1, *p2, *e;
 
-    p1 = a->ptr;
-    e  = p1 + a->len;
-    p2 = b->ptr + a->len - 1;
+    p1 = ARY_PTR(a);
+    e  = p1 + len;
+    p2 = ARY_PTR(b) + len - 1;
     while (p1 < e) {
       *p2-- = *p1++;
     }
-    b->len = a->len;
+    ARY_SET_LEN(b, len);
   }
   return mrb_obj_value(b);
 }
@@ -405,11 +451,13 @@ MRB_API void
 mrb_ary_push(mrb_state *mrb, mrb_value ary, mrb_value elem)
 {
   struct RArray *a = mrb_ary_ptr(ary);
+  mrb_int len = ARY_LEN(a);
 
   ary_modify(mrb, a);
-  if (a->len == a->aux.capa)
-    ary_expand_capa(mrb, a, a->len + 1);
-  a->ptr[a->len++] = elem;
+  if (len == ARY_CAPA(a))
+    ary_expand_capa(mrb, a, len + 1);
+  ARY_PTR(a)[len] = elem;
+  ARY_SET_LEN(a, len+1);
   mrb_field_write_barrier_value(mrb, (struct RBasic*)a, elem);
 }
 
@@ -419,7 +467,7 @@ mrb_ary_push_m(mrb_state *mrb, mrb_value self)
   mrb_value *argv;
   mrb_int len;
 
-  mrb_get_args(mrb, "*", &argv, &len);
+  mrb_get_args(mrb, "*!", &argv, &len);
   while (len--) {
     mrb_ary_push(mrb, self, *argv++);
   }
@@ -431,9 +479,12 @@ MRB_API mrb_value
 mrb_ary_pop(mrb_state *mrb, mrb_value ary)
 {
   struct RArray *a = mrb_ary_ptr(ary);
+  mrb_int len = ARY_LEN(a);
 
-  if (a->len == 0) return mrb_nil_value();
-  return a->ptr[--a->len];
+  ary_modify_check(mrb, a);
+  if (len == 0) return mrb_nil_value();
+  ARY_SET_LEN(a, len-1);
+  return ARY_PTR(a)[len-1];
 }
 
 #define ARY_SHIFT_SHARED_MIN 10
@@ -442,30 +493,32 @@ MRB_API mrb_value
 mrb_ary_shift(mrb_state *mrb, mrb_value self)
 {
   struct RArray *a = mrb_ary_ptr(self);
+  mrb_int len = ARY_LEN(a);
   mrb_value val;
 
-  if (a->len == 0) return mrb_nil_value();
+  ary_modify_check(mrb, a);
+  if (len == 0) return mrb_nil_value();
   if (ARY_SHARED_P(a)) {
   L_SHIFT:
-    val = a->ptr[0];
-    a->ptr++;
-    a->len--;
+    val = a->as.heap.ptr[0];
+    a->as.heap.ptr++;
+    a->as.heap.len--;
     return val;
   }
-  if (a->len > ARY_SHIFT_SHARED_MIN) {
+  if (len > ARY_SHIFT_SHARED_MIN) {
     ary_make_shared(mrb, a);
     goto L_SHIFT;
   }
   else {
-    mrb_value *ptr = a->ptr;
-    mrb_int size = a->len;
+    mrb_value *ptr = ARY_PTR(a);
+    mrb_int size = len;
 
     val = *ptr;
     while (--size) {
       *ptr = *(ptr+1);
       ++ptr;
     }
-    --a->len;
+    ARY_SET_LEN(a, len-1);
   }
   return val;
 }
@@ -478,21 +531,25 @@ MRB_API mrb_value
 mrb_ary_unshift(mrb_state *mrb, mrb_value self, mrb_value item)
 {
   struct RArray *a = mrb_ary_ptr(self);
+  mrb_int len = ARY_LEN(a);
 
   if (ARY_SHARED_P(a)
-      && a->aux.shared->refcnt == 1 /* shared only referenced from this array */
-      && a->ptr - a->aux.shared->ptr >= 1) /* there's room for unshifted item */ {
-    a->ptr--;
-    a->ptr[0] = item;
+      && a->as.heap.aux.shared->refcnt == 1 /* shared only referenced from this array */
+      && a->as.heap.ptr - a->as.heap.aux.shared->ptr >= 1) /* there's room for unshifted item */ {
+    a->as.heap.ptr--;
+    a->as.heap.ptr[0] = item;
   }
   else {
+    mrb_value *ptr;
+
     ary_modify(mrb, a);
-    if (a->aux.capa < a->len + 1)
-      ary_expand_capa(mrb, a, a->len + 1);
-    value_move(a->ptr + 1, a->ptr, a->len);
-    a->ptr[0] = item;
+    if (ARY_CAPA(a) < len + 1)
+      ary_expand_capa(mrb, a, len + 1);
+    ptr = ARY_PTR(a);
+    value_move(ptr + 1, ptr, len);
+    ptr[0] = item;
   }
-  a->len++;
+  ARY_SET_LEN(a, len+1);
   mrb_field_write_barrier_value(mrb, (struct RBasic*)a, item);
 
   return self;
@@ -502,24 +559,31 @@ static mrb_value
 mrb_ary_unshift_m(mrb_state *mrb, mrb_value self)
 {
   struct RArray *a = mrb_ary_ptr(self);
-  mrb_value *vals;
-  mrb_int len;
+  mrb_value *vals, *ptr;
+  mrb_int alen, len;
 
-  mrb_get_args(mrb, "*", &vals, &len);
+  mrb_get_args(mrb, "*!", &vals, &alen);
+  len = ARY_LEN(a);
+  if (alen > ARY_MAX_SIZE - len) {
+    mrb_raise(mrb, E_ARGUMENT_ERROR, "array size too big");
+  }
   if (ARY_SHARED_P(a)
-      && a->aux.shared->refcnt == 1 /* shared only referenced from this array */
-      && a->ptr - a->aux.shared->ptr >= len) /* there's room for unshifted item */ {
-    a->ptr -= len;
+      && a->as.heap.aux.shared->refcnt == 1 /* shared only referenced from this array */
+      && a->as.heap.ptr - a->as.heap.aux.shared->ptr >= alen) /* there's room for unshifted item */ {
+    ary_modify_check(mrb, a);
+    a->as.heap.ptr -= len;
+    ptr = a->as.heap.ptr;
   }
   else {
     ary_modify(mrb, a);
-    if (len == 0) return self;
-    if (a->aux.capa < a->len + len)
-      ary_expand_capa(mrb, a, a->len + len);
-    value_move(a->ptr + len, a->ptr, a->len);
+    if (alen == 0) return self;
+    if (ARY_CAPA(a) < len + alen)
+      ary_expand_capa(mrb, a, len + alen);
+    ptr = ARY_PTR(a);
+    value_move(ptr + alen, ptr, len);
   }
-  array_copy(a->ptr, vals, len);
-  a->len += len;
+  array_copy(ptr, vals, alen);
+  ARY_SET_LEN(a, len+alen);
   while (len--) {
     mrb_field_write_barrier_value(mrb, (struct RBasic*)a, vals[len]);
   }
@@ -531,45 +595,58 @@ MRB_API mrb_value
 mrb_ary_ref(mrb_state *mrb, mrb_value ary, mrb_int n)
 {
   struct RArray *a = mrb_ary_ptr(ary);
+  mrb_int len = ARY_LEN(a);
 
   /* range check */
-  if (n < 0) n += a->len;
-  if (n < 0 || a->len <= n) return mrb_nil_value();
+  if (n < 0) n += len;
+  if (n < 0 || len <= n) return mrb_nil_value();
 
-  return a->ptr[n];
+  return ARY_PTR(a)[n];
 }
 
 MRB_API void
 mrb_ary_set(mrb_state *mrb, mrb_value ary, mrb_int n, mrb_value val)
 {
   struct RArray *a = mrb_ary_ptr(ary);
+  mrb_int len = ARY_LEN(a);
 
   ary_modify(mrb, a);
   /* range check */
   if (n < 0) {
-    n += a->len;
+    n += len;
     if (n < 0) {
-      mrb_raisef(mrb, E_INDEX_ERROR, "index %S out of array", mrb_fixnum_value(n - a->len));
+      mrb_raisef(mrb, E_INDEX_ERROR, "index %S out of array", mrb_fixnum_value(n - len));
     }
   }
-  if (a->len <= n) {
-    if (a->aux.capa <= n)
+  if (len <= n) {
+    if (ARY_CAPA(a) <= n)
       ary_expand_capa(mrb, a, n + 1);
-    ary_fill_with_nil(a->ptr + a->len, n + 1 - a->len);
-    a->len = n + 1;
+    ary_fill_with_nil(ARY_PTR(a) + len, n + 1 - len);
+    ARY_SET_LEN(a, n+1);
   }
 
-  a->ptr[n] = val;
+  ARY_PTR(a)[n] = val;
   mrb_field_write_barrier_value(mrb, (struct RBasic*)a, val);
 }
 
+static struct RArray*
+ary_dup(mrb_state *mrb, struct RArray *a)
+{
+  mrb_int len = ARY_LEN(a);
+  struct RArray *d = ary_new_capa(mrb, len);
+
+  ary_replace(mrb, d, ARY_PTR(a), len);
+  return d;
+}
+
 MRB_API mrb_value
 mrb_ary_splice(mrb_state *mrb, mrb_value ary, mrb_int head, mrb_int len, mrb_value rpl)
 {
   struct RArray *a = mrb_ary_ptr(ary);
-  mrb_int tail, size;
+  mrb_int alen = ARY_LEN(a);
   const mrb_value *argv;
-  mrb_int i, argc;
+  mrb_int argc;
+  mrb_int tail;
 
   ary_modify(mrb, a);
 
@@ -578,45 +655,70 @@ mrb_ary_splice(mrb_state *mrb, mrb_value ary, mrb_int head, mrb_int len, mrb_val
 
   /* range check */
   if (head < 0) {
-    head += a->len;
+    head += alen;
     if (head < 0) {
       mrb_raise(mrb, E_INDEX_ERROR, "index is out of array");
     }
   }
-  if (a->len < len || a->len < head + len) {
-    len = a->len - head;
-  }
   tail = head + len;
+  if (alen < len || alen < tail) {
+    len = alen - head;
+  }
 
   /* size check */
   if (mrb_array_p(rpl)) {
     argc = RARRAY_LEN(rpl);
     argv = RARRAY_PTR(rpl);
+    if (argv == ARY_PTR(a)) {
+      struct RArray *r;
+
+      if (argc > 32767) {
+        mrb_raise(mrb, E_ARGUMENT_ERROR, "too big recursive splice");
+      }
+      r = ary_dup(mrb, a);
+      argv = ARY_PTR(r);
+    }
   }
   else {
     argc = 1;
     argv = &rpl;
   }
-  size = head + argc;
-
-  if (tail < a->len) size += a->len - tail;
-  if (size > a->aux.capa)
-    ary_expand_capa(mrb, a, size);
-
-  if (head > a->len) {
-    ary_fill_with_nil(a->ptr + a->len, head - a->len);
-  }
-  else if (head < a->len) {
-    value_move(a->ptr + head + argc, a->ptr + tail, a->len - tail);
-  }
-
-  for (i = 0; i < argc; i++) {
-    *(a->ptr + head + i) = *(argv + i);
-    mrb_field_write_barrier_value(mrb, (struct RBasic*)a, argv[i]);
+  if (head >= alen) {
+    if (head > ARY_MAX_SIZE - argc) {
+      mrb_raisef(mrb, E_INDEX_ERROR, "index %S too big", mrb_fixnum_value(head));
+    }
+    len = head + argc;
+    if (len > ARY_CAPA(a)) {
+      ary_expand_capa(mrb, a, head + argc);
+    }
+    ary_fill_with_nil(ARY_PTR(a) + alen, head - alen);
+    if (argc > 0) {
+      array_copy(ARY_PTR(a) + head, argv, argc);
+    }
+    ARY_SET_LEN(a, len);
   }
+  else {
+    mrb_int newlen;
 
-  a->len = size;
+    if (alen - len > ARY_MAX_SIZE - argc) {
+      mrb_raisef(mrb, E_INDEX_ERROR, "index %S too big", mrb_fixnum_value(alen + argc - len));
+    }
+    newlen = alen + argc - len;
+    if (newlen > ARY_CAPA(a)) {
+      ary_expand_capa(mrb, a, newlen);
+    }
 
+    if (len != argc) {
+      mrb_value *ptr = ARY_PTR(a);
+      tail = head + len;
+      value_move(ptr + head + argc, ptr + tail, alen - tail);
+      ARY_SET_LEN(a, newlen);
+    }
+    if (argc > 0) {
+      value_move(ARY_PTR(a) + head, argv, argc);
+    }
+  }
+  mrb_write_barrier(mrb, (struct RBasic*)a);
   return ary;
 }
 
@@ -635,12 +737,15 @@ ary_subseq(mrb_state *mrb, struct RArray *a, mrb_int beg, mrb_int len)
 {
   struct RArray *b;
 
+  if (!ARY_SHARED_P(a) && len <= ARY_SHIFT_SHARED_MIN) {
+    return mrb_ary_new_from_values(mrb, len, ARY_PTR(a)+beg);
+  }
   ary_make_shared(mrb, a);
   b  = (struct RArray*)mrb_obj_alloc(mrb, MRB_TT_ARRAY, mrb->array_class);
-  b->ptr = a->ptr + beg;
-  b->len = len;
-  b->aux.shared = a->aux.shared;
-  b->aux.shared->refcnt++;
+  b->as.heap.ptr = a->as.heap.ptr + beg;
+  b->as.heap.len = len;
+  b->as.heap.aux.shared = a->as.heap.aux.shared;
+  b->as.heap.aux.shared->refcnt++;
   ARY_SET_SHARED_FLAG(b);
 
   return mrb_obj_value(b);
@@ -659,7 +764,7 @@ aget_index(mrb_state *mrb, mrb_value index)
     mrb_int i, argc;
     mrb_value *argv;
 
-    mrb_get_args(mrb, "i*", &i, &argv, &argc);
+    mrb_get_args(mrb, "i*!", &i, &argv, &argc);
     return i;
   }
 }
@@ -695,14 +800,14 @@ static mrb_value
 mrb_ary_aget(mrb_state *mrb, mrb_value self)
 {
   struct RArray *a = mrb_ary_ptr(self);
-  mrb_int i, len;
+  mrb_int i, len, alen = ARY_LEN(a);
   mrb_value index;
 
   if (mrb_get_args(mrb, "o|i", &index, &len) == 1) {
     switch (mrb_type(index)) {
       /* a[n..m] */
     case MRB_TT_RANGE:
-      if (mrb_range_beg_len(mrb, index, &i, &len, a->len)) {
+      if (mrb_range_beg_len(mrb, index, &i, &len, alen, TRUE) == 1) {
         return ary_subseq(mrb, a, i, len);
       }
       else {
@@ -716,11 +821,11 @@ mrb_ary_aget(mrb_state *mrb, mrb_value self)
   }
 
   i = aget_index(mrb, index);
-  if (i < 0) i += a->len;
-  if (i < 0 || a->len < i) return mrb_nil_value();
+  if (i < 0) i += alen;
+  if (i < 0 || alen < i) return mrb_nil_value();
   if (len < 0) return mrb_nil_value();
-  if (a->len == i) return mrb_ary_new(mrb);
-  if (len > a->len - i) len = a->len - i;
+  if (alen == i) return mrb_ary_new(mrb);
+  if (len > alen - i) len = alen - i;
 
   return ary_subseq(mrb, a, i, len);
 }
@@ -766,20 +871,18 @@ mrb_ary_aset(mrb_state *mrb, mrb_value self)
   mrb_value v1, v2, v3;
   mrb_int i, len;
 
+  mrb_ary_modify(mrb, mrb_ary_ptr(self));
   if (mrb_get_args(mrb, "oo|o", &v1, &v2, &v3) == 2) {
-    switch (mrb_type(v1)) {
     /* a[n..m] = v */
-    case MRB_TT_RANGE:
-      if (mrb_range_beg_len(mrb, v1, &i, &len, RARRAY_LEN(self))) {
-        mrb_ary_splice(mrb, self, i, len, v2);
-      }
+    switch (mrb_range_beg_len(mrb, v1, &i, &len, RARRAY_LEN(self), FALSE)) {
+    case 0:                   /* not range */
+      mrb_ary_set(mrb, self, aget_index(mrb, v1), v2);
       break;
-    /* a[n] = v */
-    case MRB_TT_FIXNUM:
-      mrb_ary_set(mrb, self, mrb_fixnum(v1), v2);
+    case 1:                   /* range */
+      mrb_ary_splice(mrb, self, i, len, v2);
       break;
-    default:
-      mrb_ary_set(mrb, self, aget_index(mrb, v1), v2);
+    case 2:                   /* out of range */
+      mrb_raisef(mrb, E_RANGE_ERROR, "%S out of range", v1);
       break;
     }
     return v2;
@@ -797,22 +900,23 @@ mrb_ary_delete_at(mrb_state *mrb, mrb_value self)
   mrb_int   index;
   mrb_value val;
   mrb_value *ptr;
-  mrb_int len;
+  mrb_int len, alen = ARY_LEN(a);
 
   mrb_get_args(mrb, "i", &index);
-  if (index < 0) index += a->len;
-  if (index < 0 || a->len <= index) return mrb_nil_value();
+  if (index < 0) index += alen;
+  if (index < 0 || alen <= index) return mrb_nil_value();
 
   ary_modify(mrb, a);
-  val = a->ptr[index];
+  ptr = ARY_PTR(a);
+  val = ptr[index];
 
-  ptr = a->ptr + index;
-  len = a->len - index;
+  ptr += index;
+  len = alen - index;
   while (--len) {
     *ptr = *(ptr+1);
     ++ptr;
   }
-  --a->len;
+  ARY_SET_LEN(a, alen-1);
 
   ary_shrink_capa(mrb, a);
 
@@ -823,39 +927,39 @@ static mrb_value
 mrb_ary_first(mrb_state *mrb, mrb_value self)
 {
   struct RArray *a = mrb_ary_ptr(self);
-  mrb_int size;
+  mrb_int size, alen = ARY_LEN(a);
 
   if (mrb_get_args(mrb, "|i", &size) == 0) {
-    return (a->len > 0)? a->ptr[0]: mrb_nil_value();
+    return (alen > 0)? ARY_PTR(a)[0]: mrb_nil_value();
   }
   if (size < 0) {
     mrb_raise(mrb, E_ARGUMENT_ERROR, "negative array size");
   }
 
-  if (size > a->len) size = a->len;
+  if (size > alen) size = alen;
   if (ARY_SHARED_P(a)) {
     return ary_subseq(mrb, a, 0, size);
   }
-  return mrb_ary_new_from_values(mrb, size, a->ptr);
+  return mrb_ary_new_from_values(mrb, size, ARY_PTR(a));
 }
 
 static mrb_value
 mrb_ary_last(mrb_state *mrb, mrb_value self)
 {
   struct RArray *a = mrb_ary_ptr(self);
-  mrb_int size;
+  mrb_int size, alen = ARY_LEN(a);
 
   if (mrb_get_args(mrb, "|i", &size) == 0)
-    return (a->len > 0)? a->ptr[a->len - 1]: mrb_nil_value();
+    return (alen > 0)? ARY_PTR(a)[alen - 1]: mrb_nil_value();
 
   if (size < 0) {
     mrb_raise(mrb, E_ARGUMENT_ERROR, "negative array size");
   }
-  if (size > a->len) size = a->len;
+  if (size > alen) size = alen;
   if (ARY_SHARED_P(a) || size > ARY_DEFAULT_LEN) {
-    return ary_subseq(mrb, a, a->len - size, size);
+    return ary_subseq(mrb, a, alen - size, size);
   }
-  return mrb_ary_new_from_values(mrb, size, a->ptr + a->len - size);
+  return mrb_ary_new_from_values(mrb, size, ARY_PTR(a) + alen - size);
 }
 
 static mrb_value
@@ -877,13 +981,16 @@ static mrb_value
 mrb_ary_rindex_m(mrb_state *mrb, mrb_value self)
 {
   mrb_value obj;
-  mrb_int i;
+  mrb_int i, len;
 
   mrb_get_args(mrb, "o", &obj);
   for (i = RARRAY_LEN(self) - 1; i >= 0; i--) {
     if (mrb_equal(mrb, RARRAY_PTR(self)[i], obj)) {
       return mrb_fixnum_value(i);
     }
+    if (i > (len = RARRAY_LEN(self))) {
+      i = len;
+    }
   }
   return mrb_nil_value();
 }
@@ -891,15 +998,33 @@ mrb_ary_rindex_m(mrb_state *mrb, mrb_value self)
 MRB_API mrb_value
 mrb_ary_splat(mrb_state *mrb, mrb_value v)
 {
+  mrb_value a, recv_class;
+
   if (mrb_array_p(v)) {
     return v;
   }
-  if (mrb_respond_to(mrb, v, mrb_intern_lit(mrb, "to_a"))) {
-    return mrb_funcall(mrb, v, "to_a", 0);
+
+  if (!mrb_respond_to(mrb, v, mrb_intern_lit(mrb, "to_a"))) {
+    return mrb_ary_new_from_values(mrb, 1, &v);
   }
-  else {
+
+  a = mrb_funcall(mrb, v, "to_a", 0);
+  if (mrb_array_p(a)) {
+    return a;
+  }
+  else if (mrb_nil_p(a)) {
     return mrb_ary_new_from_values(mrb, 1, &v);
   }
+  else {
+    recv_class = mrb_obj_value(mrb_obj_class(mrb, v));
+    mrb_raisef(mrb, E_TYPE_ERROR, "can't convert %S to Array (%S#to_a gives %S)",
+      recv_class,
+      recv_class,
+      mrb_obj_value(mrb_obj_class(mrb, a))
+    );
+    /* not reached */
+    return mrb_undef_value();
+  }
 }
 
 static mrb_value
@@ -907,7 +1032,7 @@ mrb_ary_size(mrb_state *mrb, mrb_value self)
 {
   struct RArray *a = mrb_ary_ptr(self);
 
-  return mrb_fixnum_value(a->len);
+  return mrb_fixnum_value(ARY_LEN(a));
 }
 
 MRB_API mrb_value
@@ -915,16 +1040,16 @@ mrb_ary_clear(mrb_state *mrb, mrb_value self)
 {
   struct RArray *a = mrb_ary_ptr(self);
 
+  ary_modify(mrb, a);
   if (ARY_SHARED_P(a)) {
-    mrb_ary_decref(mrb, a->aux.shared);
+    mrb_ary_decref(mrb, a->as.heap.aux.shared);
     ARY_UNSET_SHARED_FLAG(a);
   }
-  else {
-    mrb_free(mrb, a->ptr);
+  else if (!ARY_EMBED_P(a)){
+    mrb_free(mrb, a->as.heap.ptr);
   }
-  a->len = 0;
-  a->aux.capa = 0;
-  a->ptr = 0;
+  ARY_SET_EMBED_FLAG(a);
+  ARY_SET_EMBED_LEN(a, 0);
 
   return self;
 }
@@ -934,7 +1059,7 @@ mrb_ary_empty_p(mrb_state *mrb, mrb_value self)
 {
   struct RArray *a = mrb_ary_ptr(self);
 
-  return mrb_bool_value(a->len == 0);
+  return mrb_bool_value(ARY_LEN(a) == 0);
 }
 
 MRB_API mrb_value
@@ -987,15 +1112,17 @@ join_ary(mrb_state *mrb, mrb_value ary, mrb_value sep, mrb_value list)
       break;
 
     default:
-      tmp = mrb_check_string_type(mrb, val);
-      if (!mrb_nil_p(tmp)) {
-        val = tmp;
-        goto str_join;
-      }
-      tmp = mrb_check_convert_type(mrb, val, MRB_TT_ARRAY, "Array", "to_ary");
-      if (!mrb_nil_p(tmp)) {
-        val = tmp;
-        goto ary_join;
+      if (!mrb_immediate_p(val)) {
+        tmp = mrb_check_string_type(mrb, val);
+        if (!mrb_nil_p(tmp)) {
+          val = tmp;
+          goto str_join;
+        }
+        tmp = mrb_check_convert_type(mrb, val, MRB_TT_ARRAY, "Array", "to_ary");
+        if (!mrb_nil_p(tmp)) {
+          val = tmp;
+          goto ary_join;
+        }
       }
       val = mrb_obj_as_string(mrb, val);
       goto str_join;
@@ -1010,7 +1137,9 @@ join_ary(mrb_state *mrb, mrb_value ary, mrb_value sep, mrb_value list)
 MRB_API mrb_value
 mrb_ary_join(mrb_state *mrb, mrb_value ary, mrb_value sep)
 {
-  sep = mrb_obj_as_string(mrb, sep);
+  if (!mrb_nil_p(sep)) {
+    sep = mrb_obj_as_string(mrb, sep);
+  }
   return join_ary(mrb, ary, sep, mrb_ary_new(mrb));
 }
 
@@ -1090,6 +1219,7 @@ mrb_init_array(mrb_state *mrb)
   mrb_define_method(mrb, a, "length",          mrb_ary_size,         MRB_ARGS_NONE()); /* 15.2.12.5.19 */
   mrb_define_method(mrb, a, "pop",             mrb_ary_pop,          MRB_ARGS_NONE()); /* 15.2.12.5.21 */
   mrb_define_method(mrb, a, "push",            mrb_ary_push_m,       MRB_ARGS_ANY());  /* 15.2.12.5.22 */
+  mrb_define_method(mrb, a, "append",          mrb_ary_push_m,       MRB_ARGS_ANY());
   mrb_define_method(mrb, a, "replace",         mrb_ary_replace_m,    MRB_ARGS_REQ(1)); /* 15.2.12.5.23 */
   mrb_define_method(mrb, a, "reverse",         mrb_ary_reverse,      MRB_ARGS_NONE()); /* 15.2.12.5.24 */
   mrb_define_method(mrb, a, "reverse!",        mrb_ary_reverse_bang, MRB_ARGS_NONE()); /* 15.2.12.5.25 */
@@ -1098,6 +1228,7 @@ mrb_init_array(mrb_state *mrb)
   mrb_define_method(mrb, a, "size",            mrb_ary_size,         MRB_ARGS_NONE()); /* 15.2.12.5.28 */
   mrb_define_method(mrb, a, "slice",           mrb_ary_aget,         MRB_ARGS_ANY());  /* 15.2.12.5.29 */
   mrb_define_method(mrb, a, "unshift",         mrb_ary_unshift_m,    MRB_ARGS_ANY());  /* 15.2.12.5.30 */
+  mrb_define_method(mrb, a, "prepend",         mrb_ary_unshift_m,    MRB_ARGS_ANY());
 
   mrb_define_method(mrb, a, "__ary_eq",        mrb_ary_eq,           MRB_ARGS_REQ(1));
   mrb_define_method(mrb, a, "__ary_cmp",       mrb_ary_cmp,          MRB_ARGS_REQ(1));
index cbf89e0..f043955 100644 (file)
@@ -4,99 +4,36 @@
 ** See Copyright Notice in mruby.h
 */
 
-#include "mruby.h"
-#include "mruby/variable.h"
-#include "mruby/proc.h"
-#include "mruby/array.h"
-#include "mruby/string.h"
-#include "mruby/class.h"
-#include "mruby/debug.h"
-#include "mruby/error.h"
-#include "mruby/numeric.h"
+#include <mruby.h>
+#include <mruby/variable.h>
+#include <mruby/proc.h>
+#include <mruby/array.h>
+#include <mruby/string.h>
+#include <mruby/class.h>
+#include <mruby/debug.h>
+#include <mruby/error.h>
+#include <mruby/numeric.h>
+#include <mruby/data.h>
 
 struct backtrace_location {
-  int i;
   int lineno;
   const char *filename;
-  const char *method;
-  const char *sep;
-  const char *class_name;
-};
-
-typedef void (*output_stream_func)(mrb_state*, struct backtrace_location*, void*);
-
-#ifndef MRB_DISABLE_STDIO
-
-struct print_backtrace_args {
-  FILE *stream;
-  int tracehead;
+  mrb_sym method_id;
 };
 
-static void
-print_backtrace_i(mrb_state *mrb, struct backtrace_location *loc, void *data)
-{
-  struct print_backtrace_args *args;
-
-  args = (struct print_backtrace_args*)data;
-
-  if (args->tracehead) {
-    fprintf(args->stream, "trace:\n");
-    args->tracehead = FALSE;
-  }
-
-  fprintf(args->stream, "\t[%d] %s:%d", loc->i, loc->filename, loc->lineno);
+typedef void (*each_backtrace_func)(mrb_state*, int i, struct backtrace_location*, void*);
 
-  if (loc->method) {
-    if (loc->class_name) {
-      fprintf(args->stream, ":in %s%s%s", loc->class_name, loc->sep, loc->method);
-    }
-    else {
-      fprintf(args->stream, ":in %s", loc->method);
-    }
-  }
-
-  fprintf(args->stream, "\n");
-}
-
-#endif
+static const mrb_data_type bt_type = { "Backtrace", mrb_free };
 
 static void
-get_backtrace_i(mrb_state *mrb, struct backtrace_location *loc, void *data)
+each_backtrace(mrb_state *mrb, mrb_int ciidx, mrb_code *pc0, each_backtrace_func func, void *data)
 {
-  mrb_value ary, str;
-  int ai;
-
-  ai = mrb_gc_arena_save(mrb);
-  ary = mrb_obj_value((struct RArray*)data);
-
-  str = mrb_str_new_cstr(mrb, loc->filename);
-  mrb_str_cat_lit(mrb, str, ":");
-  mrb_str_concat(mrb, str, mrb_fixnum_to_str(mrb, mrb_fixnum_value(loc->lineno), 10));
-
-  if (loc->method) {
-    mrb_str_cat_lit(mrb, str, ":in ");
-
-    if (loc->class_name) {
-      mrb_str_cat_cstr(mrb, str, loc->class_name);
-      mrb_str_cat_cstr(mrb, str, loc->sep);
-    }
-
-    mrb_str_cat_cstr(mrb, str, loc->method);
-  }
-
-  mrb_ary_push(mrb, ary, str);
-  mrb_gc_arena_restore(mrb, ai);
-}
-
-static void
-output_backtrace(mrb_state *mrb, mrb_int ciidx, mrb_code *pc0, output_stream_func func, void *data)
-{
-  int i;
+  int i, j;
 
   if (ciidx >= mrb->c->ciend - mrb->c->cibase)
     ciidx = 10; /* ciidx is broken... */
 
-  for (i = ciidx; i >= 0; i--) {
+  for (i=ciidx, j=0; i >= 0; i--,j++) {
     struct backtrace_location loc;
     mrb_callinfo *ci;
     mrb_irep *irep;
@@ -108,6 +45,7 @@ output_backtrace(mrb_state *mrb, mrb_int ciidx, mrb_code *pc0, output_stream_fun
     if (MRB_PROC_CFUNC_P(ci->proc)) continue;
 
     irep = ci->proc->body.irep;
+    if (!irep) continue;
 
     if (mrb->c->cibase[i].err) {
       pc = mrb->c->cibase[i].err;
@@ -123,64 +61,104 @@ output_backtrace(mrb_state *mrb, mrb_int ciidx, mrb_code *pc0, output_stream_fun
 
     if (loc.lineno == -1) continue;
 
-    if (ci->target_class == ci->proc->target_class) {
-      loc.sep = ".";
-    }
-    else {
-      loc.sep = "#";
-    }
-
     if (!loc.filename) {
       loc.filename = "(unknown)";
     }
 
-    loc.method = mrb_sym2name(mrb, ci->mid);
-    loc.class_name = mrb_class_name(mrb, ci->proc->target_class);
-    loc.i = i;
-    func(mrb, &loc, data);
+    loc.method_id = ci->mid;
+    func(mrb, j, &loc, data);
   }
 }
 
+#ifndef MRB_DISABLE_STDIO
+
 static void
-exc_output_backtrace(mrb_state *mrb, struct RObject *exc, output_stream_func func, void *stream)
+print_backtrace(mrb_state *mrb, mrb_value backtrace)
 {
-  mrb_value lastpc;
-  mrb_code *code;
-
-  lastpc = mrb_obj_iv_get(mrb, exc, mrb_intern_lit(mrb, "lastpc"));
-  if (mrb_nil_p(lastpc)) {
-    code = NULL;
-  } else {
-    code = (mrb_code*)mrb_cptr(lastpc);
+  int i, n;
+  FILE *stream = stderr;
+
+  if (!mrb_array_p(backtrace)) return;
+
+  n = RARRAY_LEN(backtrace) - 1;
+  if (n == 0) return;
+
+  fprintf(stream, "trace:\n");
+  for (i=0; i<n; i++) {
+    mrb_value entry = RARRAY_PTR(backtrace)[n-i-1];
+
+    if (mrb_string_p(entry)) {
+      fprintf(stream, "\t[%d] %.*s\n", i, (int)RSTRING_LEN(entry), RSTRING_PTR(entry));
+    }
   }
+}
+
+static int
+packed_bt_len(struct backtrace_location *bt, int n)
+{
+  int len = 0;
+  int i;
 
-  output_backtrace(mrb, mrb_fixnum(mrb_obj_iv_get(mrb, exc, mrb_intern_lit(mrb, "ciidx"))),
-                   code, func, stream);
+  for (i=0; i<n; i++) {
+    if (!bt[i].filename && !bt[i].lineno && !bt[i].method_id)
+      continue;
+    len++;
+  }
+  return len;
 }
 
-/* mrb_print_backtrace/mrb_get_backtrace:
+static void
+print_packed_backtrace(mrb_state *mrb, mrb_value packed)
+{
+  FILE *stream = stderr;
+  struct backtrace_location *bt;
+  int n, i;
+  int ai = mrb_gc_arena_save(mrb);
+
+  bt = (struct backtrace_location*)mrb_data_check_get_ptr(mrb, packed, &bt_type);
+  if (bt == NULL) return;
+  n = (mrb_int)RDATA(packed)->flags;
+
+  if (packed_bt_len(bt, n) == 0) return;
+  fprintf(stream, "trace:\n");
+  for (i = 0; i<n; i++) {
+    struct backtrace_location *entry = &bt[n-i-1];
+    if (entry->filename == NULL) continue;
+    fprintf(stream, "\t[%d] %s:%d", i, entry->filename, entry->lineno);
+    if (entry->method_id != 0) {
+      const char *method_name;
+
+      method_name = mrb_sym2name(mrb, entry->method_id);
+      fprintf(stream, ":in %s", method_name);
+      mrb_gc_arena_restore(mrb, ai);
+    }
+    fprintf(stream, "\n");
+  }
+}
 
-   function to retrieve backtrace information from the exception.
-   note that if you call method after the exception, call stack will be
-   overwritten.  So invoke these functions just after detecting exceptions.
-*/
+/* mrb_print_backtrace
 
-#ifndef MRB_DISABLE_STDIO
+   function to retrieve backtrace information from the last exception.
+*/
 
 MRB_API void
 mrb_print_backtrace(mrb_state *mrb)
 {
-  struct print_backtrace_args args;
+  mrb_value backtrace;
 
-  if (!mrb->exc || mrb_obj_is_kind_of(mrb, mrb_obj_value(mrb->exc), E_SYSSTACK_ERROR)) {
+  if (!mrb->exc) {
     return;
   }
 
-  args.stream = stderr;
-  args.tracehead = TRUE;
-  exc_output_backtrace(mrb, mrb->exc, print_backtrace_i, (void*)&args);
+  backtrace = mrb_obj_iv_get(mrb, mrb->exc, mrb_intern_lit(mrb, "backtrace"));
+  if (mrb_nil_p(backtrace)) return;
+  if (mrb_array_p(backtrace)) {
+    print_backtrace(mrb, backtrace);
+  }
+  else {
+    print_packed_backtrace(mrb, backtrace);
+  }
 }
-
 #else
 
 MRB_API void
@@ -190,28 +168,116 @@ mrb_print_backtrace(mrb_state *mrb)
 
 #endif
 
-MRB_API mrb_value
-mrb_exc_backtrace(mrb_state *mrb, mrb_value self)
+static void
+count_backtrace_i(mrb_state *mrb,
+                 int i,
+                 struct backtrace_location *loc,
+                 void *data)
+{
+  int *lenp = (int*)data;
+
+  if (loc->filename == NULL) return;
+  (*lenp)++;
+}
+
+static void
+pack_backtrace_i(mrb_state *mrb,
+                 int i,
+                 struct backtrace_location *loc,
+                 void *data)
 {
-  mrb_value ary;
+  struct backtrace_location **pptr = (struct backtrace_location**)data;
+  struct backtrace_location *ptr = *pptr;
 
-  ary = mrb_ary_new(mrb);
-  exc_output_backtrace(mrb, mrb_obj_ptr(self), get_backtrace_i, (void*)mrb_ary_ptr(ary));
+  if (loc->filename == NULL) return;
+  *ptr = *loc;
+  *pptr = ptr+1;
+}
+
+static mrb_value
+packed_backtrace(mrb_state *mrb)
+{
+  struct RData *backtrace;
+  ptrdiff_t ciidx = mrb->c->ci - mrb->c->cibase;
+  int len = 0;
+  int size;
+  void *ptr;
+
+  each_backtrace(mrb, ciidx, mrb->c->ci->pc, count_backtrace_i, &len);
+  size = len * sizeof(struct backtrace_location);
+  ptr = mrb_malloc(mrb, size);
+  memset(ptr, 0, size);
+  backtrace = mrb_data_object_alloc(mrb, NULL, ptr, &bt_type);
+  backtrace->flags = (unsigned int)len;
+  each_backtrace(mrb, ciidx, mrb->c->ci->pc, pack_backtrace_i, &ptr);
+  return mrb_obj_value(backtrace);
+}
+
+void
+mrb_keep_backtrace(mrb_state *mrb, mrb_value exc)
+{
+  mrb_value backtrace;
+  int ai = mrb_gc_arena_save(mrb);
 
-  return ary;
+  backtrace = packed_backtrace(mrb);
+  mrb_iv_set(mrb, exc, mrb_intern_lit(mrb, "backtrace"), backtrace);
+  mrb_gc_arena_restore(mrb, ai);
+}
+
+mrb_value
+mrb_unpack_backtrace(mrb_state *mrb, mrb_value backtrace)
+{
+  struct backtrace_location *bt;
+  mrb_int n, i;
+  int ai;
+
+  if (mrb_nil_p(backtrace)) {
+  empty_backtrace:
+    return mrb_ary_new_capa(mrb, 0);
+  }
+  if (mrb_array_p(backtrace)) return backtrace;
+  bt = (struct backtrace_location*)mrb_data_check_get_ptr(mrb, backtrace, &bt_type);
+  if (bt == NULL) goto empty_backtrace;
+  n = (mrb_int)RDATA(backtrace)->flags;
+  backtrace = mrb_ary_new_capa(mrb, n);
+  ai = mrb_gc_arena_save(mrb);
+  for (i = 0; i < n; i++) {
+    struct backtrace_location *entry = &bt[i];
+    mrb_value btline;
+
+    if (entry->filename == NULL) continue;
+    btline = mrb_format(mrb, "%S:%S",
+                              mrb_str_new_cstr(mrb, entry->filename),
+                              mrb_fixnum_value(entry->lineno));
+    if (entry->method_id != 0) {
+      mrb_str_cat_lit(mrb, btline, ":in ");
+      mrb_str_cat_cstr(mrb, btline, mrb_sym2name(mrb, entry->method_id));
+    }
+    mrb_ary_push(mrb, backtrace, btline);
+    mrb_gc_arena_restore(mrb, ai);
+  }
+
+  return backtrace;
 }
 
 MRB_API mrb_value
-mrb_get_backtrace(mrb_state *mrb)
+mrb_exc_backtrace(mrb_state *mrb, mrb_value exc)
 {
-  mrb_value ary;
-  mrb_callinfo *ci = mrb->c->ci;
-  mrb_code *pc = ci->pc;
-  mrb_int ciidx = (mrb_int)(ci - mrb->c->cibase - 1);
+  mrb_sym attr_name;
+  mrb_value backtrace;
 
-  if (ciidx < 0) ciidx = 0;
-  ary = mrb_ary_new(mrb);
-  output_backtrace(mrb, ciidx, pc, get_backtrace_i, (void*)mrb_ary_ptr(ary));
+  attr_name = mrb_intern_lit(mrb, "backtrace");
+  backtrace = mrb_iv_get(mrb, exc, attr_name);
+  if (mrb_nil_p(backtrace) || mrb_array_p(backtrace)) {
+    return backtrace;
+  }
+  backtrace = mrb_unpack_backtrace(mrb, backtrace);
+  mrb_iv_set(mrb, exc, attr_name, backtrace);
+  return backtrace;
+}
 
-  return ary;
+MRB_API mrb_value
+mrb_get_backtrace(mrb_state *mrb)
+{
+  return mrb_unpack_backtrace(mrb, packed_backtrace(mrb));
 }
index f63155c..56f64fd 100644 (file)
@@ -5,15 +5,16 @@
 */
 
 #include <stdarg.h>
-#include "mruby.h"
-#include "mruby/array.h"
-#include "mruby/class.h"
-#include "mruby/numeric.h"
-#include "mruby/proc.h"
-#include "mruby/string.h"
-#include "mruby/variable.h"
-#include "mruby/error.h"
-#include "mruby/data.h"
+#include <mruby.h>
+#include <mruby/array.h>
+#include <mruby/class.h>
+#include <mruby/numeric.h>
+#include <mruby/proc.h>
+#include <mruby/string.h>
+#include <mruby/variable.h>
+#include <mruby/error.h>
+#include <mruby/data.h>
+#include <mruby/istruct.h>
 
 KHASH_DEFINE(mt, mrb_sym, struct RProc*, TRUE, kh_int_hash_func, kh_int_hash_equal)
 
@@ -96,6 +97,7 @@ prepare_singleton_class(mrb_state *mrb, struct RBasic *o)
   }
   else {
     sc->super = o->c;
+    prepare_singleton_class(mrb, (struct RBasic*)sc);
   }
   o->c = sc;
   mrb_field_write_barrier(mrb, (struct RBasic*)o, (struct RBasic*)sc);
@@ -121,25 +123,44 @@ module_from_sym(mrb_state *mrb, struct RClass *klass, mrb_sym id)
   return mrb_class_ptr(c);
 }
 
+static mrb_bool
+class_ptr_p(mrb_value obj)
+{
+  switch (mrb_type(obj)) {
+  case MRB_TT_CLASS:
+  case MRB_TT_SCLASS:
+  case MRB_TT_MODULE:
+    return TRUE;
+  default:
+    return FALSE;
+  }
+}
+
 MRB_API struct RClass*
 mrb_class_outer_module(mrb_state *mrb, struct RClass *c)
 {
   mrb_value outer;
+  struct RClass *cls;
 
   outer = mrb_obj_iv_get(mrb, (struct RObject*)c, mrb_intern_lit(mrb, "__outer__"));
   if (mrb_nil_p(outer)) return NULL;
-  return mrb_class_ptr(outer);
+  cls = mrb_class_ptr(outer);
+  if (cls->tt == MRB_TT_SCLASS)
+  {
+    mrb_value klass;
+    klass = mrb_obj_iv_get(mrb, (struct RObject *)cls,
+                           mrb_intern_lit(mrb, "__attached__"));
+    if (class_ptr_p(klass)) {
+      cls = mrb_class_ptr(klass);
+    }
+  }
+  return cls;
 }
 
 static void
 check_if_class_or_module(mrb_state *mrb, mrb_value obj)
 {
-  switch (mrb_type(obj)) {
-  case MRB_TT_CLASS:
-  case MRB_TT_SCLASS:
-  case MRB_TT_MODULE:
-    return;
-  default:
+  if (!class_ptr_p(obj)) {
     mrb_raisef(mrb, E_TYPE_ERROR, "%S is not a class/module", mrb_inspect(mrb, obj));
   }
 }
@@ -174,6 +195,14 @@ MRB_API struct RClass*
 mrb_vm_define_module(mrb_state *mrb, mrb_value outer, mrb_sym id)
 {
   check_if_class_or_module(mrb, outer);
+  if (mrb_const_defined_at(mrb, outer, id)) {
+    mrb_value old = mrb_const_get(mrb, outer, id);
+
+    if (mrb_type(old) != MRB_TT_MODULE) {
+      mrb_raisef(mrb, E_TYPE_ERROR, "%S is not a module", mrb_inspect(mrb, old));
+    }
+    return mrb_class_ptr(old);
+  }
   return define_module(mrb, id, mrb_class_ptr(outer));
 }
 
@@ -231,12 +260,22 @@ mrb_define_class(mrb_state *mrb, const char *name, struct RClass *super)
   return mrb_define_class_id(mrb, mrb_intern_cstr(mrb, name), super);
 }
 
+static mrb_value mrb_bob_init(mrb_state *mrb, mrb_value cv);
+
 static void
 mrb_class_inherited(mrb_state *mrb, struct RClass *super, struct RClass *klass)
 {
+  mrb_value s;
+  mrb_sym mid;
+
   if (!super)
     super = mrb->object_class;
-  mrb_funcall(mrb, mrb_obj_value(super), "inherited", 1, mrb_obj_value(klass));
+  s = mrb_obj_value(super);
+  mid = mrb_intern_lit(mrb, "inherited");
+  if (!mrb_func_basic_p(mrb, s, mid, mrb_bob_init)) {
+    mrb_value c = mrb_obj_value(klass);
+    mrb_funcall_argv(mrb, mrb_obj_value(super), mid, 1, &c);
+  }
 }
 
 MRB_API struct RClass*
@@ -247,7 +286,8 @@ mrb_vm_define_class(mrb_state *mrb, mrb_value outer, mrb_value super, mrb_sym id
 
   if (!mrb_nil_p(super)) {
     if (mrb_type(super) != MRB_TT_CLASS) {
-      mrb_raisef(mrb, E_TYPE_ERROR, "superclass must be a Class (%S given)", super);
+      mrb_raisef(mrb, E_TYPE_ERROR, "superclass must be a Class (%S given)",
+                 mrb_inspect(mrb, super));
     }
     s = mrb_class_ptr(super);
   }
@@ -255,6 +295,21 @@ mrb_vm_define_class(mrb_state *mrb, mrb_value outer, mrb_value super, mrb_sym id
     s = 0;
   }
   check_if_class_or_module(mrb, outer);
+  if (mrb_const_defined_at(mrb, outer, id)) {
+    mrb_value old = mrb_const_get(mrb, outer, id);
+
+    if (mrb_type(old) != MRB_TT_CLASS) {
+      mrb_raisef(mrb, E_TYPE_ERROR, "%S is not a class", mrb_inspect(mrb, old));
+    }
+    c = mrb_class_ptr(old);
+    if (s) {
+      /* check super class */
+      if (mrb_class_real(c->super) != s) {
+        mrb_raisef(mrb, E_TYPE_ERROR, "superclass mismatch for class %S", old);
+      }
+    }
+    return c;
+  }
   c = define_class(mrb, id, s, mrb_class_ptr(outer));
   mrb_class_inherited(mrb, mrb_class_real(c->super), c);
 
@@ -271,6 +326,16 @@ mrb_class_defined(mrb_state *mrb, const char *name)
   return mrb_const_defined(mrb, mrb_obj_value(mrb->object_class), mrb_symbol(sym));
 }
 
+MRB_API mrb_bool
+mrb_class_defined_under(mrb_state *mrb, struct RClass *outer, const char *name)
+{
+  mrb_value sym = mrb_check_intern_cstr(mrb, name);
+  if (mrb_nil_p(sym)) {
+    return FALSE;
+  }
+  return mrb_const_defined_at(mrb, mrb_obj_value(outer), mrb_symbol(sym));
+}
+
 MRB_API struct RClass *
 mrb_class_get_under(mrb_state *mrb, struct RClass *outer, const char *name)
 {
@@ -284,6 +349,26 @@ mrb_class_get(mrb_state *mrb, const char *name)
 }
 
 MRB_API struct RClass *
+mrb_exc_get(mrb_state *mrb, const char *name)
+{
+  struct RClass *exc, *e;
+  mrb_value c = mrb_const_get(mrb, mrb_obj_value(mrb->object_class),
+                              mrb_intern_cstr(mrb, name));
+
+  if (mrb_type(c) != MRB_TT_CLASS) {
+    mrb_raise(mrb, mrb->eException_class, "exception corrupted");
+  }
+  exc = e = mrb_class_ptr(c);
+
+  while (e) {
+    if (e == mrb->eException_class)
+      return exc;
+    e = e->super;
+  }
+  return mrb->eException_class;
+}
+
+MRB_API struct RClass *
 mrb_module_get_under(mrb_state *mrb, struct RClass *outer, const char *name)
 {
   return module_from_sym(mrb, outer, mrb_intern_cstr(mrb, name));
@@ -336,10 +421,17 @@ mrb_define_method_raw(mrb_state *mrb, struct RClass *c, mrb_sym mid, struct RPro
   MRB_CLASS_ORIGIN(c);
   h = c->mt;
 
+  if (MRB_FROZEN_P(c)) {
+    if (c->tt == MRB_TT_MODULE)
+      mrb_raise(mrb, E_RUNTIME_ERROR, "can't modify frozen module");
+    else
+      mrb_raise(mrb, E_RUNTIME_ERROR, "can't modify frozen class");
+  }
   if (!h) h = c->mt = kh_init(mt, mrb);
   k = kh_put(mt, mrb, h, mid);
   kh_value(h, k) = p;
   if (p) {
+    p->c = NULL;
     mrb_field_write_barrier(mrb, (struct RBasic *)c, (struct RBasic *)p);
   }
 }
@@ -429,6 +521,8 @@ to_sym(mrb_state *mrb, mrb_value ss)
   else {
     mrb_value obj = mrb_funcall(mrb, ss, "inspect", 0);
     mrb_raisef(mrb, E_TYPE_ERROR, "%S is not a symbol", obj);
+    /* not reached */
+    return 0;
   }
 }
 
@@ -456,19 +550,21 @@ to_sym(mrb_state *mrb, mrb_value ss)
     b:      Boolean        [mrb_bool]
     n:      Symbol         [mrb_sym]
     d:      Data           [void*,mrb_data_type const] 2nd argument will be used to check data type so it won't be modified
+    I:      Inline struct  [void*]
     &:      Block          [mrb_value]
-    *:      rest argument  [mrb_value*,mrb_int]   Receive the rest of the arguments as an array.
-    |:      optional                              Next argument of '|' and later are optional.
-    ?:      optional given [mrb_bool]             true if preceding argument (optional) is given.
+    *:      rest argument  [mrb_value*,mrb_int]   The rest of the arguments as an array; *! avoid copy of the stack
+    |:      optional                              Following arguments are optional
+    ?:      optional given [mrb_bool]             true if preceding argument (optional) is given
  */
 MRB_API mrb_int
 mrb_get_args(mrb_state *mrb, const char *format, ...)
 {
   char c;
   int i = 0;
-  mrb_value *sp = mrb->c->stack + 1;
   va_list ap;
   int argc = mrb->c->ci->argc;
+  int arg_i = 0;
+  mrb_value *array_argv;
   mrb_bool opt = FALSE;
   mrb_bool given = TRUE;
 
@@ -476,9 +572,16 @@ mrb_get_args(mrb_state *mrb, const char *format, ...)
   if (argc < 0) {
     struct RArray *a = mrb_ary_ptr(mrb->c->stack[1]);
 
-    argc = a->len;
-    sp = a->ptr;
+    argc = ARY_LEN(a);
+    array_argv = ARY_PTR(a);
+  }
+  else {
+    array_argv = NULL;
   }
+
+#define ARGV \
+  (array_argv ? array_argv : (mrb->c->stack + 1))
+
   while ((c = *format++)) {
     switch (c) {
     case '|': case '*': case '&': case '?':
@@ -502,7 +605,7 @@ mrb_get_args(mrb_state *mrb, const char *format, ...)
 
         p = va_arg(ap, mrb_value*);
         if (i < argc) {
-          *p = *sp++;
+          *p = ARGV[arg_i++];
           i++;
         }
       }
@@ -515,15 +618,9 @@ mrb_get_args(mrb_state *mrb, const char *format, ...)
         if (i < argc) {
           mrb_value ss;
 
-          ss = *sp++;
-          switch (mrb_type(ss)) {
-          case MRB_TT_CLASS:
-          case MRB_TT_MODULE:
-          case MRB_TT_SCLASS:
-            break;
-          default:
+          ss = ARGV[arg_i++];
+          if (!class_ptr_p(ss)) {
             mrb_raisef(mrb, E_TYPE_ERROR, "%S is not class/module", ss);
-            break;
           }
           *p = ss;
           i++;
@@ -537,14 +634,14 @@ mrb_get_args(mrb_state *mrb, const char *format, ...)
         p = va_arg(ap, mrb_value*);
         if (*format == '!') {
           format++;
-          if (i < argc && mrb_nil_p(*sp)) {
-            *p = *sp++;
+          if (i < argc && mrb_nil_p(ARGV[arg_i])) {
+            *p = ARGV[arg_i++];
             i++;
             break;
           }
         }
         if (i < argc) {
-          *p = to_str(mrb, *sp++);
+          *p = to_str(mrb, ARGV[arg_i++]);
           i++;
         }
       }
@@ -556,14 +653,14 @@ mrb_get_args(mrb_state *mrb, const char *format, ...)
         p = va_arg(ap, mrb_value*);
         if (*format == '!') {
           format++;
-          if (i < argc && mrb_nil_p(*sp)) {
-            *p = *sp++;
+          if (i < argc && mrb_nil_p(ARGV[arg_i])) {
+            *p = ARGV[arg_i++];
             i++;
             break;
           }
         }
         if (i < argc) {
-          *p = to_ary(mrb, *sp++);
+          *p = to_ary(mrb, ARGV[arg_i++]);
           i++;
         }
       }
@@ -575,14 +672,14 @@ mrb_get_args(mrb_state *mrb, const char *format, ...)
         p = va_arg(ap, mrb_value*);
         if (*format == '!') {
           format++;
-          if (i < argc && mrb_nil_p(*sp)) {
-            *p = *sp++;
+          if (i < argc && mrb_nil_p(ARGV[arg_i])) {
+            *p = ARGV[arg_i++];
             i++;
             break;
           }
         }
         if (i < argc) {
-          *p = to_hash(mrb, *sp++);
+          *p = to_hash(mrb, ARGV[arg_i++]);
           i++;
         }
       }
@@ -597,15 +694,15 @@ mrb_get_args(mrb_state *mrb, const char *format, ...)
         pl = va_arg(ap, mrb_int*);
         if (*format == '!') {
           format++;
-          if (i < argc && mrb_nil_p(*sp)) {
+          if (i < argc && mrb_nil_p(ARGV[arg_i])) {
             *ps = NULL;
             *pl = 0;
-            i++;
+            i++; arg_i++;
             break;
           }
         }
         if (i < argc) {
-          ss = to_str(mrb, *sp++);
+          ss = to_str(mrb, ARGV[arg_i++]);
           *ps = RSTRING_PTR(ss);
           *pl = RSTRING_LEN(ss);
           i++;
@@ -620,14 +717,14 @@ mrb_get_args(mrb_state *mrb, const char *format, ...)
         ps = va_arg(ap, const char**);
         if (*format == '!') {
           format++;
-          if (i < argc && mrb_nil_p(*sp)) {
+          if (i < argc && mrb_nil_p(ARGV[arg_i])) {
             *ps = NULL;
-            i++; sp++;
+            i++; arg_i++;
             break;
           }
         }
         if (i < argc) {
-          ss = to_str(mrb, *sp++);
+          ss = to_str(mrb, ARGV[arg_i++]);
           *ps = mrb_string_value_cstr(mrb, &ss);
           i++;
         }
@@ -644,18 +741,36 @@ mrb_get_args(mrb_state *mrb, const char *format, ...)
         pl = va_arg(ap, mrb_int*);
         if (*format == '!') {
           format++;
-          if (i < argc && mrb_nil_p(*sp)) {
+          if (i < argc && mrb_nil_p(ARGV[arg_i])) {
             *pb = 0;
             *pl = 0;
-            i++; sp++;
+            i++; arg_i++;
             break;
           }
         }
         if (i < argc) {
-          aa = to_ary(mrb, *sp++);
+          aa = to_ary(mrb, ARGV[arg_i++]);
           a = mrb_ary_ptr(aa);
-          *pb = a->ptr;
-          *pl = a->len;
+          *pb = ARY_PTR(a);
+          *pl = ARY_LEN(a);
+          i++;
+        }
+      }
+      break;
+    case 'I':
+      {
+        void* *p;
+        mrb_value ss;
+
+        p = va_arg(ap, void**);
+        if (i < argc) {
+          ss = ARGV[arg_i];
+          if (mrb_type(ss) != MRB_TT_ISTRUCT)
+          {
+            mrb_raisef(mrb, E_TYPE_ERROR, "%S is not inline struct", ss);
+          }
+          *p = mrb_istruct_ptr(ss);
+          arg_i++;
           i++;
         }
       }
@@ -666,8 +781,8 @@ mrb_get_args(mrb_state *mrb, const char *format, ...)
 
         p = va_arg(ap, mrb_float*);
         if (i < argc) {
-          *p = mrb_to_flo(mrb, *sp);
-          sp++;
+          *p = mrb_to_flo(mrb, ARGV[arg_i]);
+          arg_i++;
           i++;
         }
       }
@@ -678,15 +793,15 @@ mrb_get_args(mrb_state *mrb, const char *format, ...)
 
         p = va_arg(ap, mrb_int*);
         if (i < argc) {
-          switch (mrb_type(*sp)) {
+          switch (mrb_type(ARGV[arg_i])) {
             case MRB_TT_FIXNUM:
-              *p = mrb_fixnum(*sp);
+              *p = mrb_fixnum(ARGV[arg_i]);
               break;
             case MRB_TT_FLOAT:
               {
-                mrb_float f = mrb_float(*sp);
+                mrb_float f = mrb_float(ARGV[arg_i]);
 
-                if (!FIXABLE(f)) {
+                if (!FIXABLE_FLOAT(f)) {
                   mrb_raise(mrb, E_RANGE_ERROR, "float too big for int");
                 }
                 *p = (mrb_int)f;
@@ -696,10 +811,10 @@ mrb_get_args(mrb_state *mrb, const char *format, ...)
               mrb_raise(mrb, E_TYPE_ERROR, "no implicit conversion of String into Integer");
               break;
             default:
-              *p = mrb_fixnum(mrb_Integer(mrb, *sp));
+              *p = mrb_fixnum(mrb_Integer(mrb, ARGV[arg_i]));
               break;
           }
-          sp++;
+          arg_i++;
           i++;
         }
       }
@@ -709,7 +824,7 @@ mrb_get_args(mrb_state *mrb, const char *format, ...)
         mrb_bool *boolp = va_arg(ap, mrb_bool*);
 
         if (i < argc) {
-          mrb_value b = *sp++;
+          mrb_value b = ARGV[arg_i++];
           *boolp = mrb_test(b);
           i++;
         }
@@ -723,7 +838,7 @@ mrb_get_args(mrb_state *mrb, const char *format, ...)
         if (i < argc) {
           mrb_value ss;
 
-          ss = *sp++;
+          ss = ARGV[arg_i++];
           *symp = to_sym(mrb, ss);
           i++;
         }
@@ -738,14 +853,14 @@ mrb_get_args(mrb_state *mrb, const char *format, ...)
         type = va_arg(ap, struct mrb_data_type const*);
         if (*format == '!') {
           format++;
-          if (i < argc && mrb_nil_p(*sp)) {
+          if (i < argc && mrb_nil_p(ARGV[arg_i])) {
             *datap = 0;
-            i++; sp++;
+            i++; arg_i++;
             break;
           }
         }
         if (i < argc) {
-          *datap = mrb_data_get_ptr(mrb, *sp++, type);
+          *datap = mrb_data_get_ptr(mrb, ARGV[arg_i++], type);
           ++i;
         }
       }
@@ -781,16 +896,28 @@ mrb_get_args(mrb_state *mrb, const char *format, ...)
       {
         mrb_value **var;
         mrb_int *pl;
+        mrb_bool nocopy = array_argv ? TRUE : FALSE;
 
+        if (*format == '!') {
+          format++;
+          nocopy = TRUE;
+        }
         var = va_arg(ap, mrb_value**);
         pl = va_arg(ap, mrb_int*);
         if (argc > i) {
           *pl = argc-i;
           if (*pl > 0) {
-            *var = sp;
+            if (nocopy) {
+              *var = ARGV+arg_i;
+            }
+            else {
+              mrb_value args = mrb_ary_new_from_values(mrb, *pl, ARGV+arg_i);
+              RARRAY(args)->c = NULL;
+              *var = RARRAY_PTR(args);
+            }
           }
           i = argc;
-          sp += *pl;
+          arg_i += *pl;
         }
         else {
           *pl = 0;
@@ -803,6 +930,9 @@ mrb_get_args(mrb_state *mrb, const char *format, ...)
       break;
     }
   }
+
+#undef ARGV
+
   if (!c && argc > i) {
     mrb_raise(mrb, E_ARGUMENT_ERROR, "wrong number of arguments");
   }
@@ -830,7 +960,9 @@ boot_defclass(mrb_state *mrb, struct RClass *super)
 static void
 boot_initmod(mrb_state *mrb, struct RClass *mod)
 {
-  mod->mt = kh_init(mt, mrb);
+  if (!mod->mt) {
+    mod->mt = kh_init(mt, mrb);
+  }
 }
 
 static struct RClass*
@@ -846,7 +978,8 @@ include_class_new(mrb_state *mrb, struct RClass *m, struct RClass *super)
   ic->super = super;
   if (m->tt == MRB_TT_ICLASS) {
     ic->c = m->c;
-  } else {
+  }
+  else {
     ic->c = m;
   }
   return ic;
@@ -872,7 +1005,7 @@ include_module_at(mrb_state *mrb, struct RClass *c, struct RClass *ins_pos, stru
       if (p->tt == MRB_TT_ICLASS) {
         if (p->mt == m->mt) {
           if (!superclass_seen) {
-            ins_pos = p; // move insert point
+            ins_pos = p; /* move insert point */
           }
           goto skip;
         }
@@ -936,24 +1069,6 @@ mrb_mod_prepend_features(mrb_state *mrb, mrb_value mod)
 }
 
 static mrb_value
-mrb_mod_prepend(mrb_state *mrb, mrb_value klass)
-{
-  mrb_value *argv;
-  mrb_int argc, i;
-
-  mrb_get_args(mrb, "*", &argv, &argc);
-  for (i=0; i<argc; i++) {
-    mrb_check_type(mrb, argv[i], MRB_TT_MODULE);
-  }
-  while (argc--) {
-    mrb_funcall(mrb, argv[argc], "prepend_features", 1, klass);
-    mrb_funcall(mrb, argv[argc], "prepended", 1, klass);
-  }
-
-  return klass;
-}
-
-static mrb_value
 mrb_mod_append_features(mrb_state *mrb, mrb_value mod)
 {
   mrb_value klass;
@@ -964,24 +1079,6 @@ mrb_mod_append_features(mrb_state *mrb, mrb_value mod)
   return mod;
 }
 
-static mrb_value
-mrb_mod_include(mrb_state *mrb, mrb_value klass)
-{
-  mrb_value *argv;
-  mrb_int argc, i;
-
-  mrb_get_args(mrb, "*", &argv, &argc);
-  for (i=0; i<argc; i++) {
-    mrb_check_type(mrb, argv[i], MRB_TT_MODULE);
-  }
-  while (argc--) {
-    mrb_funcall(mrb, argv[argc], "append_features", 1, klass);
-    mrb_funcall(mrb, argv[argc], "included", 1, klass);
-  }
-
-  return klass;
-}
-
 /* 15.2.2.4.28 */
 /*
  *  call-seq:
@@ -1075,7 +1172,7 @@ mrb_mod_initialize(mrb_state *mrb, mrb_value mod)
 {
   mrb_value b;
   struct RClass *m = mrb_class_ptr(mod);
-  boot_initmod(mrb, m); // bootstrap a newly initialized module
+  boot_initmod(mrb, m); /* bootstrap a newly initialized module */
   mrb_get_args(mrb, "|&", &b);
   if (!mrb_nil_p(b)) {
     mrb_yield_with_class(mrb, b, 1, &mod, mod, m);
@@ -1155,10 +1252,6 @@ mrb_singleton_class(mrb_state *mrb, mrb_value v)
   }
   obj = mrb_basic_ptr(v);
   prepare_singleton_class(mrb, obj);
-  if (mrb->c && mrb->c->ci && mrb->c->ci->target_class) {
-    mrb_obj_iv_set(mrb, (struct RObject*)obj->c, mrb_intern_lit(mrb, "__outer__"),
-                   mrb_obj_value(mrb->c->ci->target_class));
-  }
   return mrb_obj_value(obj->c);
 }
 
@@ -1319,6 +1412,9 @@ mrb_instance_alloc(mrb_state *mrb, mrb_value cv)
     mrb_raise(mrb, E_TYPE_ERROR, "can't create instance of singleton class");
 
   if (ttype == 0) ttype = MRB_TT_OBJECT;
+  if (ttype <= MRB_TT_CPTR) {
+    mrb_raisef(mrb, E_TYPE_ERROR, "can't create instance of %S", cv);
+  }
   o = (struct RObject*)mrb_obj_alloc(mrb, ttype, c);
   return mrb_obj_value(o);
 }
@@ -1353,10 +1449,13 @@ MRB_API mrb_value
 mrb_obj_new(mrb_state *mrb, struct RClass *c, mrb_int argc, const mrb_value *argv)
 {
   mrb_value obj;
+  mrb_sym mid;
 
   obj = mrb_instance_alloc(mrb, mrb_obj_value(c));
-  mrb_funcall_argv(mrb, obj, mrb_intern_lit(mrb, "initialize"), argc, argv);
-
+  mid = mrb_intern_lit(mrb, "initialize");
+  if (!mrb_func_basic_p(mrb, obj, mid, mrb_bob_init)) {
+    mrb_funcall_argv(mrb, obj, mid, argc, argv);
+  }
   return obj;
 }
 
@@ -1378,13 +1477,17 @@ mrb_class_new_class(mrb_state *mrb, mrb_value cv)
   mrb_int n;
   mrb_value super, blk;
   mrb_value new_class;
+  mrb_sym mid;
 
   n = mrb_get_args(mrb, "|C&", &super, &blk);
   if (n == 0) {
     super = mrb_obj_value(mrb->object_class);
   }
   new_class = mrb_obj_value(mrb_class_new(mrb, mrb_class_ptr(super)));
-  mrb_funcall_with_block(mrb, new_class, mrb_intern_lit(mrb, "initialize"), n, &super, blk);
+  mid = mrb_intern_lit(mrb, "initialize");
+  if (!mrb_func_basic_p(mrb, new_class, mid, mrb_bob_init)) {
+    mrb_funcall_with_block(mrb, new_class, mid, n, &super, blk);
+  }
   mrb_class_inherited(mrb, mrb_class_ptr(super), mrb_class_ptr(new_class));
   return new_class;
 }
@@ -1415,75 +1518,53 @@ mrb_bob_not(mrb_state *mrb, mrb_value cv)
   return mrb_bool_value(!mrb_test(cv));
 }
 
-void
-mrb_method_missing(mrb_state *mrb, mrb_sym name, mrb_value self, mrb_value args)
-{
-  mrb_sym inspect;
-  mrb_value repr;
-
-  inspect = mrb_intern_lit(mrb, "inspect");
-  if (mrb->c->ci > mrb->c->cibase && mrb->c->ci[-1].mid == inspect) {
-    /* method missing in inspect; avoid recursion */
-    repr = mrb_any_to_s(mrb, self);
-  }
-  else if (mrb_respond_to(mrb, self, inspect) && mrb->c->ci - mrb->c->cibase < 64) {
-    repr = mrb_funcall_argv(mrb, self, inspect, 0, 0);
-    if (mrb_string_p(repr) && RSTRING_LEN(repr) > 64) {
-      repr = mrb_any_to_s(mrb, self);
-    }
-  }
-  else {
-    repr = mrb_any_to_s(mrb, self);
-  }
-
-  mrb_no_method_error(mrb, name, args, "undefined method '%S' for %S",
-                      mrb_sym2str(mrb, name), repr);
-}
-
-/* 15.3.1.3.30 */
+/* 15.3.1.3.1  */
+/* 15.3.1.3.10 */
+/* 15.3.1.3.11 */
 /*
  *  call-seq:
- *     obj.method_missing(symbol [, *args] )   -> result
+ *     obj == other        -> true or false
+ *     obj.equal?(other)   -> true or false
+ *     obj.eql?(other)     -> true or false
  *
- *  Invoked by Ruby when <i>obj</i> is sent a message it cannot handle.
- *  <i>symbol</i> is the symbol for the method called, and <i>args</i>
- *  are any arguments that were passed to it. By default, the interpreter
- *  raises an error when this method is called. However, it is possible
- *  to override the method to provide more dynamic behavior.
- *  If it is decided that a particular method should not be handled, then
- *  <i>super</i> should be called, so that ancestors can pick up the
- *  missing method.
- *  The example below creates
- *  a class <code>Roman</code>, which responds to methods with names
- *  consisting of roman numerals, returning the corresponding integer
- *  values.
+ *  Equality---At the <code>Object</code> level, <code>==</code> returns
+ *  <code>true</code> only if <i>obj</i> and <i>other</i> are the
+ *  same object. Typically, this method is overridden in descendant
+ *  classes to provide class-specific meaning.
  *
- *     class Roman
- *       def romanToInt(str)
- *         # ...
- *       end
- *       def method_missing(methId)
- *         str = methId.id2name
- *         romanToInt(str)
- *       end
- *     end
+ *  Unlike <code>==</code>, the <code>equal?</code> method should never be
+ *  overridden by subclasses: it is used to determine object identity
+ *  (that is, <code>a.equal?(b)</code> iff <code>a</code> is the same
+ *  object as <code>b</code>).
  *
- *     r = Roman.new
- *     r.iv      #=> 4
- *     r.xxiii   #=> 23
- *     r.mm      #=> 2000
+ *  The <code>eql?</code> method returns <code>true</code> if
+ *  <i>obj</i> and <i>anObject</i> have the same value. Used by
+ *  <code>Hash</code> to test members for equality.  For objects of
+ *  class <code>Object</code>, <code>eql?</code> is synonymous with
+ *  <code>==</code>. Subclasses normally continue this tradition, but
+ *  there are exceptions. <code>Numeric</code> types, for example,
+ *  perform type conversion across <code>==</code>, but not across
+ *  <code>eql?</code>, so:
+ *
+ *     1 == 1.0     #=> true
+ *     1.eql? 1.0   #=> false
  */
+mrb_value
+mrb_obj_equal_m(mrb_state *mrb, mrb_value self)
+{
+  mrb_value arg;
+
+  mrb_get_args(mrb, "o", &arg);
+  return mrb_bool_value(mrb_obj_equal(mrb, self, arg));
+}
+
 static mrb_value
-mrb_bob_missing(mrb_state *mrb, mrb_value mod)
+mrb_obj_not_equal_m(mrb_state *mrb, mrb_value self)
 {
-  mrb_sym name;
-  mrb_value *a;
-  mrb_int alen;
+  mrb_value arg;
 
-  mrb_get_args(mrb, "n*", &name, &a, &alen);
-  mrb_method_missing(mrb, name, mod, mrb_ary_new_from_values(mrb, alen, a));
-  /* not reached */
-  return mrb_nil_value();
+  mrb_get_args(mrb, "o", &arg);
+  return mrb_bool_value(!mrb_equal(mrb, self, arg));
 }
 
 MRB_API mrb_bool
@@ -1532,7 +1613,7 @@ mrb_class_path(mrb_state *mrb, struct RClass *c)
     if (sym == 0) {
       return mrb_nil_value();
     }
-    else if (outer && outer != mrb->object_class) {
+    else if (outer && outer != c && outer != mrb->object_class) {
       mrb_value base = mrb_class_path(mrb, outer);
       path = mrb_str_buf_new(mrb, 0);
       if (mrb_nil_p(base)) {
@@ -1551,9 +1632,11 @@ mrb_class_path(mrb_state *mrb, struct RClass *c)
       name = mrb_sym2name_len(mrb, sym, &len);
       path = mrb_str_new(mrb, name, len);
     }
-    mrb_obj_iv_set(mrb, (struct RObject*)c, classpath, path);
+    if (!MRB_FROZEN_P(c)) {
+      mrb_obj_iv_set(mrb, (struct RObject*)c, classpath, path);
+    }
   }
-  return path;
+  return mrb_str_dup(mrb, path);
 }
 
 MRB_API struct RClass *
@@ -1698,15 +1781,11 @@ mrb_mod_to_s(mrb_state *mrb, mrb_value klass)
 
     str = mrb_str_new_lit(mrb, "#<Class:");
 
-    switch (mrb_type(v)) {
-      case MRB_TT_CLASS:
-      case MRB_TT_MODULE:
-      case MRB_TT_SCLASS:
-        mrb_str_cat_str(mrb, str, mrb_inspect(mrb, v));
-        break;
-      default:
-        mrb_str_cat_str(mrb, str, mrb_any_to_s(mrb, v));
-        break;
+    if (class_ptr_p(v)) {
+      mrb_str_cat_str(mrb, str, mrb_inspect(mrb, v));
+    }
+    else {
+      mrb_str_cat_str(mrb, str, mrb_any_to_s(mrb, v));
     }
     return mrb_str_cat_lit(mrb, str, ">");
   }
@@ -1785,7 +1864,7 @@ mrb_mod_undef(mrb_state *mrb, mrb_value mod)
 
   mrb_get_args(mrb, "*", &argv, &argc);
   while (argc--) {
-    undef_method(mrb, c, mrb_symbol(*argv));
+    undef_method(mrb, c, to_sym(mrb, *argv));
     argv++;
   }
   return mrb_nil_value();
@@ -1797,9 +1876,21 @@ mod_define_method(mrb_state *mrb, mrb_value self)
   struct RClass *c = mrb_class_ptr(self);
   struct RProc *p;
   mrb_sym mid;
+  mrb_value proc = mrb_undef_value();
   mrb_value blk;
 
-  mrb_get_args(mrb, "n&", &mid, &blk);
+  mrb_get_args(mrb, "n|o&", &mid, &proc, &blk);
+  switch (mrb_type(proc)) {
+    case MRB_TT_PROC:
+      blk = proc;
+      break;
+    case MRB_TT_UNDEF:
+      /* ignored */
+      break;
+    default:
+      mrb_raisef(mrb, E_TYPE_ERROR, "wrong argument type %S (expected Proc)", mrb_obj_value(mrb_obj_class(mrb, proc)));
+      break;
+  }
   if (mrb_nil_p(blk)) {
     mrb_raise(mrb, E_ARGUMENT_ERROR, "no block given");
   }
@@ -2027,7 +2118,7 @@ mrb_mod_remove_method(mrb_state *mrb, mrb_value mod)
 
   mrb_get_args(mrb, "*", &argv, &argc);
   while (argc--) {
-    remove_method(mrb, mod, mrb_symbol(*argv));
+    remove_method(mrb, mod, to_sym(mrb, *argv));
     argv++;
   }
   return mod;
@@ -2087,7 +2178,14 @@ mrb_mod_const_set(mrb_state *mrb, mrb_value mod)
 
   mrb_get_args(mrb, "no", &id, &value);
   check_const_name_sym(mrb, id);
-  mrb_const_set(mrb, mod, id, value);
+  if ((mrb_type(value) == MRB_TT_CLASS || mrb_type(value) == MRB_TT_MODULE)
+      && !mrb_obj_iv_defined(mrb, mrb_obj_ptr(value), mrb_intern_lit(mrb, "__classid__"))) {
+    /* name unnamed classes/modules */
+    setup_class(mrb, mrb_class_ptr(mod), mrb_class_ptr(value), id);
+  }
+  else {
+    mrb_const_set(mrb, mod, id, value);
+  }
   return value;
 }
 
@@ -2158,7 +2256,7 @@ mrb_mod_module_function(mrb_state *mrb, mrb_value mod)
   mrb_check_type(mrb, mod, MRB_TT_MODULE);
 
   mrb_get_args(mrb, "*", &argv, &argc);
-  if(argc == 0) {
+  if (argc == 0) {
     /* set MODFUNC SCOPE if implemented */
     return mod;
   }
@@ -2182,6 +2280,11 @@ mrb_mod_module_function(mrb_state *mrb, mrb_value mod)
   return mod;
 }
 
+/* implementation of __id__ */
+mrb_value mrb_obj_id_m(mrb_state *mrb, mrb_value self);
+/* implementation of instance_eval */
+mrb_value mrb_obj_instance_eval(mrb_state*, mrb_value);
+
 void
 mrb_init_class(mrb_state *mrb)
 {
@@ -2221,7 +2324,11 @@ mrb_init_class(mrb_state *mrb)
   MRB_SET_INSTANCE_TT(cls, MRB_TT_CLASS);
   mrb_define_method(mrb, bob, "initialize",              mrb_bob_init,             MRB_ARGS_NONE());
   mrb_define_method(mrb, bob, "!",                       mrb_bob_not,              MRB_ARGS_NONE());
-  mrb_define_method(mrb, bob, "method_missing",          mrb_bob_missing,          MRB_ARGS_ANY());  /* 15.3.1.3.30 */
+  mrb_define_method(mrb, bob, "==",                      mrb_obj_equal_m,          MRB_ARGS_REQ(1)); /* 15.3.1.3.1  */
+  mrb_define_method(mrb, bob, "!=",                      mrb_obj_not_equal_m,      MRB_ARGS_REQ(1));
+  mrb_define_method(mrb, bob, "__id__",                  mrb_obj_id_m,             MRB_ARGS_NONE()); /* 15.3.1.3.3  */
+  mrb_define_method(mrb, bob, "__send__",                mrb_f_send,               MRB_ARGS_ANY());  /* 15.3.1.3.4  */
+  mrb_define_method(mrb, bob, "instance_eval",           mrb_obj_instance_eval,    MRB_ARGS_ANY());  /* 15.3.1.3.18 */
 
   mrb_define_class_method(mrb, cls, "new",               mrb_class_new_class,      MRB_ARGS_OPT(1));
   mrb_define_method(mrb, cls, "superclass",              mrb_class_superclass,     MRB_ARGS_NONE()); /* 15.2.3.3.4 */
@@ -2235,10 +2342,8 @@ mrb_init_class(mrb_state *mrb)
   mrb_define_method(mrb, mod, "class_variable_set",      mrb_mod_cvar_set,         MRB_ARGS_REQ(2)); /* 15.2.2.4.18 */
   mrb_define_method(mrb, mod, "extend_object",           mrb_mod_extend_object,    MRB_ARGS_REQ(1)); /* 15.2.2.4.25 */
   mrb_define_method(mrb, mod, "extended",                mrb_bob_init,             MRB_ARGS_REQ(1)); /* 15.2.2.4.26 */
-  mrb_define_method(mrb, mod, "prepend",                 mrb_mod_prepend,          MRB_ARGS_ANY());
   mrb_define_method(mrb, mod, "prepended",               mrb_bob_init,             MRB_ARGS_REQ(1));
   mrb_define_method(mrb, mod, "prepend_features",        mrb_mod_prepend_features, MRB_ARGS_REQ(1));
-  mrb_define_method(mrb, mod, "include",                 mrb_mod_include,          MRB_ARGS_ANY());  /* 15.2.2.4.27 */
   mrb_define_method(mrb, mod, "include?",                mrb_mod_include_p,        MRB_ARGS_REQ(1)); /* 15.2.2.4.28 */
   mrb_define_method(mrb, mod, "append_features",         mrb_mod_append_features,  MRB_ARGS_REQ(1)); /* 15.2.2.4.10 */
   mrb_define_method(mrb, mod, "class_eval",              mrb_mod_module_eval,      MRB_ARGS_ANY());  /* 15.2.2.4.15 */
@@ -2268,7 +2373,7 @@ mrb_init_class(mrb_state *mrb)
   mrb_define_method(mrb, mod, "constants",               mrb_mod_constants,        MRB_ARGS_OPT(1)); /* 15.2.2.4.24 */
   mrb_define_method(mrb, mod, "remove_const",            mrb_mod_remove_const,     MRB_ARGS_REQ(1)); /* 15.2.2.4.40 */
   mrb_define_method(mrb, mod, "const_missing",           mrb_mod_const_missing,    MRB_ARGS_REQ(1));
-  mrb_define_method(mrb, mod, "define_method",           mod_define_method,        MRB_ARGS_REQ(1));
+  mrb_define_method(mrb, mod, "define_method",           mod_define_method,        MRB_ARGS_ARG(1,1));
   mrb_define_method(mrb, mod, "class_variables",         mrb_mod_class_variables,  MRB_ARGS_NONE()); /* 15.2.2.4.19 */
   mrb_define_method(mrb, mod, "===",                     mrb_mod_eqq,              MRB_ARGS_REQ(1));
   mrb_define_class_method(mrb, mod, "constants",         mrb_mod_s_constants,      MRB_ARGS_ANY());  /* 15.2.2.3.1 */
index 6db5c65..6b2c43b 100644 (file)
@@ -1,9 +1,9 @@
-#include "mruby.h"
-#include "mruby/irep.h"
-#include "mruby/debug.h"
-#include "mruby/opcode.h"
-#include "mruby/string.h"
-#include "mruby/proc.h"
+#include <mruby.h>
+#include <mruby/irep.h>
+#include <mruby/debug.h>
+#include <mruby/opcode.h>
+#include <mruby/string.h>
+#include <mruby/proc.h>
 
 #ifndef MRB_DISABLE_STDIO
 static int
@@ -239,13 +239,13 @@ codedump(mrb_state *mrb, mrb_irep *irep)
       printf("OP_RETURN\tR%d", GETARG_A(c));
       switch (GETARG_B(c)) {
       case OP_R_NORMAL:
+        printf("\tnormal\t"); break;
       case OP_R_RETURN:
         printf("\treturn\t"); break;
       case OP_R_BREAK:
         printf("\tbreak\t"); break;
       default:
         printf("\tbroken\t"); break;
-        break;
       }
       print_lv(mrb, irep, c, RA);
       break;
@@ -259,7 +259,15 @@ codedump(mrb_state *mrb, mrb_irep *irep)
       break;
 
     case OP_LAMBDA:
-      printf("OP_LAMBDA\tR%d\tI(%+d)\t%d", GETARG_A(c), GETARG_b(c)+1, GETARG_c(c));
+      printf("OP_LAMBDA\tR%d\tI(%+d)\t", GETARG_A(c), GETARG_b(c)+1);
+      switch (GETARG_c(c)) {
+      case OP_L_METHOD:
+        printf("method"); break;
+      case OP_L_BLOCK:
+        printf("block"); break;
+      case OP_L_LAMBDA:
+        printf("lambda"); break;
+      }
       print_lv(mrb, irep, c, RA);
       break;
     case OP_RANGE:
@@ -323,7 +331,7 @@ codedump(mrb_state *mrb, mrb_irep *irep)
              GETARG_C(c));
       break;
     case OP_EQ:
-      printf("OP_EQ\tR%d\t:%s\t%d\n", GETARG_A(c),
+      printf("OP_EQ\t\tR%d\t:%s\t%d\n", GETARG_A(c),
              mrb_sym2name(mrb, irep->syms[GETARG_B(c)]),
              GETARG_C(c));
       break;
@@ -409,16 +417,29 @@ codedump(mrb_state *mrb, mrb_irep *irep)
       printf("OP_ONERR\t%03d\n", i+GETARG_sBx(c));
       break;
     case OP_RESCUE:
-      printf("OP_RESCUE\tR%d\t\t", GETARG_A(c));
-      print_lv(mrb, irep, c, RA);
+      {
+        int a = GETARG_A(c);
+        int b = GETARG_B(c);
+        int cnt = GETARG_C(c);
+
+        if (b == 0) {
+          printf("OP_RESCUE\tR%d\t\t%s", a, cnt ? "cont" : "");
+          print_lv(mrb, irep, c, RA);
+          break;
+        }
+        else {
+          printf("OP_RESCUE\tR%d\tR%d\t%s", a, b, cnt ? "cont" : "");
+          print_lv(mrb, irep, c, RAB);
+          break;
+        }
+      }
       break;
     case OP_RAISE:
       printf("OP_RAISE\tR%d\t\t", GETARG_A(c));
       print_lv(mrb, irep, c, RA);
       break;
     case OP_POPERR:
-      printf("OP_POPERR\t%d\t\t", GETARG_A(c));
-      print_lv(mrb, irep, c, RA);
+      printf("OP_POPERR\t%d\t\t\n", GETARG_A(c));
       break;
     case OP_EPOP:
       printf("OP_EPOP\t%d\n", GETARG_A(c));
index 0186b94..0032fc8 100644 (file)
@@ -4,7 +4,7 @@
 ** See Copyright Notice in mruby.h
 */
 
-#include "mruby.h"
+#include <mruby.h>
 
 void
 mrb_init_comparable(mrb_state *mrb)
index 4ac6920..d2bcd7d 100644 (file)
@@ -1,7 +1,7 @@
 #include <string.h>
-#include "mruby.h"
-#include "mruby/irep.h"
-#include "mruby/debug.h"
+#include <mruby.h>
+#include <mruby/irep.h>
+#include <mruby/debug.h>
 
 static mrb_irep_debug_info_file *
 get_file(mrb_irep_debug_info *info, uint32_t pc)
@@ -19,7 +19,8 @@ get_file(mrb_irep_debug_info *info, uint32_t pc)
     if (!(pc < (*it)->start_pos)) {
       ret = it + 1;
       count -= step + 1;
-    } else { count = step; }
+    }
+    else { count = step; }
   }
 
   --ret;
@@ -86,7 +87,8 @@ mrb_debug_get_line(mrb_irep *irep, uint32_t pc)
             if (!(pc < it->start_pos)) {
               ret = it + 1;
               count -= step + 1;
-            } else { count = step; }
+            }
+            else { count = step; }
           }
 
           --ret;
index 6a70eac..bb9ed8c 100644 (file)
@@ -6,11 +6,11 @@
 
 #include <string.h>
 #include <limits.h>
-#include "mruby/dump.h"
-#include "mruby/string.h"
-#include "mruby/irep.h"
-#include "mruby/numeric.h"
-#include "mruby/debug.h"
+#include <mruby/dump.h>
+#include <mruby/string.h>
+#include <mruby/irep.h>
+#include <mruby/numeric.h>
+#include <mruby/debug.h>
 
 #define FLAG_BYTEORDER_NATIVE 2
 #define FLAG_BYTEORDER_NONATIVE 0
@@ -54,7 +54,7 @@ write_irep_header(mrb_state *mrb, mrb_irep *irep, uint8_t *buf)
 {
   uint8_t *cur = buf;
 
-  cur += uint32_to_bin(get_irep_record_size_1(mrb, irep), cur);  /* record size */
+  cur += uint32_to_bin((uint32_t)get_irep_record_size_1(mrb, irep), cur);  /* record size */
   cur += uint16_to_bin((uint16_t)irep->nlocals, cur);  /* number of local variable */
   cur += uint16_to_bin((uint16_t)irep->nregs, cur);  /* number of register variable */
   cur += uint16_to_bin((uint16_t)irep->rlen, cur);  /* number of child irep */
@@ -407,7 +407,8 @@ write_lineno_record_1(mrb_state *mrb, mrb_irep *irep, uint8_t* bin)
 
   if (irep->filename) {
     filename_len = strlen(irep->filename);
-  } else {
+  }
+  else {
     filename_len = 0;
   }
   mrb_assert_int_fit(size_t, filename_len, uint16_t, UINT16_MAX);
@@ -1059,6 +1060,7 @@ mrb_dump_irep_cfunc(mrb_state *mrb, mrb_irep *irep, uint8_t flags, FILE *fp, con
       return MRB_DUMP_WRITE_FAULT;
     }
     if (fprintf(fp,
+          "extern const uint8_t %s[];\n"
           "const uint8_t\n"
           "#if defined __GNUC__\n"
           "__attribute__((aligned(%u)))\n"
@@ -1066,6 +1068,7 @@ mrb_dump_irep_cfunc(mrb_state *mrb, mrb_irep *irep, uint8_t flags, FILE *fp, con
           "__declspec(align(%u))\n"
           "#endif\n"
           "%s[] = {",
+          initname,
           (uint16_t)MRB_DUMP_ALIGNMENT, (uint16_t)MRB_DUMP_ALIGNMENT, initname) < 0) {
       mrb_free(mrb, bin);
       return MRB_DUMP_WRITE_FAULT;
index 3def9e8..adb815b 100644 (file)
@@ -4,7 +4,7 @@
 ** See Copyright Notice in mruby.h
 */
 
-#include "mruby.h"
+#include <mruby.h>
 
 void
 mrb_init_enumerable(mrb_state *mrb)
index e2bcebb..0622827 100644 (file)
@@ -7,16 +7,16 @@
 #include <errno.h>
 #include <stdarg.h>
 #include <stdlib.h>
-#include "mruby.h"
-#include "mruby/array.h"
-#include "mruby/irep.h"
-#include "mruby/proc.h"
-#include "mruby/string.h"
-#include "mruby/variable.h"
-#include "mruby/debug.h"
-#include "mruby/error.h"
-#include "mruby/class.h"
-#include "mruby/throw.h"
+#include <mruby.h>
+#include <mruby/array.h>
+#include <mruby/irep.h>
+#include <mruby/proc.h>
+#include <mruby/string.h>
+#include <mruby/variable.h>
+#include <mruby/debug.h>
+#include <mruby/error.h>
+#include <mruby/class.h>
+#include <mruby/throw.h>
 
 MRB_API mrb_value
 mrb_exc_new(mrb_state *mrb, struct RClass *c, const char *ptr, size_t len)
@@ -44,8 +44,10 @@ static mrb_value
 exc_initialize(mrb_state *mrb, mrb_value exc)
 {
   mrb_value mesg;
+  mrb_int argc;
+  mrb_value *argv;
 
-  if (mrb_get_args(mrb, "|o", &mesg) == 1) {
+  if (mrb_get_args(mrb, "|o*!", &mesg, &argv, &argc) >= 1) {
     mrb_iv_set(mrb, exc, mrb_intern_lit(mrb, "mesg"), mesg);
   }
   return exc;
@@ -60,7 +62,7 @@ exc_initialize(mrb_state *mrb, mrb_value exc)
  *  With no argument, or if the argument is the same as the receiver,
  *  return the receiver. Otherwise, create a new
  *  exception object of the same class as the receiver, but with a
- *  message equal to <code>string.to_str</code>.
+ *  message equal to <code>string</code>.
  *
  */
 
@@ -109,9 +111,7 @@ exc_to_s(mrb_state *mrb, mrb_value exc)
  *   exception.message   ->  string
  *
  * Returns the result of invoking <code>exception.to_s</code>.
- * Normally this returns the exception's message or name. By
- * supplying a to_str method, exceptions are agreeing to
- * be used where Strings are expected.
+ * Normally this returns the exception's message or name.
  */
 
 static mrb_value
@@ -135,6 +135,7 @@ exc_inspect(mrb_state *mrb, mrb_value exc)
 {
   mrb_value str, mesg, file, line;
   mrb_bool append_mesg;
+  const char *cname;
 
   mesg = mrb_attr_get(mrb, exc, mrb_intern_lit(mrb, "mesg"));
   file = mrb_attr_get(mrb, exc, mrb_intern_lit(mrb, "file"));
@@ -146,34 +147,52 @@ exc_inspect(mrb_state *mrb, mrb_value exc)
     append_mesg = RSTRING_LEN(mesg) > 0;
   }
 
-  if (!mrb_nil_p(file) && !mrb_nil_p(line)) {
-    str = mrb_str_dup(mrb, file);
-    mrb_str_cat_lit(mrb, str, ":");
-    mrb_str_append(mrb, str, line);
-    mrb_str_cat_lit(mrb, str, ": ");
+  cname = mrb_obj_classname(mrb, exc);
+  str = mrb_str_new_cstr(mrb, cname);
+  if (mrb_string_p(file) && mrb_fixnum_p(line)) {
     if (append_mesg) {
-      mrb_str_cat_str(mrb, str, mesg);
-      mrb_str_cat_lit(mrb, str, " (");
+      str = mrb_format(mrb, "%S:%S: %S (%S)", file, line, mesg, str);
     }
-    mrb_str_cat_cstr(mrb, str, mrb_obj_classname(mrb, exc));
-    if (append_mesg) {
-      mrb_str_cat_lit(mrb, str, ")");
+    else {
+      str = mrb_format(mrb, "%S:%S: %S", file, line, str);
     }
   }
+  else if (append_mesg) {
+    str = mrb_format(mrb, "%S: %S", str, mesg);
+  }
+  return str;
+}
+
+void mrb_keep_backtrace(mrb_state *mrb, mrb_value exc);
+
+static void
+set_backtrace(mrb_state *mrb, mrb_value exc, mrb_value backtrace)
+{
+  if (!mrb_array_p(backtrace)) {
+  type_err:
+    mrb_raise(mrb, E_TYPE_ERROR, "backtrace must be Array of String");
+  }
   else {
-    const char *cname = mrb_obj_classname(mrb, exc);
-    str = mrb_str_new_cstr(mrb, cname);
-    mrb_str_cat_lit(mrb, str, ": ");
-    if (append_mesg) {
-      mrb_str_cat_str(mrb, str, mesg);
-    }
-    else {
-      mrb_str_cat_cstr(mrb, str, cname);
+    const mrb_value *p = RARRAY_PTR(backtrace);
+    const mrb_value *pend = p + RARRAY_LEN(backtrace);
+
+    while (p < pend) {
+      if (!mrb_string_p(*p)) goto type_err;
+      p++;
     }
   }
-  return str;
+  mrb_iv_set(mrb, exc, mrb_intern_lit(mrb, "backtrace"), backtrace);
 }
 
+static mrb_value
+exc_set_backtrace(mrb_state *mrb, mrb_value exc)
+{
+  mrb_value backtrace;
+
+  mrb_get_args(mrb, "o", &backtrace);
+  set_backtrace(mrb, exc, backtrace);
+  return backtrace;
+}
 
 static void
 exc_debug_info(mrb_state *mrb, struct RObject *exc)
@@ -181,7 +200,6 @@ exc_debug_info(mrb_state *mrb, struct RObject *exc)
   mrb_callinfo *ci = mrb->c->ci;
   mrb_code *pc = ci->pc;
 
-  mrb_obj_iv_set(mrb, exc, mrb_intern_lit(mrb, "ciidx"), mrb_fixnum_value((mrb_int)(ci - mrb->c->cibase)));
   while (ci >= mrb->c->cibase) {
     mrb_code *err = ci->err;
 
@@ -202,13 +220,28 @@ exc_debug_info(mrb_state *mrb, struct RObject *exc)
   }
 }
 
+void
+mrb_exc_set(mrb_state *mrb, mrb_value exc)
+{
+  if (mrb_nil_p(exc)) {
+    mrb->exc = 0;
+  }
+  else {
+    mrb->exc = mrb_obj_ptr(exc);
+    if (!mrb->gc.out_of_memory) {
+      exc_debug_info(mrb, mrb->exc);
+      mrb_keep_backtrace(mrb, exc);
+    }
+  }
+}
+
 MRB_API mrb_noreturn void
 mrb_exc_raise(mrb_state *mrb, mrb_value exc)
 {
-  mrb->exc = mrb_obj_ptr(exc);
-  if (!mrb->gc.out_of_memory) {
-    exc_debug_info(mrb, mrb->exc);
+  if (!mrb_obj_is_kind_of(mrb, exc, mrb->eException_class)) {
+    mrb_raise(mrb, E_TYPE_ERROR, "exception object expected");
   }
+  mrb_exc_set(mrb, exc);
   if (!mrb->jmp) {
     mrb_p(mrb, exc);
     abort();
@@ -219,9 +252,7 @@ mrb_exc_raise(mrb_state *mrb, mrb_value exc)
 MRB_API mrb_noreturn void
 mrb_raise(mrb_state *mrb, struct RClass *c, const char *msg)
 {
-  mrb_value mesg;
-  mesg = mrb_str_new_cstr(mrb, msg);
-  mrb_exc_raise(mrb, mrb_exc_new_str(mrb, c, mesg));
+  mrb_exc_raise(mrb, mrb_exc_new_str(mrb, c, mrb_str_new_cstr(mrb, msg)));
 }
 
 MRB_API mrb_value
@@ -231,6 +262,7 @@ mrb_vformat(mrb_state *mrb, const char *format, va_list ap)
   const char *b = p;
   ptrdiff_t size;
   mrb_value ary = mrb_ary_new_capa(mrb, 4);
+  int ai = mrb_gc_arena_save(mrb);
 
   while (*p) {
     const char c = *p++;
@@ -254,14 +286,18 @@ mrb_vformat(mrb_state *mrb, const char *format, va_list ap)
         break;
       }
     }
+    mrb_gc_arena_restore(mrb, ai);
   }
   if (b == format) {
     return mrb_str_new_cstr(mrb, format);
   }
   else {
     size = p - b;
-    mrb_ary_push(mrb, ary, mrb_str_new(mrb, b, size));
-    return mrb_ary_join(mrb, ary, mrb_str_new(mrb, NULL, 0));
+    if (size > 0) {
+      mrb_ary_push(mrb, ary, mrb_str_new(mrb, b, size));
+      mrb_gc_arena_restore(mrb, ai);
+    }
+    return mrb_ary_join(mrb, ary, mrb_nil_value());
   }
 }
 
@@ -278,32 +314,41 @@ mrb_format(mrb_state *mrb, const char *format, ...)
   return str;
 }
 
+static mrb_noreturn void
+raise_va(mrb_state *mrb, struct RClass *c, const char *fmt, va_list ap, int argc, mrb_value *argv)
+{
+  mrb_value mesg;
+
+  mesg = mrb_vformat(mrb, fmt, ap);
+  if (argv == NULL) {
+    argv = &mesg;
+  }
+  else {
+    argv[0] = mesg;
+  }
+  mrb_exc_raise(mrb, mrb_obj_new(mrb, c, argc+1, argv));
+}
+
 MRB_API mrb_noreturn void
 mrb_raisef(mrb_state *mrb, struct RClass *c, const char *fmt, ...)
 {
   va_list args;
-  mrb_value mesg;
 
   va_start(args, fmt);
-  mesg = mrb_vformat(mrb, fmt, args);
+  raise_va(mrb, c, fmt, args, 0, NULL);
   va_end(args);
-  mrb_exc_raise(mrb, mrb_exc_new_str(mrb, c, mesg));
 }
 
 MRB_API mrb_noreturn void
 mrb_name_error(mrb_state *mrb, mrb_sym id, const char *fmt, ...)
 {
-  mrb_value exc;
   mrb_value argv[2];
   va_list args;
 
   va_start(args, fmt);
-  argv[0] = mrb_vformat(mrb, fmt, args);
-  va_end(args);
-
   argv[1] = mrb_symbol_value(id);
-  exc = mrb_obj_new(mrb, E_NAME_ERROR, 2, argv);
-  mrb_exc_raise(mrb, exc);
+  raise_va(mrb, E_NAME_ERROR, fmt, args, 1, argv);
+  va_end(args);
 }
 
 MRB_API void
@@ -337,14 +382,8 @@ mrb_bug(mrb_state *mrb, const char *fmt, ...)
   exit(EXIT_FAILURE);
 }
 
-static void
-set_backtrace(mrb_state *mrb, mrb_value info, mrb_value bt)
-{
-  mrb_funcall(mrb, info, "set_backtrace", 1, bt);
-}
-
-static mrb_value
-make_exception(mrb_state *mrb, int argc, const mrb_value *argv, mrb_bool isstr)
+MRB_API mrb_value
+mrb_make_exception(mrb_state *mrb, int argc, const mrb_value *argv)
 {
   mrb_value mesg;
   int n;
@@ -356,12 +395,9 @@ make_exception(mrb_state *mrb, int argc, const mrb_value *argv, mrb_bool isstr)
     case 1:
       if (mrb_nil_p(argv[0]))
         break;
-      if (isstr) {
-        mesg = mrb_check_string_type(mrb, argv[0]);
-        if (!mrb_nil_p(mesg)) {
-          mesg = mrb_exc_new_str(mrb, E_RUNTIME_ERROR, mesg);
-          break;
-        }
+      if (mrb_string_p(argv[0])) {
+        mesg = mrb_exc_new_str(mrb, E_RUNTIME_ERROR, argv[0]);
+        break;
       }
       n = 0;
       goto exception_call;
@@ -388,20 +424,14 @@ exception_call:
   }
   if (argc > 0) {
     if (!mrb_obj_is_kind_of(mrb, mesg, mrb->eException_class))
-      mrb_raise(mrb, E_TYPE_ERROR, "exception object expected");
+      mrb_raise(mrb, mrb->eException_class, "exception object expected");
     if (argc > 2)
-        set_backtrace(mrb, mesg, argv[2]);
+      set_backtrace(mrb, mesg, argv[2]);
   }
 
   return mesg;
 }
 
-MRB_API mrb_value
-mrb_make_exception(mrb_state *mrb, int argc, const mrb_value *argv)
-{
-  return make_exception(mrb, argc, argv, TRUE);
-}
-
 MRB_API void
 mrb_sys_fail(mrb_state *mrb, const char *mesg)
 {
@@ -427,19 +457,22 @@ MRB_API mrb_noreturn void
 mrb_no_method_error(mrb_state *mrb, mrb_sym id, mrb_value args, char const* fmt, ...)
 {
   mrb_value exc;
+  mrb_value argv[3];
   va_list ap;
 
   va_start(ap, fmt);
-  exc = mrb_funcall(mrb, mrb_obj_value(E_NOMETHOD_ERROR), "new", 3,
-                    mrb_vformat(mrb, fmt, ap), mrb_symbol_value(id), args);
+  argv[0] = mrb_vformat(mrb, fmt, ap);
+  argv[1] = mrb_symbol_value(id);
+  argv[2] = args;
   va_end(ap);
+  exc = mrb_obj_new(mrb, E_NOMETHOD_ERROR, 3, argv);
   mrb_exc_raise(mrb, exc);
 }
 
 void
 mrb_init_exception(mrb_state *mrb)
 {
-  struct RClass *exception, *runtime_error, *script_error;
+  struct RClass *exception, *script_error, *stack_error, *nomem_error;
 
   mrb->eException_class = exception = mrb_define_class(mrb, "Exception", mrb->object_class); /* 15.2.22 */
   MRB_SET_INSTANCE_TT(exception, MRB_TT_EXCEPTION);
@@ -450,11 +483,18 @@ mrb_init_exception(mrb_state *mrb)
   mrb_define_method(mrb, exception, "message",         exc_message,       MRB_ARGS_NONE());
   mrb_define_method(mrb, exception, "inspect",         exc_inspect,       MRB_ARGS_NONE());
   mrb_define_method(mrb, exception, "backtrace",       mrb_exc_backtrace, MRB_ARGS_NONE());
+  mrb_define_method(mrb, exception, "set_backtrace",   exc_set_backtrace, MRB_ARGS_REQ(1));
 
   mrb->eStandardError_class = mrb_define_class(mrb, "StandardError", mrb->eException_class); /* 15.2.23 */
-  runtime_error = mrb_define_class(mrb, "RuntimeError", mrb->eStandardError_class);          /* 15.2.28 */
-  mrb->nomem_err = mrb_obj_ptr(mrb_exc_new_str_lit(mrb, runtime_error, "Out of memory"));
+  mrb_define_class(mrb, "RuntimeError", mrb->eStandardError_class);          /* 15.2.28 */
   script_error = mrb_define_class(mrb, "ScriptError", mrb->eException_class);                /* 15.2.37 */
   mrb_define_class(mrb, "SyntaxError", script_error);                                        /* 15.2.38 */
-  mrb_define_class(mrb, "SystemStackError", exception);
+  stack_error = mrb_define_class(mrb, "SystemStackError", exception);
+  mrb->stack_err = mrb_obj_ptr(mrb_exc_new_str_lit(mrb, stack_error, "stack level too deep"));
+
+  nomem_error = mrb_define_class(mrb, "NoMemoryError", exception);
+  mrb->nomem_err = mrb_obj_ptr(mrb_exc_new_str_lit(mrb, nomem_error, "Out of memory"));
+#ifdef MRB_GC_FIXED_ARENA
+  mrb->arena_err = mrb_obj_ptr(mrb_exc_new_str_lit(mrb, nomem_error, "arena overflow error"));
+#endif
 }
index 0e0dacf..eb755ec 100644 (file)
@@ -1,3 +1,3 @@
 /* this header file is to be removed soon.
    added for compatibility purpose (1.0.0) */
-#include "mruby/error.h"
+#include <mruby/error.h>
index f5a5027..e0810d5 100644 (file)
@@ -4,12 +4,12 @@
 ** See Copyright Notice in mruby.h
 */
 
-#include "mruby.h"
-#include "mruby/string.h"
-#include "mruby/data.h"
-#include "mruby/class.h"
-#include "mruby/re.h"
-#include "mruby/irep.h"
+#include <mruby.h>
+#include <mruby/string.h>
+#include <mruby/data.h>
+#include <mruby/class.h>
+#include <mruby/re.h>
+#include <mruby/irep.h>
 
 MRB_API struct RData*
 mrb_data_object_alloc(mrb_state *mrb, struct RClass *klass, void *ptr, const mrb_data_type *type)
@@ -139,6 +139,7 @@ mrb_obj_id(mrb_value obj)
   case MRB_TT_EXCEPTION:
   case MRB_TT_FILE:
   case MRB_TT_DATA:
+  case MRB_TT_ISTRUCT:
   default:
     return MakeID(mrb_ptr(obj));
   }
@@ -179,7 +180,18 @@ mrb_word_boxing_cptr_value(mrb_state *mrb, void *p)
 MRB_API mrb_bool
 mrb_regexp_p(mrb_state *mrb, mrb_value v)
 {
-  return mrb_class_defined(mrb, REGEXP_CLASS) && mrb_obj_is_kind_of(mrb, v, mrb_class_get(mrb, REGEXP_CLASS));
+  if (mrb->flags & MRB_STATE_NO_REGEXP) {
+    return FALSE;
+  }
+  if ((mrb->flags & MRB_STATE_REGEXP) || mrb_class_defined(mrb, REGEXP_CLASS)) {
+    mrb->flags |= MRB_STATE_REGEXP;
+    return mrb_obj_is_kind_of(mrb, v, mrb_class_get(mrb, REGEXP_CLASS));
+  }
+  else {
+    mrb->flags |= MRB_STATE_REGEXP;
+    mrb->flags |= MRB_STATE_NO_REGEXP;
+  }
+  return FALSE;
 }
 
 #if defined _MSC_VER && _MSC_VER < 1900
index 0df0bb5..483e04c 100644 (file)
@@ -33,8 +33,8 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 #include <float.h>
 #include <ctype.h>
 
-#include "mruby.h"
-#include "mruby/string.h"
+#include <mruby.h>
+#include <mruby/string.h>
 
 struct fmt_args {
   mrb_state *mrb;
@@ -156,7 +156,7 @@ fmt_fp(struct fmt_args *f, long double y, int w, int p, int fl, int t)
 
     s=buf;
     do {
-      int x=y;
+      int x=(int)y;
       *s++=xdigits[x]|(t&32);
       y=16*(y-x);
       if (s-buf==1 && (y||p>0||(fl&ALT_FORM))) *s++='.';
@@ -184,7 +184,7 @@ fmt_fp(struct fmt_args *f, long double y, int w, int p, int fl, int t)
   else a=r=z=big+sizeof(big)/sizeof(*big) - LDBL_MANT_DIG - 1;
 
   do {
-    *z = y;
+    *z = (uint32_t)y;
     y = 1000000000*(y-*z++);
   } while (y);
 
@@ -194,7 +194,7 @@ fmt_fp(struct fmt_args *f, long double y, int w, int p, int fl, int t)
     for (d=z-1; d>=a; d--) {
       uint64_t x = ((uint64_t)*d<<sh)+carry;
       *d = x % 1000000000;
-      carry = x / 1000000000;
+      carry = (uint32_t)(x / 1000000000);
     }
     if (carry) *--a = carry;
     while (z>a && !z[-1]) z--;
@@ -253,7 +253,7 @@ fmt_fp(struct fmt_args *f, long double y, int w, int p, int fl, int t)
     if (z>d+1) z=d+1;
   }
   for (; z>a && !z[-1]; z--);
-  
+
   if ((t|32)=='g') {
     if (!p) p++;
     if (p>e && e>=-4) {
@@ -354,7 +354,7 @@ fmt_core(struct fmt_args *f, const char *fmt, mrb_float flo)
     return fmt_fp(f, flo, 0, p, 0, *fmt);
   default:
     return -1;
-  } 
+  }
 }
 
 mrb_value
index 2b0eb88..8cc8feb 100644 (file)
@@ -6,17 +6,18 @@
 
 #include <string.h>
 #include <stdlib.h>
-#include "mruby.h"
-#include "mruby/array.h"
-#include "mruby/class.h"
-#include "mruby/data.h"
-#include "mruby/hash.h"
-#include "mruby/proc.h"
-#include "mruby/range.h"
-#include "mruby/string.h"
-#include "mruby/variable.h"
-#include "mruby/gc.h"
-#include "mruby/error.h"
+#include <mruby.h>
+#include <mruby/array.h>
+#include <mruby/class.h>
+#include <mruby/data.h>
+#include <mruby/hash.h>
+#include <mruby/proc.h>
+#include <mruby/range.h>
+#include <mruby/string.h>
+#include <mruby/variable.h>
+#include <mruby/gc.h>
+#include <mruby/error.h>
+#include <mruby/throw.h>
 
 /*
   = Tri-color Incremental Garbage Collection
@@ -65,7 +66,7 @@
 
   mruby implementer and C extension library writer must insert a write
   barrier when updating a reference from a field of an object.
-  When updating a reference from a field of object A to object B, 
+  When updating a reference from a field of object A to object B,
   two different types of write barrier are available:
 
     * mrb_field_write_barrier - target B object for a mark.
@@ -109,7 +110,9 @@ typedef struct {
     struct RRange range;
     struct RData data;
     struct RProc proc;
+    struct REnv env;
     struct RException exc;
+    struct RBreak brk;
 #ifdef MRB_WORD_BOXING
     struct RFloat floatv;
     struct RCptr cptr;
@@ -137,7 +140,7 @@ gettimeofday_time(void)
   fprintf(stderr, "%s\n", with);\
   fprintf(stderr, "gc_invoke: %19.3f\n", gettimeofday_time() - program_invoke_time);\
   fprintf(stderr, "is_generational: %d\n", is_generational(gc));\
-  fprintf(stderr, "is_major_gc: %d\n", is_major_gc(mrb));\
+  fprintf(stderr, "is_major_gc: %d\n", is_major_gc(gc));\
 } while(0)
 
 #define GC_TIME_START do {\
@@ -213,8 +216,10 @@ mrb_realloc(mrb_state *mrb, void *p, size_t len)
   void *p2;
 
   p2 = mrb_realloc_simple(mrb, p, len);
-  if (!p2 && len) {
+  if (len == 0) return p2;
+  if (p2 == NULL) {
     if (mrb->gc.out_of_memory) {
+      mrb_exc_raise(mrb, mrb_obj_value(mrb->nomem_err));
       /* mrb_panic(mrb); */
     }
     else {
@@ -366,7 +371,7 @@ mrb_gc_init(mrb_state *mrb, mrb_gc *gc)
 #endif
 }
 
-static void obj_free(mrb_state *mrb, struct RBasic *obj);
+static void obj_free(mrb_state *mrb, struct RBasic *obj, int end);
 
 void
 free_heap(mrb_state *mrb, mrb_gc *gc)
@@ -380,7 +385,7 @@ free_heap(mrb_state *mrb, mrb_gc *gc)
     page = page->next;
     for (p = objects(tmp), e=p+MRB_HEAP_PAGE_SIZE; p<e; p++) {
       if (p->as.free.tt != MRB_TT_FREE)
-        obj_free(mrb, &p->as.basic);
+        obj_free(mrb, &p->as.basic, TRUE);
     }
     mrb_free(mrb, tmp);
   }
@@ -402,7 +407,7 @@ gc_protect(mrb_state *mrb, mrb_gc *gc, struct RBasic *p)
   if (gc->arena_idx >= MRB_GC_ARENA_SIZE) {
     /* arena overflow error */
     gc->arena_idx = MRB_GC_ARENA_SIZE - 4; /* force room in arena */
-    mrb_raise(mrb, E_RUNTIME_ERROR, "arena overflow error");
+    mrb_exc_raise(mrb, mrb_obj_value(mrb->arena_err));
   }
 #else
   if (gc->arena_idx >= gc->arena_capa) {
@@ -428,7 +433,7 @@ mrb_gc_protect(mrb_state *mrb, mrb_value obj)
 
    Register your object when it's exported to C world,
    without reference from Ruby world, e.g. callback
-   arguments.  Don't forget to remove the obejct using
+   arguments.  Don't forget to remove the object using
    mrb_gc_unregister, otherwise your object will leak.
 */
 
@@ -452,7 +457,7 @@ mrb_gc_unregister(mrb_state *mrb, mrb_value obj)
   mrb_sym root = mrb_intern_lit(mrb, GC_ROOT_NAME);
   mrb_value table = mrb_gv_get(mrb, root);
   struct RArray *a;
-  mrb_int i, j;
+  mrb_int i;
 
   if (mrb_nil_p(table)) return;
   if (mrb_type(table) != MRB_TT_ARRAY) {
@@ -461,12 +466,16 @@ mrb_gc_unregister(mrb_state *mrb, mrb_value obj)
   }
   a = mrb_ary_ptr(table);
   mrb_ary_modify(mrb, a);
-  for (i=j=0; i<a->len; i++) {
-    if (!mrb_obj_eq(mrb, a->ptr[i], obj)) {
-      a->ptr[j++] = a->ptr[i];
+  for (i = 0; i < ARY_LEN(a); i++) {
+    if (mrb_obj_eq(mrb, ARY_PTR(a)[i], obj)) {
+      mrb_int len = ARY_LEN(a)-1;
+      mrb_value *ptr = ARY_PTR(a);
+
+      ARY_SET_LEN(a, len);
+      memmove(&ptr[i], &ptr[i + 1], (len - i) * sizeof(mrb_value));
+      break;
     }
   }
-  a->len = j;
 }
 
 MRB_API struct RBasic*
@@ -476,6 +485,28 @@ mrb_obj_alloc(mrb_state *mrb, enum mrb_vtype ttype, struct RClass *cls)
   static const RVALUE RVALUE_zero = { { { MRB_TT_FALSE } } };
   mrb_gc *gc = &mrb->gc;
 
+  if (cls) {
+    enum mrb_vtype tt;
+
+    switch (cls->tt) {
+    case MRB_TT_CLASS:
+    case MRB_TT_SCLASS:
+    case MRB_TT_MODULE:
+    case MRB_TT_ENV:
+      break;
+    default:
+      mrb_raise(mrb, E_TYPE_ERROR, "allocation failure");
+    }
+    tt = MRB_INSTANCE_TT(cls);
+    if (tt != MRB_TT_FALSE &&
+        ttype != MRB_TT_SCLASS &&
+        ttype != MRB_TT_ICLASS &&
+        ttype != MRB_TT_ENV &&
+        ttype != tt) {
+      mrb_raisef(mrb, E_TYPE_ERROR, "allocation failure of %S", mrb_obj_value(cls));
+    }
+  }
+
 #ifdef MRB_GC_STRESS
   mrb_full_gc(mrb);
 #endif
@@ -519,51 +550,60 @@ mark_context_stack(mrb_state *mrb, struct mrb_context *c)
 {
   size_t i;
   size_t e;
+  mrb_value nil;
+  int nregs;
 
+  if (c->stack == NULL) return;
   e = c->stack - c->stbase;
-  if (c->ci) e += c->ci->nregs;
+  if (c->ci) {
+    nregs = c->ci->argc + 2;
+    if (c->ci->nregs > nregs)
+      nregs = c->ci->nregs;
+    e += nregs;
+  }
   if (c->stbase + e > c->stend) e = c->stend - c->stbase;
   for (i=0; i<e; i++) {
     mrb_value v = c->stbase[i];
 
     if (!mrb_immediate_p(v)) {
-      if (mrb_basic_ptr(v)->tt == MRB_TT_FREE) {
-        c->stbase[i] = mrb_nil_value();
-      }
-      else {
-        mrb_gc_mark(mrb, mrb_basic_ptr(v));
-      }
+      mrb_gc_mark(mrb, mrb_basic_ptr(v));
     }
   }
+  e = c->stend - c->stbase;
+  nil = mrb_nil_value();
+  for (; i<e; i++) {
+    c->stbase[i] = nil;
+  }
 }
 
 static void
 mark_context(mrb_state *mrb, struct mrb_context *c)
 {
-  int i, e = 0;
+  int i;
   mrb_callinfo *ci;
 
-  /* mark stack */
-  mark_context_stack(mrb, c);
+  if (c->status == MRB_FIBER_TERMINATED) return;
 
   /* mark VM stack */
+  mark_context_stack(mrb, c);
+
+  /* mark call stack */
   if (c->cibase) {
     for (ci = c->cibase; ci <= c->ci; ci++) {
-      if (ci->eidx > e) {
-        e = ci->eidx;
-      }
       mrb_gc_mark(mrb, (struct RBasic*)ci->env);
       mrb_gc_mark(mrb, (struct RBasic*)ci->proc);
       mrb_gc_mark(mrb, (struct RBasic*)ci->target_class);
     }
   }
   /* mark ensure stack */
-  for (i=0; i<e; i++) {
+  for (i=0; i<c->esize; i++) {
+    if (c->ensure[i] == NULL) break;
     mrb_gc_mark(mrb, (struct RBasic*)c->ensure[i]);
   }
   /* mark fibers */
-  if (c->prev && c->prev->fib) {
-    mrb_gc_mark(mrb, (struct RBasic*)c->prev->fib);
+  mrb_gc_mark(mrb, (struct RBasic*)c->fib);
+  if (c->prev) {
+    mark_context(mrb, c->prev);
   }
 }
 
@@ -613,14 +653,17 @@ gc_mark_children(mrb_state *mrb, mrb_gc *gc, struct RBasic *obj)
   case MRB_TT_ENV:
     {
       struct REnv *e = (struct REnv*)obj;
+      mrb_int i, len;
 
-      if (!MRB_ENV_STACK_SHARED_P(e)) {
-        mrb_int i, len;
-
-        len = MRB_ENV_STACK_LEN(e);
-        for (i=0; i<len; i++) {
-          mrb_gc_mark_value(mrb, e->stack[i]);
+      if (MRB_ENV_STACK_SHARED_P(e)) {
+        if (e->cxt.c->fib) {
+          mrb_gc_mark(mrb, (struct RBasic*)e->cxt.c->fib);
         }
+        break;
+      }
+      len = MRB_ENV_STACK_LEN(e);
+      for (i=0; i<len; i++) {
+        mrb_gc_mark_value(mrb, e->stack[i]);
       }
     }
     break;
@@ -638,8 +681,8 @@ gc_mark_children(mrb_state *mrb, mrb_gc *gc, struct RBasic *obj)
       struct RArray *a = (struct RArray*)obj;
       size_t i, e;
 
-      for (i=0,e=a->len; i<e; i++) {
-        mrb_gc_mark_value(mrb, a->ptr[i]);
+      for (i=0,e=ARY_LEN(a); i<e; i++) {
+        mrb_gc_mark_value(mrb, ARY_PTR(a)[i]);
       }
     }
     break;
@@ -678,9 +721,9 @@ mrb_gc_mark(mrb_state *mrb, struct RBasic *obj)
 }
 
 static void
-obj_free(mrb_state *mrb, struct RBasic *obj)
+obj_free(mrb_state *mrb, struct RBasic *obj, int end)
 {
-  DEBUG(printf("obj_free(%p,tt=%d)\n",obj,obj->tt));
+  DEBUG(fprintf(stderr, "obj_free(%p,tt=%d)\n",obj,obj->tt));
   switch (obj->tt) {
     /* immediate - no mark */
   case MRB_TT_TRUE:
@@ -697,6 +740,9 @@ obj_free(mrb_state *mrb, struct RBasic *obj)
 #endif
 
   case MRB_TT_OBJECT:
+    mrb_gc_free_iv(mrb, (struct RObject*)obj);
+    break;
+
   case MRB_TT_EXCEPTION:
     mrb_gc_free_iv(mrb, (struct RObject*)obj);
     break;
@@ -715,10 +761,12 @@ obj_free(mrb_state *mrb, struct RBasic *obj)
     {
       struct REnv *e = (struct REnv*)obj;
 
-      if (!MRB_ENV_STACK_SHARED_P(e)) {
-        mrb_free(mrb, e->stack);
-        e->stack = NULL;
+      if (MRB_ENV_STACK_SHARED_P(e)) {
+        /* cannot be freed */
+        return;
       }
+      mrb_free(mrb, e->stack);
+      e->stack = NULL;
     }
     break;
 
@@ -726,16 +774,28 @@ obj_free(mrb_state *mrb, struct RBasic *obj)
     {
       struct mrb_context *c = ((struct RFiber*)obj)->cxt;
 
-      if (c != mrb->root_c)
+      if (!end && c && c != mrb->root_c) {
+        mrb_callinfo *ci = c->ci;
+        mrb_callinfo *ce = c->cibase;
+
+        while (ce <= ci) {
+          struct REnv *e = ci->env;
+          if (e && !is_dead(&mrb->gc, e) &&
+              e->tt == MRB_TT_ENV && MRB_ENV_STACK_SHARED_P(e)) {
+            mrb_env_unshare(mrb, e);
+          }
+          ci--;
+        }
         mrb_free_context(mrb, c);
+      }
     }
     break;
 
   case MRB_TT_ARRAY:
     if (ARY_SHARED_P(obj))
-      mrb_ary_decref(mrb, ((struct RArray*)obj)->aux.shared);
-    else
-      mrb_free(mrb, ((struct RArray*)obj)->ptr);
+      mrb_ary_decref(mrb, ((struct RArray*)obj)->as.heap.aux.shared);
+    else if (!ARY_EMBED_P(obj))
+      mrb_free(mrb, ((struct RArray*)obj)->as.heap.ptr);
     break;
 
   case MRB_TT_HASH:
@@ -780,7 +840,7 @@ obj_free(mrb_state *mrb, struct RBasic *obj)
 static void
 root_scan_phase(mrb_state *mrb, mrb_gc *gc)
 {
-  size_t i, e;
+  int i, e;
 
   if (!is_minor_gc(gc)) {
     gc->gray_list = NULL;
@@ -794,19 +854,40 @@ root_scan_phase(mrb_state *mrb, mrb_gc *gc)
   }
   /* mark class hierarchy */
   mrb_gc_mark(mrb, (struct RBasic*)mrb->object_class);
+
+  /* mark built-in classes */
+  mrb_gc_mark(mrb, (struct RBasic*)mrb->class_class);
+  mrb_gc_mark(mrb, (struct RBasic*)mrb->module_class);
+  mrb_gc_mark(mrb, (struct RBasic*)mrb->proc_class);
+  mrb_gc_mark(mrb, (struct RBasic*)mrb->string_class);
+  mrb_gc_mark(mrb, (struct RBasic*)mrb->array_class);
+  mrb_gc_mark(mrb, (struct RBasic*)mrb->hash_class);
+
+  mrb_gc_mark(mrb, (struct RBasic*)mrb->float_class);
+  mrb_gc_mark(mrb, (struct RBasic*)mrb->fixnum_class);
+  mrb_gc_mark(mrb, (struct RBasic*)mrb->true_class);
+  mrb_gc_mark(mrb, (struct RBasic*)mrb->false_class);
+  mrb_gc_mark(mrb, (struct RBasic*)mrb->nil_class);
+  mrb_gc_mark(mrb, (struct RBasic*)mrb->symbol_class);
+  mrb_gc_mark(mrb, (struct RBasic*)mrb->kernel_module);
+
+  mrb_gc_mark(mrb, (struct RBasic*)mrb->eException_class);
+  mrb_gc_mark(mrb, (struct RBasic*)mrb->eStandardError_class);
+
   /* mark top_self */
   mrb_gc_mark(mrb, (struct RBasic*)mrb->top_self);
   /* mark exception */
   mrb_gc_mark(mrb, (struct RBasic*)mrb->exc);
   /* mark pre-allocated exception */
   mrb_gc_mark(mrb, (struct RBasic*)mrb->nomem_err);
+  mrb_gc_mark(mrb, (struct RBasic*)mrb->stack_err);
+#ifdef MRB_GC_FIXED_ARENA
+  mrb_gc_mark(mrb, (struct RBasic*)mrb->arena_err);
+#endif
 
-  mark_context(mrb, mrb->root_c);
-  if (mrb->root_c->fib) {
-    mrb_gc_mark(mrb, (struct RBasic*)mrb->root_c->fib);
-  }
+  mark_context(mrb, mrb->c);
   if (mrb->root_c != mrb->c) {
-    mark_context(mrb, mrb->c);
+    mark_context(mrb, mrb->root_c);
   }
 }
 
@@ -858,7 +939,7 @@ gc_gray_mark(mrb_state *mrb, mrb_gc *gc, struct RBasic *obj)
       children += i;
 
       /* mark ensure stack */
-      children += (c->ci) ? c->ci->eidx : 0;
+      children += c->eidx;
 
       /* mark closure */
       if (c->cibase) {
@@ -872,7 +953,7 @@ gc_gray_mark(mrb_state *mrb, mrb_gc *gc, struct RBasic *obj)
   case MRB_TT_ARRAY:
     {
       struct RArray *a = (struct RArray*)obj;
-      children += a->len;
+      children += ARY_LEN(a);
     }
     break;
 
@@ -919,7 +1000,16 @@ incremental_marking_phase(mrb_state *mrb, mrb_gc *gc, size_t limit)
 static void
 final_marking_phase(mrb_state *mrb, mrb_gc *gc)
 {
-  mark_context_stack(mrb, mrb->root_c);
+  int i, e;
+
+  /* mark arena */
+  for (i=0,e=gc->arena_idx; i<e; i++) {
+    mrb_gc_mark(mrb, gc->arena[i]);
+  }
+  mrb_gc_mark_gv(mrb);
+  mark_context(mrb, mrb->c);
+  mark_context(mrb, mrb->root_c);
+  mrb_gc_mark(mrb, (struct RBasic*)mrb->exc);
   gc_mark_gray_list(mrb, gc);
   mrb_assert(gc->gray_list == NULL);
   gc->gray_list = gc->atomic_gray_list;
@@ -957,16 +1047,21 @@ incremental_sweep_phase(mrb_state *mrb, mrb_gc *gc, size_t limit)
     while (p<e) {
       if (is_dead(gc, &p->as.basic)) {
         if (p->as.basic.tt != MRB_TT_FREE) {
-          obj_free(mrb, &p->as.basic);
-          p->as.free.next = page->freelist;
-          page->freelist = (struct RBasic*)p;
-          freed++;
+          obj_free(mrb, &p->as.basic, FALSE);
+          if (p->as.basic.tt == MRB_TT_FREE) {
+            p->as.free.next = page->freelist;
+            page->freelist = (struct RBasic*)p;
+            freed++;
+          }
+          else {
+            dead_slot = FALSE;
+          }
         }
       }
       else {
         if (!is_generational(gc))
           paint_partial_white(gc, &p->as.basic); /* next gc target */
-        dead_slot = 0;
+        dead_slot = FALSE;
       }
       p++;
     }
@@ -1079,7 +1174,7 @@ mrb_incremental_gc(mrb_state *mrb)
 {
   mrb_gc *gc = &mrb->gc;
 
-  if (gc->disabled) return;
+  if (gc->disabled || gc->iterating) return;
 
   GC_INVOKE_TIME_REPORT("mrb_incremental_gc()");
   GC_TIME_START;
@@ -1119,7 +1214,7 @@ mrb_full_gc(mrb_state *mrb)
 {
   mrb_gc *gc = &mrb->gc;
 
-  if (gc->disabled) return;
+  if (gc->disabled || gc->iterating) return;
 
   GC_INVOKE_TIME_REPORT("mrb_full_gc()");
   GC_TIME_START;
@@ -1151,34 +1246,6 @@ mrb_garbage_collect(mrb_state *mrb)
   mrb_full_gc(mrb);
 }
 
-MRB_API int
-mrb_gc_arena_save(mrb_state *mrb)
-{
-  return mrb->gc.arena_idx;
-}
-
-MRB_API void
-mrb_gc_arena_restore(mrb_state *mrb, int idx)
-{
-  mrb_gc *gc = &mrb->gc;
-
-#ifndef MRB_GC_FIXED_ARENA
-  int capa = gc->arena_capa;
-
-  if (idx < capa / 2) {
-    capa = (int)(capa * 0.66);
-    if (capa < MRB_GC_ARENA_SIZE) {
-      capa = MRB_GC_ARENA_SIZE;
-    }
-    if (capa != gc->arena_capa) {
-      gc->arena = (struct RBasic**)mrb_realloc(mrb, gc->arena, sizeof(struct RBasic*)*capa);
-      gc->arena_capa = capa;
-    }
-  }
-#endif
-  gc->arena_idx = idx;
-}
-
 /*
  * Field write barrier
  *   Paint obj(Black) -> value(White) to obj(Black) -> value(Gray).
@@ -1356,6 +1423,10 @@ gc_step_ratio_set(mrb_state *mrb, mrb_value obj)
 static void
 change_gen_gc_mode(mrb_state *mrb, mrb_gc *gc, mrb_bool enable)
 {
+  if (gc->disabled || gc->iterating) {
+    mrb_raise(mrb, E_RUNTIME_ERROR, "generational mode changed when GC disabled");
+    return;
+  }
   if (is_generational(gc) && !enable) {
     clear_all_old(mrb, gc);
     mrb_assert(gc->state == MRB_GC_STATE_ROOT);
@@ -1407,17 +1478,18 @@ gc_generational_mode_set(mrb_state *mrb, mrb_value self)
 static void
 gc_each_objects(mrb_state *mrb, mrb_gc *gc, mrb_each_object_callback *callback, void *data)
 {
-  mrb_heap_page* page = gc->heaps;
+  mrb_heap_page* page;
 
+  page = gc->heaps;
   while (page != NULL) {
-    RVALUE *p, *pend;
+    RVALUE *p;
+    int i;
 
     p = objects(page);
-    pend = p + MRB_HEAP_PAGE_SIZE;
-    for (;p < pend; p++) {
-      (*callback)(mrb, &p->as.basic, data);
+    for (i=0; i < MRB_HEAP_PAGE_SIZE; i++) {
+      if ((*callback)(mrb, &p[i].as.basic, data) == MRB_EACH_OBJ_BREAK)
+        return;
     }
-
     page = page->next;
   }
 }
@@ -1425,7 +1497,27 @@ gc_each_objects(mrb_state *mrb, mrb_gc *gc, mrb_each_object_callback *callback,
 void
 mrb_objspace_each_objects(mrb_state *mrb, mrb_each_object_callback *callback, void *data)
 {
-  gc_each_objects(mrb, &mrb->gc, callback, data);
+  mrb_bool iterating = mrb->gc.iterating;
+
+  mrb->gc.iterating = TRUE;
+  if (iterating) {
+    gc_each_objects(mrb, &mrb->gc, callback, data);
+  }
+  else {
+    struct mrb_jmpbuf *prev_jmp = mrb->jmp;
+    struct mrb_jmpbuf c_jmp;
+
+    MRB_TRY(&c_jmp) {
+      mrb->jmp = &c_jmp;
+      gc_each_objects(mrb, &mrb->gc, callback, data);
+      mrb->jmp = prev_jmp;
+      mrb->gc.iterating = iterating; 
+   } MRB_CATCH(&c_jmp) {
+      mrb->gc.iterating = iterating;
+      mrb->jmp = prev_jmp;
+      MRB_THROW(prev_jmp);
+    } MRB_END_EXC(&c_jmp);
+  }
 }
 
 #ifdef GC_TEST
index 22937df..27d25b3 100644 (file)
@@ -4,13 +4,13 @@
 ** See Copyright Notice in mruby.h
 */
 
-#include "mruby.h"
-#include "mruby/array.h"
-#include "mruby/class.h"
-#include "mruby/hash.h"
-#include "mruby/khash.h"
-#include "mruby/string.h"
-#include "mruby/variable.h"
+#include <mruby.h>
+#include <mruby/array.h>
+#include <mruby/class.h>
+#include <mruby/hash.h>
+#include <mruby/khash.h>
+#include <mruby/string.h>
+#include <mruby/variable.h>
 
 /* a function to get hash value of a float number */
 mrb_int mrb_float_id(mrb_float f);
@@ -91,12 +91,6 @@ mrb_hash_ht_hash_equal(mrb_state *mrb, mrb_value a, mrb_value b)
   }
 }
 
-typedef struct {
-  mrb_value v;
-  mrb_int n;
-} mrb_hash_value;
-
-KHASH_DECLARE(ht, mrb_value, mrb_hash_value, TRUE)
 KHASH_DEFINE (ht, mrb_value, mrb_hash_value, TRUE, mrb_hash_ht_hash_func, mrb_hash_ht_hash_equal)
 
 static void mrb_hash_modify(mrb_state *mrb, mrb_value hash);
@@ -104,9 +98,9 @@ static void mrb_hash_modify(mrb_state *mrb, mrb_value hash);
 static inline mrb_value
 mrb_hash_ht_key(mrb_state *mrb, mrb_value key)
 {
-  if (mrb_string_p(key) && !RSTR_FROZEN_P(mrb_str_ptr(key))) {
+  if (mrb_string_p(key) && !MRB_FROZEN_P(mrb_str_ptr(key))) {
     key = mrb_str_dup(mrb, key);
-    RSTR_SET_FROZEN_FLAG(mrb_str_ptr(key));
+    MRB_SET_FROZEN_FLAG(mrb_str_ptr(key));
   }
   return key;
 }
@@ -146,7 +140,7 @@ mrb_gc_free_hash(mrb_state *mrb, struct RHash *hash)
 
 
 MRB_API mrb_value
-mrb_hash_new_capa(mrb_state *mrb, int capa)
+mrb_hash_new_capa(mrb_state *mrb, mrb_int capa)
 {
   struct RHash *h;
 
@@ -165,11 +159,15 @@ mrb_hash_new(mrb_state *mrb)
   return mrb_hash_new_capa(mrb, 0);
 }
 
+static mrb_value mrb_hash_default(mrb_state *mrb, mrb_value hash);
+static mrb_value hash_default(mrb_state *mrb, mrb_value hash, mrb_value key);
+
 MRB_API mrb_value
 mrb_hash_get(mrb_state *mrb, mrb_value hash, mrb_value key)
 {
   khash_t(ht) *h = RHASH_TBL(hash);
   khiter_t k;
+  mrb_sym mid;
 
   if (h) {
     k = kh_get(ht, mrb, h, key);
@@ -177,11 +175,12 @@ mrb_hash_get(mrb_state *mrb, mrb_value hash, mrb_value key)
       return kh_value(h, k).v;
   }
 
-  /* not found */
-  if (MRB_RHASH_PROCDEFAULT_P(hash)) {
-    return mrb_funcall(mrb, RHASH_PROCDEFAULT(hash), "call", 2, hash, key);
+  mid = mrb_intern_lit(mrb, "default");
+  if (mrb_func_basic_p(mrb, hash, mid, mrb_hash_default)) {
+    return hash_default(mrb, hash, key);
   }
-  return RHASH_IFNONE(hash);
+  /* xxx mrb_funcall_tailcall(mrb, hash, "default", 1, key); */
+  return mrb_funcall_argv(mrb, hash, mid, 1, &key);
 }
 
 MRB_API mrb_value
@@ -233,12 +232,13 @@ mrb_hash_dup(mrb_state *mrb, mrb_value hash)
   struct RHash* ret;
   khash_t(ht) *h, *ret_h;
   khiter_t k, ret_k;
+  mrb_value ifnone, vret;
 
   h = RHASH_TBL(hash);
   ret = (struct RHash*)mrb_obj_alloc(mrb, MRB_TT_HASH, mrb->hash_class);
   ret->ht = kh_init(ht, mrb);
 
-  if (kh_size(h) > 0) {
+  if (h && kh_size(h) > 0) {
     ret_h = ret->ht;
 
     for (k = kh_begin(h); k != kh_end(h); k++) {
@@ -246,12 +246,24 @@ mrb_hash_dup(mrb_state *mrb, mrb_value hash)
         int ai = mrb_gc_arena_save(mrb);
         ret_k = kh_put(ht, mrb, ret_h, KEY(kh_key(h, k)));
         mrb_gc_arena_restore(mrb, ai);
-        kh_val(ret_h, ret_k) = kh_val(h, k);
+        kh_val(ret_h, ret_k).v = kh_val(h, k).v;
+        kh_val(ret_h, ret_k).n = kh_size(ret_h)-1;
       }
     }
   }
 
-  return mrb_obj_value(ret);
+  if (MRB_RHASH_DEFAULT_P(hash)) {
+    ret->flags |= MRB_HASH_DEFAULT;
+  }
+  if (MRB_RHASH_PROCDEFAULT_P(hash)) {
+    ret->flags |= MRB_HASH_PROC_DEFAULT;
+  }
+  vret = mrb_obj_value(ret);
+  ifnone = RHASH_IFNONE(hash);
+  if (!mrb_nil_p(ifnone)) {
+      mrb_iv_set(mrb, vret, mrb_intern_lit(mrb, "ifnone"), ifnone);
+  }
+  return vret;
 }
 
 MRB_API mrb_value
@@ -274,6 +286,9 @@ mrb_hash_tbl(mrb_state *mrb, mrb_value hash)
 static void
 mrb_hash_modify(mrb_state *mrb, mrb_value hash)
 {
+  if (MRB_FROZEN_P(mrb_hash_ptr(hash))) {
+    mrb_raise(mrb, E_RUNTIME_ERROR, "can't modify frozen hash");
+  }
   mrb_hash_tbl(mrb, hash);
 }
 
@@ -329,7 +344,10 @@ mrb_hash_init(mrb_state *mrb, mrb_value hash)
     RHASH(hash)->flags |= MRB_HASH_PROC_DEFAULT;
     ifnone = block;
   }
-  mrb_iv_set(mrb, hash, mrb_intern_lit(mrb, "ifnone"), ifnone);
+  if (!mrb_nil_p(ifnone)) {
+    RHASH(hash)->flags |= MRB_HASH_DEFAULT;
+    mrb_iv_set(mrb, hash, mrb_intern_lit(mrb, "ifnone"), ifnone);
+  }
   return hash;
 }
 
@@ -356,6 +374,20 @@ mrb_hash_aget(mrb_state *mrb, mrb_value self)
   return mrb_hash_get(mrb, self, key);
 }
 
+static mrb_value
+hash_default(mrb_state *mrb, mrb_value hash, mrb_value key)
+{
+  if (MRB_RHASH_DEFAULT_P(hash)) {
+    if (MRB_RHASH_PROCDEFAULT_P(hash)) {
+      return mrb_funcall(mrb, RHASH_PROCDEFAULT(hash), "call", 2, hash, key);
+    }
+    else {
+      return RHASH_IFNONE(hash);
+    }
+  }
+  return mrb_nil_value();
+}
+
 /* 15.2.13.4.5  */
 /*
  *  call-seq:
@@ -385,13 +417,16 @@ mrb_hash_default(mrb_state *mrb, mrb_value hash)
   mrb_bool given;
 
   mrb_get_args(mrb, "|o?", &key, &given);
-  if (MRB_RHASH_PROCDEFAULT_P(hash)) {
-    if (!given) return mrb_nil_value();
-    return mrb_funcall(mrb, RHASH_PROCDEFAULT(hash), "call", 2, hash, key);
-  }
-  else {
-    return RHASH_IFNONE(hash);
+  if (MRB_RHASH_DEFAULT_P(hash)) {
+    if (MRB_RHASH_PROCDEFAULT_P(hash)) {
+      if (!given) return mrb_nil_value();
+      return mrb_funcall(mrb, RHASH_PROCDEFAULT(hash), "call", 2, hash, key);
+    }
+    else {
+      return RHASH_IFNONE(hash);
+    }
   }
+  return mrb_nil_value();
 }
 
 /* 15.2.13.4.6  */
@@ -423,8 +458,13 @@ mrb_hash_set_default(mrb_state *mrb, mrb_value hash)
   mrb_get_args(mrb, "o", &ifnone);
   mrb_hash_modify(mrb, hash);
   mrb_iv_set(mrb, hash, mrb_intern_lit(mrb, "ifnone"), ifnone);
-  RHASH(hash)->flags &= ~(MRB_HASH_PROC_DEFAULT);
-
+  RHASH(hash)->flags &= ~MRB_HASH_PROC_DEFAULT;
+  if (!mrb_nil_p(ifnone)) {
+    RHASH(hash)->flags |= MRB_HASH_DEFAULT;
+  }
+  else {
+    RHASH(hash)->flags &= ~MRB_HASH_DEFAULT;
+  }
   return ifnone;
 }
 
@@ -474,7 +514,14 @@ mrb_hash_set_default_proc(mrb_state *mrb, mrb_value hash)
   mrb_get_args(mrb, "o", &ifnone);
   mrb_hash_modify(mrb, hash);
   mrb_iv_set(mrb, hash, mrb_intern_lit(mrb, "ifnone"), ifnone);
-  RHASH(hash)->flags |= MRB_HASH_PROC_DEFAULT;
+  if (!mrb_nil_p(ifnone)) {
+    RHASH(hash)->flags |= MRB_HASH_PROC_DEFAULT;
+    RHASH(hash)->flags |= MRB_HASH_DEFAULT;
+  }
+  else {
+    RHASH(hash)->flags &= ~MRB_HASH_DEFAULT;
+    RHASH(hash)->flags &= ~MRB_HASH_PROC_DEFAULT;
+  }
 
   return ifnone;
 }
@@ -529,6 +576,7 @@ mrb_hash_delete(mrb_state *mrb, mrb_value self)
   mrb_value key;
 
   mrb_get_args(mrb, "o", &key);
+  mrb_hash_modify(mrb, self);
   return mrb_hash_delete_key(mrb, self, key);
 }
 
@@ -567,12 +615,15 @@ mrb_hash_shift(mrb_state *mrb, mrb_value hash)
     }
   }
 
-  if (MRB_RHASH_PROCDEFAULT_P(hash)) {
-    return mrb_funcall(mrb, RHASH_PROCDEFAULT(hash), "call", 2, hash, mrb_nil_value());
-  }
-  else {
-    return RHASH_IFNONE(hash);
+  if (MRB_RHASH_DEFAULT_P(hash)) {
+    if (MRB_RHASH_PROCDEFAULT_P(hash)) {
+      return mrb_funcall(mrb, RHASH_PROCDEFAULT(hash), "call", 2, hash, mrb_nil_value());
+    }
+    else {
+      return RHASH_IFNONE(hash);
+    }
   }
+  return mrb_nil_value();
 }
 
 /* 15.2.13.4.4  */
@@ -592,6 +643,7 @@ mrb_hash_clear(mrb_state *mrb, mrb_value hash)
 {
   khash_t(ht) *h = RHASH_TBL(hash);
 
+  mrb_hash_modify(mrb, hash);
   if (h) kh_clear(ht, mrb, h);
   return hash;
 }
@@ -699,19 +751,26 @@ mrb_hash_keys(mrb_state *mrb, mrb_value hash)
 {
   khash_t(ht) *h = RHASH_TBL(hash);
   khiter_t k;
+  mrb_int end;
   mrb_value ary;
   mrb_value *p;
 
   if (!h || kh_size(h) == 0) return mrb_ary_new(mrb);
   ary = mrb_ary_new_capa(mrb, kh_size(h));
-  mrb_ary_set(mrb, ary, kh_size(h)-1, mrb_nil_value());
-  p = mrb_ary_ptr(ary)->ptr;
+  end = kh_size(h)-1;
+  mrb_ary_set(mrb, ary, end, mrb_nil_value());
+  p = RARRAY_PTR(ary);
   for (k = kh_begin(h); k != kh_end(h); k++) {
     if (kh_exist(h, k)) {
       mrb_value kv = kh_key(h, k);
       mrb_hash_value hv = kh_value(h, k);
 
-      p[hv.n] = kv;
+      if (hv.n <= end) {
+        p[hv.n] = kv;
+      }
+      else {
+        p[end] = kv;
+      }
     }
   }
   return ary;
@@ -730,7 +789,7 @@ mrb_hash_keys(mrb_state *mrb, mrb_value hash)
  *
  */
 
-static mrb_value
+MRB_API mrb_value
 mrb_hash_values(mrb_state *mrb, mrb_value hash)
 {
   khash_t(ht) *h = RHASH_TBL(hash);
index 955d6e3..afd6997 100644 (file)
@@ -4,7 +4,7 @@
 ** See Copyright Notice in mruby.h
 */
 
-#include "mruby.h"
+#include <mruby.h>
 
 void mrb_init_symtbl(mrb_state*);
 void mrb_init_class(mrb_state*);
@@ -13,9 +13,9 @@ void mrb_init_kernel(mrb_state*);
 void mrb_init_comparable(mrb_state*);
 void mrb_init_enumerable(mrb_state*);
 void mrb_init_symbol(mrb_state*);
+void mrb_init_string(mrb_state*);
 void mrb_init_exception(mrb_state*);
 void mrb_init_proc(mrb_state*);
-void mrb_init_string(mrb_state*);
 void mrb_init_array(mrb_state*);
 void mrb_init_hash(mrb_state*);
 void mrb_init_numeric(mrb_state*);
@@ -38,9 +38,9 @@ mrb_init_core(mrb_state *mrb)
   mrb_init_enumerable(mrb); DONE;
 
   mrb_init_symbol(mrb); DONE;
+  mrb_init_string(mrb); DONE;
   mrb_init_exception(mrb); DONE;
   mrb_init_proc(mrb); DONE;
-  mrb_init_string(mrb); DONE;
   mrb_init_array(mrb); DONE;
   mrb_init_hash(mrb); DONE;
   mrb_init_numeric(mrb); DONE;
index a6c967b..bd21ddf 100644 (file)
@@ -4,13 +4,15 @@
 ** See Copyright Notice in mruby.h
 */
 
-#include "mruby.h"
-#include "mruby/array.h"
-#include "mruby/class.h"
-#include "mruby/proc.h"
-#include "mruby/string.h"
-#include "mruby/variable.h"
-#include "mruby/error.h"
+#include <mruby.h>
+#include <mruby/array.h>
+#include <mruby/hash.h>
+#include <mruby/class.h>
+#include <mruby/proc.h>
+#include <mruby/string.h>
+#include <mruby/variable.h>
+#include <mruby/error.h>
+#include <mruby/istruct.h>
 
 typedef enum {
   NOEX_PUBLIC    = 0x00,
@@ -26,15 +28,21 @@ typedef enum {
   NOEX_RESPONDS  = 0x80
 } mrb_method_flag_t;
 
-static mrb_bool
-mrb_obj_basic_to_s_p(mrb_state *mrb, mrb_value obj)
+MRB_API mrb_bool
+mrb_func_basic_p(mrb_state *mrb, mrb_value obj, mrb_sym mid, mrb_func_t func)
 {
-  struct RProc *me = mrb_method_search(mrb, mrb_class(mrb, obj), mrb_intern_lit(mrb, "to_s"));
-  if (MRB_PROC_CFUNC_P(me) && (me->body.func == mrb_any_to_s))
+  struct RProc *me = mrb_method_search(mrb, mrb_class(mrb, obj), mid);
+  if (MRB_PROC_CFUNC_P(me) && (me->body.func == func))
     return TRUE;
   return FALSE;
 }
 
+static mrb_bool
+mrb_obj_basic_to_s_p(mrb_state *mrb, mrb_value obj)
+{
+  return mrb_func_basic_p(mrb, obj, mrb_intern_lit(mrb, "to_s"), mrb_any_to_s);
+}
+
 /* 15.3.1.3.17 */
 /*
  *  call-seq:
@@ -58,55 +66,6 @@ mrb_obj_inspect(mrb_state *mrb, mrb_value obj)
   return mrb_any_to_s(mrb, obj);
 }
 
-/* 15.3.1.3.1  */
-/* 15.3.1.3.10 */
-/* 15.3.1.3.11 */
-/*
- *  call-seq:
- *     obj == other        -> true or false
- *     obj.equal?(other)   -> true or false
- *     obj.eql?(other)     -> true or false
- *
- *  Equality---At the <code>Object</code> level, <code>==</code> returns
- *  <code>true</code> only if <i>obj</i> and <i>other</i> are the
- *  same object. Typically, this method is overridden in descendant
- *  classes to provide class-specific meaning.
- *
- *  Unlike <code>==</code>, the <code>equal?</code> method should never be
- *  overridden by subclasses: it is used to determine object identity
- *  (that is, <code>a.equal?(b)</code> iff <code>a</code> is the same
- *  object as <code>b</code>).
- *
- *  The <code>eql?</code> method returns <code>true</code> if
- *  <i>obj</i> and <i>anObject</i> have the same value. Used by
- *  <code>Hash</code> to test members for equality.  For objects of
- *  class <code>Object</code>, <code>eql?</code> is synonymous with
- *  <code>==</code>. Subclasses normally continue this tradition, but
- *  there are exceptions. <code>Numeric</code> types, for example,
- *  perform type conversion across <code>==</code>, but not across
- *  <code>eql?</code>, so:
- *
- *     1 == 1.0     #=> true
- *     1.eql? 1.0   #=> false
- */
-static mrb_value
-mrb_obj_equal_m(mrb_state *mrb, mrb_value self)
-{
-  mrb_value arg;
-
-  mrb_get_args(mrb, "o", &arg);
-  return mrb_bool_value(mrb_obj_equal(mrb, self, arg));
-}
-
-static mrb_value
-mrb_obj_not_equal_m(mrb_state *mrb, mrb_value self)
-{
-  mrb_value arg;
-
-  mrb_get_args(mrb, "o", &arg);
-  return mrb_bool_value(!mrb_equal(mrb, self, arg));
-}
-
 /* 15.3.1.3.2  */
 /*
  *  call-seq:
@@ -142,7 +101,7 @@ mrb_equal_m(mrb_state *mrb, mrb_value self)
  *  <code>:name</code> notation, which returns the symbol id of
  *  <code>name</code>. Replaces the deprecated <code>Object#id</code>.
  */
-static mrb_value
+mrb_value
 mrb_obj_id_m(mrb_state *mrb, mrb_value self)
 {
   return mrb_fixnum_value(mrb_obj_id(self));
@@ -177,38 +136,38 @@ mrb_f_block_given_p_m(mrb_state *mrb, mrb_value self)
 {
   mrb_callinfo *ci = mrb->c->ci;
   mrb_value *bp;
-  mrb_bool given_p;
 
   bp = ci->stackent + 1;
   ci--;
   if (ci <= mrb->c->cibase) {
-    given_p = FALSE;
+    return mrb_false_value();
   }
-  else {
-    /* block_given? called within block; check upper scope */
-    if (ci->proc->env) {
-      struct REnv *e = ci->proc->env;
-      mrb_value *sp;
+  /* block_given? called within block; check upper scope */
+  if (ci->proc->env) {
+    struct REnv *e = ci->proc->env;
 
-      while (e->c) {
-        e = (struct REnv*)e->c;
-      }
-      sp = e->stack;
-      if (sp) {
-        /* top-level does not have block slot (alway false) */
-        if (sp == mrb->c->stbase)
-          return mrb_false_value();
-        ci = mrb->c->cibase + e->cioff;
-        bp = ci[1].stackent + 1;
-      }
+    while (e->c) {
+      e = (struct REnv*)e->c;
+    }
+    /* top-level does not have block slot (always false) */
+    if (e->stack == mrb->c->stbase)
+      return mrb_false_value();
+    if (e->stack && e->cioff < 0) {
+      /* use saved block arg position */
+      bp = &e->stack[-e->cioff];
+      ci = 0;                 /* no callinfo available */
     }
-    if (ci->argc > 0) {
-      bp += ci->argc;
+    else {
+      ci = e->cxt.c->cibase + e->cioff;
+      bp = ci[1].stackent + 1;
     }
-    given_p = !mrb_nil_p(*bp);
   }
-
-  return mrb_bool_value(given_p);
+  if (ci && ci->argc > 0) {
+    bp += ci->argc;
+  }
+  if (mrb_nil_p(*bp))
+    return mrb_false_value();
+  return mrb_true_value();
 }
 
 /* 15.3.1.3.7  */
@@ -240,11 +199,13 @@ mrb_singleton_class_clone(mrb_state *mrb, mrb_value obj)
     /* copy singleton(unnamed) class */
     struct RClass *clone = (struct RClass*)mrb_obj_alloc(mrb, klass->tt, mrb->class_class);
 
-    if ((mrb_type(obj) == MRB_TT_CLASS) || (mrb_type(obj) == MRB_TT_SCLASS)) {
-      clone->c = clone;
-    }
-    else {
+    switch (mrb_type(obj)) {
+    case MRB_TT_CLASS:
+    case MRB_TT_SCLASS:
+      break;
+    default:
       clone->c = mrb_singleton_class_clone(mrb, mrb_obj_value(klass));
+      break;
     }
     clone->super = klass->super;
     if (klass->iv) {
@@ -282,8 +243,14 @@ copy_class(mrb_state *mrb, mrb_value dst, mrb_value src)
     c1->super = mrb_class_ptr(mrb_obj_dup(mrb, mrb_obj_value(c0)));
     c1->super->flags |= MRB_FLAG_IS_ORIGIN;
   }
-  dc->mt = kh_copy(mt, mrb, sc->mt);
+  if (sc->mt) {
+    dc->mt = kh_copy(mt, mrb, sc->mt);
+  }
+  else {
+    dc->mt = kh_init(mt, mrb);
+  }
   dc->super = sc->super;
+  MRB_SET_INSTANCE_TT(dc, MRB_INSTANCE_TT(sc));
 }
 
 static void
@@ -301,6 +268,9 @@ init_copy(mrb_state *mrb, mrb_value dest, mrb_value obj)
     case MRB_TT_EXCEPTION:
       mrb_iv_copy(mrb, dest, obj);
       break;
+    case MRB_TT_ISTRUCT:
+      mrb_istruct_copy(dest, obj);
+      break;
 
     default:
       break;
@@ -348,6 +318,7 @@ mrb_obj_clone(mrb_state *mrb, mrb_value self)
   }
   p = (struct RObject*)mrb_obj_alloc(mrb, mrb_type(self), mrb_obj_class(mrb, self));
   p->c = mrb_singleton_class_clone(mrb, self);
+  mrb_field_write_barrier(mrb, (struct RBasic*)p, (struct RBasic*)p->c);
   clone = mrb_obj_value(p);
   init_copy(mrb, clone, self);
 
@@ -445,6 +416,52 @@ mrb_obj_extend_m(mrb_state *mrb, mrb_value self)
   return mrb_obj_extend(mrb, argc, argv, self);
 }
 
+static mrb_value
+mrb_obj_freeze(mrb_state *mrb, mrb_value self)
+{
+  struct RBasic *b;
+
+  switch (mrb_type(self)) {
+    case MRB_TT_FALSE:
+    case MRB_TT_TRUE:
+    case MRB_TT_FIXNUM:
+    case MRB_TT_SYMBOL:
+    case MRB_TT_FLOAT:
+      return self;
+    default:
+      break;
+  }
+
+  b = mrb_basic_ptr(self);
+  if (!MRB_FROZEN_P(b)) {
+    MRB_SET_FROZEN_FLAG(b);
+  }
+  return self;
+}
+
+static mrb_value
+mrb_obj_frozen(mrb_state *mrb, mrb_value self)
+{
+  struct RBasic *b;
+
+  switch (mrb_type(self)) {
+    case MRB_TT_FALSE:
+    case MRB_TT_TRUE:
+    case MRB_TT_FIXNUM:
+    case MRB_TT_SYMBOL:
+    case MRB_TT_FLOAT:
+      return mrb_true_value();
+    default:
+      break;
+  }
+
+  b = mrb_basic_ptr(self);
+  if (!MRB_FROZEN_P(b)) {
+    return mrb_false_value();
+  }
+  return mrb_true_value();
+}
+
 /* 15.3.1.3.15 */
 /*
  *  call-seq:
@@ -477,9 +494,6 @@ mrb_obj_init_copy(mrb_state *mrb, mrb_value self)
 }
 
 
-/* implementation of instance_eval */
-mrb_value mrb_obj_instance_eval(mrb_state*, mrb_value);
-
 MRB_API mrb_bool
 mrb_obj_is_instance_of(mrb_state *mrb, mrb_value obj, struct RClass* c)
 {
@@ -722,9 +736,7 @@ mrb_obj_singleton_methods(mrb_state *mrb, mrb_bool recur, mrb_value obj)
 static mrb_value
 mrb_obj_methods(mrb_state *mrb, mrb_bool recur, mrb_value obj, mrb_method_flag_t flag)
 {
-  if (recur)
-    return mrb_class_instance_method_list(mrb, recur, mrb_class(mrb, obj), 0);
-  return mrb_obj_singleton_methods(mrb, recur, obj);
+  return mrb_class_instance_method_list(mrb, recur, mrb_class(mrb, obj), 0);
 }
 /* 15.3.1.3.31 */
 /*
@@ -852,15 +864,14 @@ mrb_f_raise(mrb_state *mrb, mrb_value self)
     mrb_raise(mrb, E_RUNTIME_ERROR, "");
     break;
   case 1:
-    a[1] = mrb_check_string_type(mrb, a[0]);
-    if (!mrb_nil_p(a[1])) {
+    if (mrb_string_p(a[0])) {
+      a[1] = a[0];
       argc = 2;
       a[0] = mrb_obj_value(E_RUNTIME_ERROR);
     }
     /* fall through */
   default:
     exc = mrb_make_exception(mrb, argc, a);
-    mrb_obj_iv_set(mrb, mrb_obj_ptr(exc), mrb_intern_lit(mrb, "lastpc"), mrb_cptr_value(mrb, mrb->c->ci->pc));
     mrb_exc_raise(mrb, exc);
     break;
   }
@@ -904,6 +915,79 @@ mrb_obj_remove_instance_variable(mrb_state *mrb, mrb_value self)
   return val;
 }
 
+void
+mrb_method_missing(mrb_state *mrb, mrb_sym name, mrb_value self, mrb_value args)
+{
+  mrb_sym inspect;
+  mrb_value repr;
+
+  inspect = mrb_intern_lit(mrb, "inspect");
+  if (mrb->c->ci > mrb->c->cibase && mrb->c->ci[-1].mid == inspect) {
+    /* method missing in inspect; avoid recursion */
+    repr = mrb_any_to_s(mrb, self);
+  }
+  else if (mrb_respond_to(mrb, self, inspect) && mrb->c->ci - mrb->c->cibase < 16) {
+    repr = mrb_funcall_argv(mrb, self, inspect, 0, 0);
+    if (mrb_string_p(repr) && RSTRING_LEN(repr) > 64) {
+      repr = mrb_any_to_s(mrb, self);
+    }
+  }
+  else {
+    repr = mrb_any_to_s(mrb, self);
+  }
+
+  mrb_no_method_error(mrb, name, args, "undefined method '%S' for %S",
+                      mrb_sym2str(mrb, name), repr);
+}
+
+/* 15.3.1.3.30 */
+/*
+ *  call-seq:
+ *     obj.method_missing(symbol [, *args] )   -> result
+ *
+ *  Invoked by Ruby when <i>obj</i> is sent a message it cannot handle.
+ *  <i>symbol</i> is the symbol for the method called, and <i>args</i>
+ *  are any arguments that were passed to it. By default, the interpreter
+ *  raises an error when this method is called. However, it is possible
+ *  to override the method to provide more dynamic behavior.
+ *  If it is decided that a particular method should not be handled, then
+ *  <i>super</i> should be called, so that ancestors can pick up the
+ *  missing method.
+ *  The example below creates
+ *  a class <code>Roman</code>, which responds to methods with names
+ *  consisting of roman numerals, returning the corresponding integer
+ *  values.
+ *
+ *     class Roman
+ *       def romanToInt(str)
+ *         # ...
+ *       end
+ *       def method_missing(methId)
+ *         str = methId.id2name
+ *         romanToInt(str)
+ *       end
+ *     end
+ *
+ *     r = Roman.new
+ *     r.iv      #=> 4
+ *     r.xxiii   #=> 23
+ *     r.mm      #=> 2000
+ */
+#ifdef MRB_DEFAULT_METHOD_MISSING
+static mrb_value
+mrb_obj_missing(mrb_state *mrb, mrb_value mod)
+{
+  mrb_sym name;
+  mrb_value *a;
+  mrb_int alen;
+
+  mrb_get_args(mrb, "n*!", &name, &a, &alen);
+  mrb_method_missing(mrb, name, mod, mrb_ary_new_from_values(mrb, alen, a));
+  /* not reached */
+  return mrb_nil_value();
+}
+#endif
+
 static inline mrb_bool
 basic_obj_respond_to(mrb_state *mrb, mrb_value obj, mrb_sym id, int pub)
 {
@@ -939,14 +1023,17 @@ obj_respond_to(mrb_state *mrb, mrb_value self)
   }
   else {
     mrb_value tmp;
-    if (!mrb_string_p(mid)) {
+    if (mrb_string_p(mid)) {
+      tmp = mrb_check_intern_str(mrb, mid);
+    }
+    else {
       tmp = mrb_check_string_type(mrb, mid);
       if (mrb_nil_p(tmp)) {
         tmp = mrb_inspect(mrb, mid);
         mrb_raisef(mrb, E_TYPE_ERROR, "%S is not a symbol", tmp);
       }
+      tmp = mrb_check_intern_str(mrb, tmp);
     }
-    tmp = mrb_check_intern_str(mrb, mid);
     if (mrb_nil_p(tmp)) {
       respond_to_p = FALSE;
     }
@@ -962,10 +1049,11 @@ obj_respond_to(mrb_state *mrb, mrb_value self)
   if (!respond_to_p) {
     rtm_id = mrb_intern_lit(mrb, "respond_to_missing?");
     if (basic_obj_respond_to(mrb, self, rtm_id, !priv)) {
-      mrb_value args[2];
+      mrb_value args[2], v;
       args[0] = mid;
       args[1] = mrb_bool_value(priv);
-      return mrb_funcall_argv(mrb, self, rtm_id, 2, args);
+      v = mrb_funcall_argv(mrb, self, rtm_id, 2, args);
+      return mrb_bool_value(mrb_bool(v));
     }
   }
   return mrb_bool_value(respond_to_p);
@@ -1050,8 +1138,8 @@ mrb_obj_ceqq(mrb_state *mrb, mrb_value self)
 static mrb_value
 mrb_local_variables(mrb_state *mrb, mrb_value self)
 {
-  mrb_value ret;
   struct RProc *proc;
+  mrb_value vars;
   struct mrb_irep *irep;
   size_t i;
 
@@ -1065,22 +1153,23 @@ mrb_local_variables(mrb_state *mrb, mrb_value self)
   if (!irep->lv) {
     return mrb_ary_new(mrb);
   }
-  ret = mrb_ary_new_capa(mrb, irep->nlocals - 1);
+  vars = mrb_hash_new(mrb);
   for (i = 0; i + 1 < irep->nlocals; ++i) {
     if (irep->lv[i].name) {
-      mrb_ary_push(mrb, ret, mrb_symbol_value(irep->lv[i].name));
+      mrb_hash_set(mrb, vars, mrb_symbol_value(irep->lv[i].name), mrb_true_value());
     }
   }
   if (proc->env) {
     struct REnv *e = proc->env;
 
     while (e) {
-      if (!MRB_PROC_CFUNC_P(mrb->c->cibase[e->cioff].proc)) {
-        irep = mrb->c->cibase[e->cioff].proc->body.irep;
+      if (MRB_ENV_STACK_SHARED_P(e) &&
+          !MRB_PROC_CFUNC_P(e->cxt.c->cibase[e->cioff].proc)) {
+        irep = e->cxt.c->cibase[e->cioff].proc->body.irep;
         if (irep->lv) {
           for (i = 0; i + 1 < irep->nlocals; ++i) {
             if (irep->lv[i].name) {
-              mrb_ary_push(mrb, ret, mrb_symbol_value(irep->lv[i].name));
+              mrb_hash_set(mrb, vars, mrb_symbol_value(irep->lv[i].name), mrb_true_value());
             }
           }
         }
@@ -1089,9 +1178,10 @@ mrb_local_variables(mrb_state *mrb, mrb_value self)
     }
   }
 
-  return ret;
+  return mrb_hash_keys(mrb, vars);
 }
 
+mrb_value mrb_obj_equal_m(mrb_state *mrb, mrb_value);
 void
 mrb_init_kernel(mrb_state *mrb)
 {
@@ -1107,11 +1197,7 @@ mrb_init_kernel(mrb_state *mrb)
 
   mrb_define_method(mrb, krn, "singleton_class",            mrb_singleton_class,             MRB_ARGS_NONE());
 
-  mrb_define_method(mrb, krn, "==",                         mrb_obj_equal_m,                 MRB_ARGS_REQ(1));    /* 15.3.1.3.1  */
-  mrb_define_method(mrb, krn, "!=",                         mrb_obj_not_equal_m,             MRB_ARGS_REQ(1));
   mrb_define_method(mrb, krn, "===",                        mrb_equal_m,                     MRB_ARGS_REQ(1));    /* 15.3.1.3.2  */
-  mrb_define_method(mrb, krn, "__id__",                     mrb_obj_id_m,                    MRB_ARGS_NONE());    /* 15.3.1.3.3  */
-  mrb_define_method(mrb, krn, "__send__",                   mrb_f_send,                      MRB_ARGS_ANY());     /* 15.3.1.3.4  */
   mrb_define_method(mrb, krn, "block_given?",               mrb_f_block_given_p_m,           MRB_ARGS_NONE());    /* 15.3.1.3.6  */
   mrb_define_method(mrb, krn, "class",                      mrb_obj_class_m,                 MRB_ARGS_NONE());    /* 15.3.1.3.7  */
   mrb_define_method(mrb, krn, "clone",                      mrb_obj_clone,                   MRB_ARGS_NONE());    /* 15.3.1.3.8  */
@@ -1119,11 +1205,12 @@ mrb_init_kernel(mrb_state *mrb)
   mrb_define_method(mrb, krn, "eql?",                       mrb_obj_equal_m,                 MRB_ARGS_REQ(1));    /* 15.3.1.3.10 */
   mrb_define_method(mrb, krn, "equal?",                     mrb_obj_equal_m,                 MRB_ARGS_REQ(1));    /* 15.3.1.3.11 */
   mrb_define_method(mrb, krn, "extend",                     mrb_obj_extend_m,                MRB_ARGS_ANY());     /* 15.3.1.3.13 */
+  mrb_define_method(mrb, krn, "freeze",                     mrb_obj_freeze,                  MRB_ARGS_NONE());
+  mrb_define_method(mrb, krn, "frozen?",                    mrb_obj_frozen,                  MRB_ARGS_NONE());
   mrb_define_method(mrb, krn, "global_variables",           mrb_f_global_variables,          MRB_ARGS_NONE());    /* 15.3.1.3.14 */
   mrb_define_method(mrb, krn, "hash",                       mrb_obj_hash,                    MRB_ARGS_NONE());    /* 15.3.1.3.15 */
   mrb_define_method(mrb, krn, "initialize_copy",            mrb_obj_init_copy,               MRB_ARGS_REQ(1));    /* 15.3.1.3.16 */
   mrb_define_method(mrb, krn, "inspect",                    mrb_obj_inspect,                 MRB_ARGS_NONE());    /* 15.3.1.3.17 */
-  mrb_define_method(mrb, krn, "instance_eval",              mrb_obj_instance_eval,           MRB_ARGS_ANY());     /* 15.3.1.3.18 */
   mrb_define_method(mrb, krn, "instance_of?",               obj_is_instance_of,              MRB_ARGS_REQ(1));    /* 15.3.1.3.19 */
   mrb_define_method(mrb, krn, "instance_variable_defined?", mrb_obj_ivar_defined,            MRB_ARGS_REQ(1));    /* 15.3.1.3.20 */
   mrb_define_method(mrb, krn, "instance_variable_get",      mrb_obj_ivar_get,                MRB_ARGS_REQ(1));    /* 15.3.1.3.21 */
@@ -1133,6 +1220,9 @@ mrb_init_kernel(mrb_state *mrb)
   mrb_define_method(mrb, krn, "iterator?",                  mrb_f_block_given_p_m,           MRB_ARGS_NONE());    /* 15.3.1.3.25 */
   mrb_define_method(mrb, krn, "kind_of?",                   mrb_obj_is_kind_of_m,            MRB_ARGS_REQ(1));    /* 15.3.1.3.26 */
   mrb_define_method(mrb, krn, "local_variables",            mrb_local_variables,             MRB_ARGS_NONE());    /* 15.3.1.3.28 */
+#ifdef MRB_DEFAULT_METHOD_MISSING
+  mrb_define_method(mrb, krn, "method_missing",             mrb_obj_missing,                 MRB_ARGS_ANY());     /* 15.3.1.3.30 */
+#endif
   mrb_define_method(mrb, krn, "methods",                    mrb_obj_methods_m,               MRB_ARGS_OPT(1));    /* 15.3.1.3.31 */
   mrb_define_method(mrb, krn, "nil?",                       mrb_false,                       MRB_ARGS_NONE());    /* 15.3.1.3.32 */
   mrb_define_method(mrb, krn, "object_id",                  mrb_obj_id_m,                    MRB_ARGS_NONE());    /* 15.3.1.3.33 */
index aa8c501..4a0dcb0 100644 (file)
@@ -7,12 +7,12 @@
 #include <limits.h>
 #include <stdlib.h>
 #include <string.h>
-#include "mruby/dump.h"
-#include "mruby/irep.h"
-#include "mruby/proc.h"
-#include "mruby/string.h"
-#include "mruby/debug.h"
-#include "mruby/error.h"
+#include <mruby/dump.h>
+#include <mruby/irep.h>
+#include <mruby/proc.h>
+#include <mruby/string.h>
+#include <mruby/debug.h>
+#include <mruby/error.h>
 
 #if SIZE_MAX < UINT32_MAX
 # error size_t must be at least 32 bits wide
@@ -523,7 +523,7 @@ read_binary_header(const uint8_t *bin, size_t *bin_size, uint16_t *crc, uint8_t
   if (memcmp(header->binary_ident, RITE_BINARY_IDENT, sizeof(header->binary_ident)) == 0) {
     if (bigendian_p())
       *flags |= FLAG_BYTEORDER_NATIVE;
-    else 
+    else
       *flags |= FLAG_BYTEORDER_BIG;
   }
   else if (memcmp(header->binary_ident, RITE_BINARY_IDENT_LIL, sizeof(header->binary_ident)) == 0) {
@@ -544,7 +544,7 @@ read_binary_header(const uint8_t *bin, size_t *bin_size, uint16_t *crc, uint8_t
   return MRB_DUMP_OK;
 }
 
-MRB_API mrb_irep*
+static mrb_irep*
 read_irep(mrb_state *mrb, const uint8_t *bin, uint8_t flags)
 {
   int result;
@@ -602,7 +602,7 @@ read_irep(mrb_state *mrb, const uint8_t *bin, uint8_t flags)
   return irep;
 }
 
-MRB_API mrb_irep*
+mrb_irep*
 mrb_read_irep(mrb_state *mrb, const uint8_t *bin)
 {
 #ifdef MRB_USE_ETEXT_EDATA
@@ -614,10 +614,12 @@ mrb_read_irep(mrb_state *mrb, const uint8_t *bin)
   return read_irep(mrb, bin, flags);
 }
 
+void mrb_exc_set(mrb_state *mrb, mrb_value exc);
+
 static void
 irep_error(mrb_state *mrb)
 {
-  mrb->exc = mrb_obj_ptr(mrb_exc_new_str_lit(mrb, E_SCRIPT_ERROR, "irep load error"));
+  mrb_exc_set(mrb, mrb_exc_new_str_lit(mrb, E_SCRIPT_ERROR, "irep load error"));
 }
 
 MRB_API mrb_value
@@ -633,7 +635,7 @@ mrb_load_irep_cxt(mrb_state *mrb, const uint8_t *bin, mrbc_context *c)
   proc = mrb_proc_new(mrb, irep);
   mrb_irep_decref(mrb, irep);
   if (c && c->no_exec) return mrb_obj_value(proc);
-  return mrb_toplevel_run(mrb, proc);
+  return mrb_top_run(mrb, proc, mrb_top_self(mrb), 0);
 }
 
 MRB_API mrb_value
@@ -644,7 +646,7 @@ mrb_load_irep(mrb_state *mrb, const uint8_t *bin)
 
 #ifndef MRB_DISABLE_STDIO
 
-MRB_API mrb_irep*
+mrb_irep*
 mrb_read_irep_file(mrb_state *mrb, FILE* fp)
 {
   mrb_irep *irep = NULL;
@@ -695,7 +697,7 @@ mrb_load_irep_file_cxt(mrb_state *mrb, FILE* fp, mrbc_context *c)
   mrb_irep_decref(mrb, irep);
   if (c && c->dump_result) mrb_codedump_all(mrb, proc);
   if (c && c->no_exec) return mrb_obj_value(proc);
-  val = mrb_toplevel_run(mrb, proc);
+  val = mrb_top_run(mrb, proc, mrb_top_self(mrb), 0);
   return val;
 }
 
index abde441..4558493 100644 (file)
@@ -4,11 +4,11 @@ MRuby.each_target do
   current_build_dir = "#{build_dir}/#{relative_from_root}"
 
   objs = Dir.glob("#{current_dir}/*.c").map { |f|
-    next nil if cxx_abi_enabled? and f =~ /(error|vm).c$/
+    next nil if cxx_exception_enabled? and f =~ /(error|vm).c$/
     objfile(f.pathmap("#{current_build_dir}/%n"))
   }.compact
 
-  if cxx_abi_enabled?
+  if cxx_exception_enabled?
     objs += %w(vm error).map { |v| compile_as_cxx "#{current_dir}/#{v}.c", "#{current_build_dir}/#{v}.cxx" }
   end
   self.libmruby << objs
index 6cceaeb..be71f11 100644 (file)
@@ -9,12 +9,14 @@
 #include <math.h>
 #include <stdlib.h>
 
-#include "mruby.h"
-#include "mruby/array.h"
-#include "mruby/numeric.h"
-#include "mruby/string.h"
+#include <mruby.h>
+#include <mruby/array.h>
+#include <mruby/numeric.h>
+#include <mruby/string.h>
+#include <mruby/class.h>
 
 #ifdef MRB_USE_FLOAT
+#define trunc(f) truncf(f)
 #define floor(f) floorf(f)
 #define ceil(f) ceilf(f)
 #define fmod(x,y) fmodf(x,y)
@@ -50,14 +52,32 @@ static mrb_value
 num_pow(mrb_state *mrb, mrb_value x)
 {
   mrb_value y;
-  mrb_float d, yv;
+  mrb_float d;
 
   mrb_get_args(mrb, "o", &y);
-  yv = mrb_to_flo(mrb, y);
-  d = pow(mrb_to_flo(mrb, x), yv);
-  if (mrb_fixnum_p(x) && mrb_fixnum_p(y) && FIXABLE(d) && yv > 0 && 
-      (d < 0 || (d > 0 && (mrb_int)d > 0)))
-    return mrb_fixnum_value((mrb_int)d);
+  if (mrb_fixnum_p(x) && mrb_fixnum_p(y)) {
+    /* try ipow() */
+    mrb_int base = mrb_fixnum(x);
+    mrb_int exp = mrb_fixnum(y);
+    mrb_int result = 1;
+
+    if (exp < 0) goto float_pow;
+    for (;;) {
+      if (exp & 1) {
+        if (mrb_int_mul_overflow(result, base, &result)) {
+          goto float_pow;
+        }
+      }
+      exp >>= 1;
+      if (exp == 0) break;
+      if (mrb_int_mul_overflow(base, base, &base)) {
+        goto float_pow;
+      }
+    }
+    return mrb_fixnum_value(result);
+  }
+ float_pow:
+  d = pow(mrb_to_flo(mrb, x), mrb_to_flo(mrb, y));
   return mrb_float_value(mrb, d);
 }
 
@@ -167,7 +187,12 @@ flodivmod(mrb_state *mrb, mrb_float x, mrb_float y, mrb_float *divp, mrb_float *
   mrb_float mod;
 
   if (y == 0.0) {
-    div = INFINITY;
+    if (x == 0.0) {
+      div = NAN;
+    }
+    else {
+      div = INFINITY;
+    }
     mod = NAN;
   }
   else {
@@ -271,6 +296,128 @@ flo_eq(mrb_state *mrb, mrb_value x)
   }
 }
 
+static int64_t
+value_int64(mrb_state *mrb, mrb_value x)
+{
+  switch (mrb_type(x)) {
+  case MRB_TT_FIXNUM:
+    return (int64_t)mrb_fixnum(x);
+    break;
+  case MRB_TT_FLOAT:
+    return (int64_t)mrb_float(x);
+  default:
+    mrb_raise(mrb, E_TYPE_ERROR, "cannot convert to Integer");
+    break;
+  }
+  /* not reached */
+  return 0;
+}
+
+static mrb_value
+int64_value(mrb_state *mrb, int64_t v)
+{
+  if (FIXABLE(v)) {
+    return mrb_fixnum_value((mrb_int)v);
+  }
+  return mrb_float_value(mrb, (mrb_float)v);
+}
+
+static mrb_value
+flo_rev(mrb_state *mrb, mrb_value x)
+{
+  int64_t v1;
+  mrb_get_args(mrb, "");
+  v1 = (int64_t)mrb_float(x);
+  return int64_value(mrb, ~v1);
+}
+
+static mrb_value
+flo_and(mrb_state *mrb, mrb_value x)
+{
+  mrb_value y;
+  int64_t v1, v2;
+  mrb_get_args(mrb, "o", &y);
+
+  v1 = (int64_t)mrb_float(x);
+  v2 = value_int64(mrb, y);
+  return int64_value(mrb, v1 & v2);
+}
+
+static mrb_value
+flo_or(mrb_state *mrb, mrb_value x)
+{
+  mrb_value y;
+  int64_t v1, v2;
+  mrb_get_args(mrb, "o", &y);
+
+  v1 = (int64_t)mrb_float(x);
+  v2 = value_int64(mrb, y);
+  return int64_value(mrb, v1 | v2);
+}
+
+static mrb_value
+flo_xor(mrb_state *mrb, mrb_value x)
+{
+  mrb_value y;
+  int64_t v1, v2;
+  mrb_get_args(mrb, "o", &y);
+
+  v1 = (int64_t)mrb_float(x);
+  v2 = value_int64(mrb, y);
+  return int64_value(mrb, v1 ^ v2);
+}
+
+static mrb_value
+flo_shift(mrb_state *mrb, mrb_value x, mrb_int width)
+{
+  mrb_float val;
+
+  if (width == 0) {
+    return x;
+  }
+  val = mrb_float(x);
+  if (width < 0) {
+    while (width++) {
+      val /= 2;
+    }
+#if defined(_ISOC99_SOURCE)
+    val = trunc(val);
+#else
+    val = val > 0 ? floor(val) : ceil(val);
+#endif
+    if (val == 0 && mrb_float(x) < 0) {
+      return mrb_fixnum_value(-1);
+    }
+  }
+  else {
+    while (width--) {
+      val *= 2;
+    }
+  }
+  if (FIXABLE_FLOAT(val)) {
+    return mrb_fixnum_value((mrb_int)val);
+  }
+  return mrb_float_value(mrb, val);
+}
+
+static mrb_value
+flo_lshift(mrb_state *mrb, mrb_value x)
+{
+  mrb_int width;
+
+  mrb_get_args(mrb, "i", &width);
+  return flo_shift(mrb, x, -width);
+}
+
+static mrb_value
+flo_rshift(mrb_state *mrb, mrb_value x)
+{
+  mrb_int width;
+
+  mrb_get_args(mrb, "i", &width);
+  return flo_shift(mrb, x, width);
+}
+
 /* 15.2.8.3.18 */
 /*
  * call-seq:
@@ -284,13 +431,13 @@ flo_hash(mrb_state *mrb, mrb_value num)
   mrb_float d;
   char *c;
   size_t i;
-  int hash;
+  mrb_int hash;
 
   d = (mrb_float)mrb_fixnum(num);
   /* normalize -0.0 to 0.0 */
   if (d == 0) d = 0.0;
   c = (char*)&d;
-  for (hash=0, i=0; i<sizeof(mrb_float);i++) {
+  for (hash=0,i=0; i<sizeof(mrb_float); i++) {
     hash = (hash * 971) ^ (unsigned char)c[i];
   }
   if (hash < 0) hash = -hash;
@@ -352,6 +499,17 @@ flo_finite_p(mrb_state *mrb, mrb_value num)
   return mrb_bool_value(isfinite(mrb_float(num)));
 }
 
+void
+mrb_check_num_exact(mrb_state *mrb, mrb_float num)
+{
+  if (isinf(num)) {
+    mrb_raise(mrb, E_FLOATDOMAIN_ERROR, num < 0 ? "-Infinity" : "Infinity");
+  }
+  if (isnan(num)) {
+    mrb_raise(mrb, E_FLOATDOMAIN_ERROR, "NaN");
+  }
+}
+
 /* 15.2.9.3.10 */
 /*
  *  call-seq:
@@ -370,7 +528,8 @@ flo_floor(mrb_state *mrb, mrb_value num)
 {
   mrb_float f = floor(mrb_float(num));
 
-  if (!FIXABLE(f)) {
+  mrb_check_num_exact(mrb, f);
+  if (!FIXABLE_FLOAT(f)) {
     return mrb_float_value(mrb, f);
   }
   return mrb_fixnum_value((mrb_int)f);
@@ -395,7 +554,8 @@ flo_ceil(mrb_state *mrb, mrb_value num)
 {
   mrb_float f = ceil(mrb_float(num));
 
-  if (!FIXABLE(f)) {
+  mrb_check_num_exact(mrb, f);
+  if (!FIXABLE_FLOAT(f)) {
     return mrb_float_value(mrb, f);
   }
   return mrb_fixnum_value((mrb_int)f);
@@ -442,14 +602,10 @@ flo_round(mrb_state *mrb, mrb_value num)
   mrb_get_args(mrb, "|i", &ndigits);
   number = mrb_float(num);
 
-  if (isinf(number)) {
-    if (0 < ndigits) return num;
-    else mrb_raise(mrb, E_FLOATDOMAIN_ERROR, number < 0 ? "-Infinity" : "Infinity");
-  }
-  if (isnan(number)) {
-    if (0 < ndigits) return num;
-    else mrb_raise(mrb, E_FLOATDOMAIN_ERROR, "NaN");
+  if (0 < ndigits && (isinf(number) || isnan(number))) {
+    return num;
   }
+  mrb_check_num_exact(mrb, number);
 
   f = 1.0;
   i = ndigits >= 0 ? ndigits : -ndigits;
@@ -505,7 +661,8 @@ flo_truncate(mrb_state *mrb, mrb_value num)
   if (f > 0.0) f = floor(f);
   if (f < 0.0) f = ceil(f);
 
-  if (!FIXABLE(f)) {
+  mrb_check_num_exact(mrb, f);
+  if (!FIXABLE_FLOAT(f)) {
     return mrb_float_value(mrb, f);
   }
   return mrb_fixnum_value((mrb_int)f);
@@ -541,10 +698,6 @@ int_to_i(mrb_state *mrb, mrb_value num)
   return num;
 }
 
-/*tests if N*N would overflow*/
-#define SQRT_INT_MAX ((mrb_int)1<<((MRB_INT_BIT-1-MRB_FIXNUM_SHIFT)/2))
-#define FIT_SQRT_INT(n) (((n)<SQRT_INT_MAX)&&((n)>=-SQRT_INT_MAX))
-
 mrb_value
 mrb_fixnum_mul(mrb_state *mrb, mrb_value x, mrb_value y)
 {
@@ -552,18 +705,14 @@ mrb_fixnum_mul(mrb_state *mrb, mrb_value x, mrb_value y)
 
   a = mrb_fixnum(x);
   if (mrb_fixnum_p(y)) {
-    mrb_float c;
-    mrb_int b;
+    mrb_int b, c;
 
     if (a == 0) return x;
     b = mrb_fixnum(y);
-    if (FIT_SQRT_INT(a) && FIT_SQRT_INT(b))
-      return mrb_fixnum_value(a*b);
-    c = a * b;
-    if ((a != 0 && c/a != b) || !FIXABLE(c)) {
-      return mrb_float_value(mrb, (mrb_float)a*(mrb_float)b);
+    if (mrb_int_mul_overflow(a, b, &c)) {
+      return mrb_float_value(mrb, (mrb_float)a * (mrb_float)b);
     }
-    return mrb_fixnum_value((mrb_int)c);
+    return mrb_fixnum_value(c);
   }
   return mrb_float_value(mrb, (mrb_float)a * mrb_to_flo(mrb, y));
 }
@@ -667,8 +816,10 @@ fix_divmod(mrb_state *mrb, mrb_value x)
     mrb_int div, mod;
 
     if (mrb_fixnum(y) == 0) {
-      return mrb_assoc_new(mrb, mrb_float_value(mrb, INFINITY),
-        mrb_float_value(mrb, NAN));
+      return mrb_assoc_new(mrb, ((mrb_fixnum(x) == 0) ?
+                                 mrb_float_value(mrb, NAN):
+                                 mrb_float_value(mrb, INFINITY)),
+                           mrb_float_value(mrb, NAN));
     }
     fixdivmod(mrb, mrb_fixnum(x), mrb_fixnum(y), &div, &mod);
     return mrb_assoc_new(mrb, mrb_fixnum_value(div), mrb_fixnum_value(mod));
@@ -678,7 +829,7 @@ fix_divmod(mrb_state *mrb, mrb_value x)
     mrb_value a, b;
 
     flodivmod(mrb, (mrb_float)mrb_fixnum(x), mrb_to_flo(mrb, y), &div, &mod);
-    a = mrb_float_value(mrb, (mrb_int)div);
+    a = mrb_float_value(mrb, div);
     b = mrb_float_value(mrb, mod);
     return mrb_assoc_new(mrb, a, b);
   }
@@ -694,7 +845,7 @@ flo_divmod(mrb_state *mrb, mrb_value x)
   mrb_get_args(mrb, "o", &y);
 
   flodivmod(mrb, mrb_float(x), mrb_to_flo(mrb, y), &div, &mod);
-  a = mrb_float_value(mrb, (mrb_int)div);
+  a = mrb_float_value(mrb, div);
   b = mrb_float_value(mrb, mod);
   return mrb_assoc_new(mrb, a, b);
 }
@@ -746,17 +897,13 @@ fix_rev(mrb_state *mrb, mrb_value num)
   return mrb_fixnum_value(~val);
 }
 
-static mrb_value
-bit_coerce(mrb_state *mrb, mrb_value x)
-{
-  while (!mrb_fixnum_p(x)) {
-    if (mrb_float_p(x)) {
-      mrb_raise(mrb, E_TYPE_ERROR, "can't convert Float into Integer");
-    }
-    x = mrb_to_int(mrb, x);
-  }
-  return x;
-}
+static mrb_value flo_and(mrb_state *mrb, mrb_value x);
+static mrb_value flo_or(mrb_state *mrb, mrb_value x);
+static mrb_value flo_xor(mrb_state *mrb, mrb_value x);
+#define bit_op(x,y,op1,op2) do {\
+  if (mrb_fixnum_p(y)) return mrb_fixnum_value(mrb_fixnum(x) op2 mrb_fixnum(y));\
+  return flo_ ## op1(mrb, mrb_float_value(mrb, mrb_fixnum(x)));\
+} while(0)
 
 /* 15.2.8.3.9  */
 /*
@@ -772,9 +919,7 @@ fix_and(mrb_state *mrb, mrb_value x)
   mrb_value y;
 
   mrb_get_args(mrb, "o", &y);
-
-  y = bit_coerce(mrb, y);
-  return mrb_fixnum_value(mrb_fixnum(x) & mrb_fixnum(y));
+  bit_op(x, y, and, &);
 }
 
 /* 15.2.8.3.10 */
@@ -791,9 +936,7 @@ fix_or(mrb_state *mrb, mrb_value x)
   mrb_value y;
 
   mrb_get_args(mrb, "o", &y);
-
-  y = bit_coerce(mrb, y);
-  return mrb_fixnum_value(mrb_fixnum(x) | mrb_fixnum(y));
+  bit_op(x, y, or, |);
 }
 
 /* 15.2.8.3.11 */
@@ -810,9 +953,7 @@ fix_xor(mrb_state *mrb, mrb_value x)
   mrb_value y;
 
   mrb_get_args(mrb, "o", &y);
-
-  y = bit_coerce(mrb, y);
-  return mrb_fixnum_value(mrb_fixnum(x) ^ mrb_fixnum(y));
+  bit_op(x, y, or, ^);
 }
 
 #define NUMERIC_SHIFT_WIDTH_MAX (MRB_INT_BIT-1)
@@ -820,21 +961,40 @@ fix_xor(mrb_state *mrb, mrb_value x)
 static mrb_value
 lshift(mrb_state *mrb, mrb_int val, mrb_int width)
 {
-  mrb_assert(width > 0);
-  if (width > NUMERIC_SHIFT_WIDTH_MAX) {
+  if (width < 0) {              /* mrb_int overflow */
+    return mrb_float_value(mrb, INFINITY);
+  }
+  if (val > 0) {
+    if ((width > NUMERIC_SHIFT_WIDTH_MAX) ||
+        (val   > (MRB_INT_MAX >> width))) {
+      goto bit_overflow;
+    }
+    return mrb_fixnum_value(val << width);
+  }
+  else {
+    if ((width > NUMERIC_SHIFT_WIDTH_MAX) ||
+        (val   < (MRB_INT_MIN >> width))) {
+      goto bit_overflow;
+    }
+    return mrb_fixnum_value(val * (1u << width));
+  }
+
+bit_overflow:
+  {
     mrb_float f = (mrb_float)val;
     while (width--) {
       f *= 2;
     }
     return mrb_float_value(mrb, f);
   }
-  return mrb_fixnum_value(val << width);
 }
 
 static mrb_value
 rshift(mrb_int val, mrb_int width)
 {
-  mrb_assert(width > 0);
+  if (width < 0) {              /* mrb_int overflow */
+    return mrb_fixnum_value(0);
+  }
   if (width >= NUMERIC_SHIFT_WIDTH_MAX) {
     if (val < 0) {
       return mrb_fixnum_value(-1);
@@ -844,15 +1004,6 @@ rshift(mrb_int val, mrb_int width)
   return mrb_fixnum_value(val >> width);
 }
 
-static inline void
-fix_shift_get_width(mrb_state *mrb, mrb_int *width)
-{
-  mrb_value y;
-
-  mrb_get_args(mrb, "o", &y);
-  *width = mrb_fixnum(bit_coerce(mrb, y));
-}
-
 /* 15.2.8.3.12 */
 /*
  * call-seq:
@@ -866,12 +1017,12 @@ fix_lshift(mrb_state *mrb, mrb_value x)
 {
   mrb_int width, val;
 
-  fix_shift_get_width(mrb, &width);
-
+  mrb_get_args(mrb, "i", &width);
   if (width == 0) {
     return x;
   }
   val = mrb_fixnum(x);
+  if (val == 0) return x;
   if (width < 0) {
     return rshift(val, -width);
   }
@@ -891,12 +1042,12 @@ fix_rshift(mrb_state *mrb, mrb_value x)
 {
   mrb_int width, val;
 
-  fix_shift_get_width(mrb, &width);
-
+  mrb_get_args(mrb, "i", &width);
   if (width == 0) {
     return x;
   }
   val = mrb_fixnum(x);
+  if (val == 0) return x;
   if (width < 0) {
     return lshift(mrb, val, -width);
   }
@@ -935,7 +1086,7 @@ fix_to_f(mrb_state *mrb, mrb_value num)
 MRB_API mrb_value
 mrb_flo_to_fixnum(mrb_state *mrb, mrb_value x)
 {
-  mrb_int z;
+  mrb_int z = 0;
 
   if (!mrb_float_p(x)) {
     mrb_raise(mrb, E_TYPE_ERROR, "non float value");
@@ -950,7 +1101,12 @@ mrb_flo_to_fixnum(mrb_state *mrb, mrb_value x)
     if (isnan(d)) {
       mrb_raise(mrb, E_FLOATDOMAIN_ERROR, "NaN");
     }
-    z = (mrb_int)d;
+    if (FIXABLE_FLOAT(d)) {
+      z = (mrb_int)d;
+    }
+    else {
+      mrb_raisef(mrb, E_ARGUMENT_ERROR, "number (%S) too big for integer", x);
+    }
   }
   return mrb_fixnum_value(z);
 }
@@ -1149,60 +1305,72 @@ mrb_init_numeric(mrb_state *mrb)
   /* Numeric Class */
   numeric = mrb_define_class(mrb, "Numeric",  mrb->object_class);                /* 15.2.7 */
 
-  mrb_define_method(mrb, numeric, "**",       num_pow,        MRB_ARGS_REQ(1));
-  mrb_define_method(mrb, numeric, "/",        num_div,        MRB_ARGS_REQ(1));  /* 15.2.8.3.4  */
-  mrb_define_method(mrb, numeric, "quo",      num_div,        MRB_ARGS_REQ(1));  /* 15.2.7.4.5 (x) */
-  mrb_define_method(mrb, numeric, "<=>",      num_cmp,        MRB_ARGS_REQ(1));  /* 15.2.9.3.6  */
+  mrb_define_method(mrb, numeric, "**",       num_pow,         MRB_ARGS_REQ(1));
+  mrb_define_method(mrb, numeric, "/",        num_div,         MRB_ARGS_REQ(1)); /* 15.2.8.3.4  */
+  mrb_define_method(mrb, numeric, "quo",      num_div,         MRB_ARGS_REQ(1)); /* 15.2.7.4.5 (x) */
+  mrb_define_method(mrb, numeric, "<=>",      num_cmp,         MRB_ARGS_REQ(1)); /* 15.2.9.3.6  */
 
   /* Integer Class */
   integer = mrb_define_class(mrb, "Integer",  numeric);                          /* 15.2.8 */
+  MRB_SET_INSTANCE_TT(integer, MRB_TT_FIXNUM);
   mrb_undef_class_method(mrb, integer, "new");
-  mrb_define_method(mrb, integer, "to_i", int_to_i, MRB_ARGS_NONE());            /* 15.2.8.3.24 */
-  mrb_define_method(mrb, integer, "to_int", int_to_i, MRB_ARGS_NONE());
+  mrb_define_method(mrb, integer, "to_i",     int_to_i,        MRB_ARGS_NONE()); /* 15.2.8.3.24 */
+  mrb_define_method(mrb, integer, "to_int",   int_to_i,        MRB_ARGS_NONE());
+  mrb_define_method(mrb, integer, "ceil",     int_to_i,        MRB_ARGS_REQ(1)); /* 15.2.8.3.8 (x) */
+  mrb_define_method(mrb, integer, "floor",    int_to_i,        MRB_ARGS_REQ(1)); /* 15.2.8.3.10 (x) */
+  mrb_define_method(mrb, integer, "round",    int_to_i,        MRB_ARGS_REQ(1)); /* 15.2.8.3.12 (x) */
+  mrb_define_method(mrb, integer, "truncate", int_to_i,        MRB_ARGS_REQ(1)); /* 15.2.8.3.15 (x) */
 
   /* Fixnum Class */
   mrb->fixnum_class = fixnum = mrb_define_class(mrb, "Fixnum", integer);
-  mrb_define_method(mrb, fixnum,  "+",        fix_plus,          MRB_ARGS_REQ(1)); /* 15.2.8.3.1  */
-  mrb_define_method(mrb, fixnum,  "-",        fix_minus,         MRB_ARGS_REQ(1)); /* 15.2.8.3.2  */
-  mrb_define_method(mrb, fixnum,  "*",        fix_mul,           MRB_ARGS_REQ(1)); /* 15.2.8.3.3  */
-  mrb_define_method(mrb, fixnum,  "%",        fix_mod,           MRB_ARGS_REQ(1)); /* 15.2.8.3.5  */
-  mrb_define_method(mrb, fixnum,  "==",       fix_equal,         MRB_ARGS_REQ(1)); /* 15.2.8.3.7  */
-  mrb_define_method(mrb, fixnum,  "~",        fix_rev,           MRB_ARGS_NONE()); /* 15.2.8.3.8  */
-  mrb_define_method(mrb, fixnum,  "&",        fix_and,           MRB_ARGS_REQ(1)); /* 15.2.8.3.9  */
-  mrb_define_method(mrb, fixnum,  "|",        fix_or,            MRB_ARGS_REQ(1)); /* 15.2.8.3.10 */
-  mrb_define_method(mrb, fixnum,  "^",        fix_xor,           MRB_ARGS_REQ(1)); /* 15.2.8.3.11 */
-  mrb_define_method(mrb, fixnum,  "<<",       fix_lshift,        MRB_ARGS_REQ(1)); /* 15.2.8.3.12 */
-  mrb_define_method(mrb, fixnum,  ">>",       fix_rshift,        MRB_ARGS_REQ(1)); /* 15.2.8.3.13 */
-  mrb_define_method(mrb, fixnum,  "eql?",     fix_eql,           MRB_ARGS_REQ(1)); /* 15.2.8.3.16 */
-  mrb_define_method(mrb, fixnum,  "hash",     flo_hash,          MRB_ARGS_NONE()); /* 15.2.8.3.18 */
-  mrb_define_method(mrb, fixnum,  "to_f",     fix_to_f,          MRB_ARGS_NONE()); /* 15.2.8.3.23 */
-  mrb_define_method(mrb, fixnum,  "to_s",     fix_to_s,          MRB_ARGS_NONE()); /* 15.2.8.3.25 */
-  mrb_define_method(mrb, fixnum,  "inspect",  fix_to_s,          MRB_ARGS_NONE());
-  mrb_define_method(mrb, fixnum,  "divmod",   fix_divmod,        MRB_ARGS_REQ(1)); /* 15.2.8.3.30 (x) */
+  mrb_define_method(mrb, fixnum,  "+",        fix_plus,        MRB_ARGS_REQ(1)); /* 15.2.8.3.1  */
+  mrb_define_method(mrb, fixnum,  "-",        fix_minus,       MRB_ARGS_REQ(1)); /* 15.2.8.3.2  */
+  mrb_define_method(mrb, fixnum,  "*",        fix_mul,         MRB_ARGS_REQ(1)); /* 15.2.8.3.3  */
+  mrb_define_method(mrb, fixnum,  "%",        fix_mod,         MRB_ARGS_REQ(1)); /* 15.2.8.3.5  */
+  mrb_define_method(mrb, fixnum,  "==",       fix_equal,       MRB_ARGS_REQ(1)); /* 15.2.8.3.7  */
+  mrb_define_method(mrb, fixnum,  "~",        fix_rev,         MRB_ARGS_NONE()); /* 15.2.8.3.8  */
+  mrb_define_method(mrb, fixnum,  "&",        fix_and,         MRB_ARGS_REQ(1)); /* 15.2.8.3.9  */
+  mrb_define_method(mrb, fixnum,  "|",        fix_or,          MRB_ARGS_REQ(1)); /* 15.2.8.3.10 */
+  mrb_define_method(mrb, fixnum,  "^",        fix_xor,         MRB_ARGS_REQ(1)); /* 15.2.8.3.11 */
+  mrb_define_method(mrb, fixnum,  "<<",       fix_lshift,      MRB_ARGS_REQ(1)); /* 15.2.8.3.12 */
+  mrb_define_method(mrb, fixnum,  ">>",       fix_rshift,      MRB_ARGS_REQ(1)); /* 15.2.8.3.13 */
+  mrb_define_method(mrb, fixnum,  "eql?",     fix_eql,         MRB_ARGS_REQ(1)); /* 15.2.8.3.16 */
+  mrb_define_method(mrb, fixnum,  "hash",     flo_hash,        MRB_ARGS_NONE()); /* 15.2.8.3.18 */
+  mrb_define_method(mrb, fixnum,  "to_f",     fix_to_f,        MRB_ARGS_NONE()); /* 15.2.8.3.23 */
+  mrb_define_method(mrb, fixnum,  "to_s",     fix_to_s,        MRB_ARGS_NONE()); /* 15.2.8.3.25 */
+  mrb_define_method(mrb, fixnum,  "inspect",  fix_to_s,        MRB_ARGS_NONE());
+  mrb_define_method(mrb, fixnum,  "divmod",   fix_divmod,      MRB_ARGS_REQ(1)); /* 15.2.8.3.30 (x) */
 
   /* Float Class */
   mrb->float_class = fl = mrb_define_class(mrb, "Float", numeric);                 /* 15.2.9 */
+  MRB_SET_INSTANCE_TT(fl, MRB_TT_FLOAT);
   mrb_undef_class_method(mrb,  fl, "new");
-  mrb_define_method(mrb, fl,      "+",         flo_plus,         MRB_ARGS_REQ(1)); /* 15.2.9.3.1  */
-  mrb_define_method(mrb, fl,      "-",         flo_minus,        MRB_ARGS_REQ(1)); /* 15.2.9.3.2  */
-  mrb_define_method(mrb, fl,      "*",         flo_mul,          MRB_ARGS_REQ(1)); /* 15.2.9.3.3  */
-  mrb_define_method(mrb, fl,      "%",         flo_mod,          MRB_ARGS_REQ(1)); /* 15.2.9.3.5  */
-  mrb_define_method(mrb, fl,      "==",        flo_eq,           MRB_ARGS_REQ(1)); /* 15.2.9.3.7  */
-  mrb_define_method(mrb, fl,      "ceil",      flo_ceil,         MRB_ARGS_NONE()); /* 15.2.9.3.8  */
-  mrb_define_method(mrb, fl,      "finite?",   flo_finite_p,     MRB_ARGS_NONE()); /* 15.2.9.3.9  */
-  mrb_define_method(mrb, fl,      "floor",     flo_floor,        MRB_ARGS_NONE()); /* 15.2.9.3.10 */
-  mrb_define_method(mrb, fl,      "infinite?", flo_infinite_p,   MRB_ARGS_NONE()); /* 15.2.9.3.11 */
-  mrb_define_method(mrb, fl,      "round",     flo_round,        MRB_ARGS_OPT(1)); /* 15.2.9.3.12 */
-  mrb_define_method(mrb, fl,      "to_f",      flo_to_f,         MRB_ARGS_NONE()); /* 15.2.9.3.13 */
-  mrb_define_method(mrb, fl,      "to_i",      flo_truncate,     MRB_ARGS_NONE()); /* 15.2.9.3.14 */
-  mrb_define_method(mrb, fl,      "to_int",    flo_truncate,     MRB_ARGS_NONE());
-  mrb_define_method(mrb, fl,      "truncate",  flo_truncate,     MRB_ARGS_NONE()); /* 15.2.9.3.15 */
-  mrb_define_method(mrb, fl,      "divmod",    flo_divmod,       MRB_ARGS_REQ(1));
-  mrb_define_method(mrb, fl,      "eql?",      flo_eql,          MRB_ARGS_REQ(1)); /* 15.2.8.3.16 */
-
-  mrb_define_method(mrb, fl,      "to_s",      flo_to_s,         MRB_ARGS_NONE()); /* 15.2.9.3.16(x) */
-  mrb_define_method(mrb, fl,      "inspect",   flo_to_s,         MRB_ARGS_NONE());
-  mrb_define_method(mrb, fl,      "nan?",      flo_nan_p,        MRB_ARGS_NONE());
+  mrb_define_method(mrb, fl,      "+",         flo_plus,       MRB_ARGS_REQ(1)); /* 15.2.9.3.1  */
+  mrb_define_method(mrb, fl,      "-",         flo_minus,      MRB_ARGS_REQ(1)); /* 15.2.9.3.2  */
+  mrb_define_method(mrb, fl,      "*",         flo_mul,        MRB_ARGS_REQ(1)); /* 15.2.9.3.3  */
+  mrb_define_method(mrb, fl,      "%",         flo_mod,        MRB_ARGS_REQ(1)); /* 15.2.9.3.5  */
+  mrb_define_method(mrb, fl,      "==",        flo_eq,         MRB_ARGS_REQ(1)); /* 15.2.9.3.7  */
+  mrb_define_method(mrb, fl,      "~",         flo_rev,        MRB_ARGS_NONE());
+  mrb_define_method(mrb, fl,      "&",         flo_and,        MRB_ARGS_REQ(1));
+  mrb_define_method(mrb, fl,      "|",         flo_or,         MRB_ARGS_REQ(1));
+  mrb_define_method(mrb, fl,      "^",         flo_xor,        MRB_ARGS_REQ(1));
+  mrb_define_method(mrb, fl,      ">>",        flo_lshift,     MRB_ARGS_REQ(1));
+  mrb_define_method(mrb, fl,      "<<",        flo_rshift,     MRB_ARGS_REQ(1));
+  mrb_define_method(mrb, fl,      "ceil",      flo_ceil,       MRB_ARGS_NONE()); /* 15.2.9.3.8  */
+  mrb_define_method(mrb, fl,      "finite?",   flo_finite_p,   MRB_ARGS_NONE()); /* 15.2.9.3.9  */
+  mrb_define_method(mrb, fl,      "floor",     flo_floor,      MRB_ARGS_NONE()); /* 15.2.9.3.10 */
+  mrb_define_method(mrb, fl,      "infinite?", flo_infinite_p, MRB_ARGS_NONE()); /* 15.2.9.3.11 */
+  mrb_define_method(mrb, fl,      "round",     flo_round,      MRB_ARGS_OPT(1)); /* 15.2.9.3.12 */
+  mrb_define_method(mrb, fl,      "to_f",      flo_to_f,       MRB_ARGS_NONE()); /* 15.2.9.3.13 */
+  mrb_define_method(mrb, fl,      "to_i",      flo_truncate,   MRB_ARGS_NONE()); /* 15.2.9.3.14 */
+  mrb_define_method(mrb, fl,      "to_int",    flo_truncate,   MRB_ARGS_NONE());
+  mrb_define_method(mrb, fl,      "truncate",  flo_truncate,   MRB_ARGS_NONE()); /* 15.2.9.3.15 */
+  mrb_define_method(mrb, fl,      "divmod",    flo_divmod,     MRB_ARGS_REQ(1));
+  mrb_define_method(mrb, fl,      "eql?",      flo_eql,        MRB_ARGS_REQ(1)); /* 15.2.8.3.16 */
+
+  mrb_define_method(mrb, fl,      "to_s",      flo_to_s,       MRB_ARGS_NONE()); /* 15.2.9.3.16(x) */
+  mrb_define_method(mrb, fl,      "inspect",   flo_to_s,       MRB_ARGS_NONE());
+  mrb_define_method(mrb, fl,      "nan?",      flo_nan_p,      MRB_ARGS_NONE());
 
 #ifdef INFINITY
   mrb_define_const(mrb, fl, "INFINITY", mrb_float_value(mrb, INFINITY));
index da60ebb..a78e9a4 100644 (file)
@@ -4,10 +4,11 @@
 ** See Copyright Notice in mruby.h
 */
 
-#include "mruby.h"
-#include "mruby/class.h"
-#include "mruby/numeric.h"
-#include "mruby/string.h"
+#include <mruby.h>
+#include <mruby/class.h>
+#include <mruby/numeric.h>
+#include <mruby/string.h>
+#include <mruby/class.h>
 
 MRB_API mrb_bool
 mrb_obj_eq(mrb_state *mrb, mrb_value v1, mrb_value v2)
@@ -265,6 +266,7 @@ mrb_init_object(mrb_state *mrb)
   struct RClass *f;
 
   mrb->nil_class   = n = mrb_define_class(mrb, "NilClass",   mrb->object_class);
+  MRB_SET_INSTANCE_TT(n, MRB_TT_TRUE);
   mrb_undef_class_method(mrb, n, "new");
   mrb_define_method(mrb, n, "&",    false_and,      MRB_ARGS_REQ(1));  /* 15.2.4.3.1  */
   mrb_define_method(mrb, n, "^",    false_xor,      MRB_ARGS_REQ(1));  /* 15.2.4.3.2  */
@@ -274,6 +276,7 @@ mrb_init_object(mrb_state *mrb)
   mrb_define_method(mrb, n, "inspect", nil_inspect, MRB_ARGS_NONE());
 
   mrb->true_class  = t = mrb_define_class(mrb, "TrueClass",  mrb->object_class);
+  MRB_SET_INSTANCE_TT(t, MRB_TT_TRUE);
   mrb_undef_class_method(mrb, t, "new");
   mrb_define_method(mrb, t, "&",    true_and,       MRB_ARGS_REQ(1));  /* 15.2.5.3.1  */
   mrb_define_method(mrb, t, "^",    true_xor,       MRB_ARGS_REQ(1));  /* 15.2.5.3.2  */
@@ -282,6 +285,7 @@ mrb_init_object(mrb_state *mrb)
   mrb_define_method(mrb, t, "inspect", true_to_s,   MRB_ARGS_NONE());
 
   mrb->false_class = f = mrb_define_class(mrb, "FalseClass", mrb->object_class);
+  MRB_SET_INSTANCE_TT(f, MRB_TT_TRUE);
   mrb_undef_class_method(mrb, f, "new");
   mrb_define_method(mrb, f, "&",    false_and,      MRB_ARGS_REQ(1));  /* 15.2.6.3.1  */
   mrb_define_method(mrb, f, "^",    false_xor,      MRB_ARGS_REQ(1));  /* 15.2.6.3.2  */
@@ -348,7 +352,7 @@ mrb_check_convert_type(mrb_state *mrb, mrb_value val, enum mrb_vtype type, const
 {
   mrb_value v;
 
-  if (mrb_type(val) == type && type != MRB_TT_DATA) return val;
+  if (mrb_type(val) == type && type != MRB_TT_DATA && type != MRB_TT_ISTRUCT) return val;
   v = convert_type(mrb, val, tname, method, FALSE);
   if (mrb_nil_p(v) || mrb_type(v) != type) return mrb_nil_value();
   return v;
@@ -380,7 +384,7 @@ static const struct types {
 /*    {MRB_TT_VARMAP,  "Varmap"}, */ /* internal use: dynamic variables */
 /*    {MRB_TT_NODE,  "Node"}, */ /* internal use: syntax tree node */
 /*    {MRB_TT_UNDEF,  "undef"}, */ /* internal use: #undef; should not happen */
-    {-1,  0}
+  {MRB_TT_MAXDEFINE,  0}
 };
 
 MRB_API void
@@ -390,7 +394,7 @@ mrb_check_type(mrb_state *mrb, mrb_value x, enum mrb_vtype t)
   enum mrb_vtype xt;
 
   xt = mrb_type(x);
-  if ((xt != t) || (xt == MRB_TT_DATA)) {
+  if ((xt != t) || (xt == MRB_TT_DATA) || (xt == MRB_TT_ISTRUCT)) {
     while (type->type < MRB_TT_MAXDEFINE) {
       if (type->type == t) {
         const char *etype;
@@ -440,7 +444,7 @@ mrb_any_to_s(mrb_state *mrb, mrb_value obj)
   mrb_str_cat_lit(mrb, str, "#<");
   mrb_str_cat_cstr(mrb, str, cname);
   mrb_str_cat_lit(mrb, str, ":");
-  mrb_str_concat(mrb, str, mrb_ptr_to_str(mrb, mrb_cptr(obj)));
+  mrb_str_concat(mrb, str, mrb_ptr_to_str(mrb, mrb_ptr(obj)));
   mrb_str_cat_lit(mrb, str, ">");
 
   return str;
@@ -481,6 +485,7 @@ mrb_obj_is_kind_of(mrb_state *mrb, mrb_value obj, struct RClass *c)
     case MRB_TT_MODULE:
     case MRB_TT_CLASS:
     case MRB_TT_ICLASS:
+    case MRB_TT_SCLASS:
       break;
 
     default:
@@ -529,8 +534,11 @@ mrb_convert_to_integer(mrb_state *mrb, mrb_value val, int base)
   switch (mrb_type(val)) {
     case MRB_TT_FLOAT:
       if (base != 0) goto arg_error;
-      if (FIXABLE(mrb_float(val))) {
-        break;
+      else {
+        mrb_float f = mrb_float(val);
+        if (FIXABLE_FLOAT(f)) {
+          break;
+        }
       }
       return mrb_flo_to_fixnum(mrb, val);
 
@@ -548,6 +556,7 @@ mrb_convert_to_integer(mrb_state *mrb, mrb_value val, int base)
   if (base != 0) {
     tmp = mrb_check_string_type(mrb, val);
     if (!mrb_nil_p(tmp)) {
+      val = tmp;
       goto string_conv;
     }
 arg_error:
index 2446f92..fe4d17a 100644 (file)
@@ -1,2 +1,2 @@
 /* this header file is to be removed soon. */
-#include "mruby/opcode.h"
+#include <mruby/opcode.h>
index 285cca6..db4546a 100644 (file)
@@ -7,14 +7,18 @@
 #include <stddef.h>
 #include <stdint.h>
 #include <string.h>
-#include "mruby.h"
+#include <mruby.h>
 
 /* configuration section */
 /* allocated memory address should be multiple of POOL_ALIGNMENT */
 /* or undef it if alignment does not matter */
 #ifndef POOL_ALIGNMENT
+#if INTPTR_MAX == INT64_MAX
+#define POOL_ALIGNMENT 8
+#else
 #define POOL_ALIGNMENT 4
 #endif
+#endif
 /* page size of memory pool */
 #ifndef POOL_PAGE_SIZE
 #define POOL_PAGE_SIZE 16000
index 111145a..03b5ead 100644 (file)
@@ -4,9 +4,9 @@
 ** See Copyright Notice in mruby.h
 */
 
-#include "mruby.h"
-#include "mruby/string.h"
-#include "mruby/variable.h"
+#include <mruby.h>
+#include <mruby/string.h>
+#include <mruby/variable.h>
 
 #ifndef MRB_DISABLE_STDIO
 static void
index 8a2b6bb..ee4a493 100644 (file)
@@ -4,10 +4,10 @@
 ** See Copyright Notice in mruby.h
 */
 
-#include "mruby.h"
-#include "mruby/class.h"
-#include "mruby/proc.h"
-#include "mruby/opcode.h"
+#include <mruby.h>
+#include <mruby/class.h>
+#include <mruby/proc.h>
+#include <mruby/opcode.h>
 
 static mrb_code call_iseq[] = {
   MKOP_A(OP_CALL, 0),
@@ -41,7 +41,7 @@ env_new(mrb_state *mrb, int nlocals)
 
   e = (struct REnv*)mrb_obj_alloc(mrb, MRB_TT_ENV, (struct RClass*)mrb->c->ci->proc->env);
   MRB_SET_ENV_STACK_LEN(e, nlocals);
-  e->mid = mrb->c->ci->mid;
+  e->cxt.c = mrb->c;
   e->cioff = mrb->c->ci - mrb->c->cibase;
   e->stack = mrb->c->stack;
 
@@ -61,6 +61,7 @@ closure_setup(mrb_state *mrb, struct RProc *p, int nlocals)
     e = mrb->c->ci->env;
   }
   p->env = e;
+  mrb_field_write_barrier(mrb, (struct RBasic *)p, (struct RBasic *)p->env);
 }
 
 struct RProc *
@@ -135,12 +136,12 @@ mrb_proc_cfunc_env_get(mrb_state *mrb, mrb_int idx)
   return e->stack[idx];
 }
 
-MRB_API void
+void
 mrb_proc_copy(struct RProc *a, struct RProc *b)
 {
   a->flags = b->flags;
   a->body = b->body;
-  if (!MRB_PROC_CFUNC_P(a)) {
+  if (!MRB_PROC_CFUNC_P(a) && a->body.irep) {
     a->body.irep->refcnt++;
   }
   a->target_class = b->target_class;
@@ -148,19 +149,22 @@ mrb_proc_copy(struct RProc *a, struct RProc *b)
 }
 
 static mrb_value
-mrb_proc_initialize(mrb_state *mrb, mrb_value self)
+mrb_proc_s_new(mrb_state *mrb, mrb_value proc_class)
 {
   mrb_value blk;
+  mrb_value proc;
+  struct RProc *p;
 
   mrb_get_args(mrb, "&", &blk);
   if (mrb_nil_p(blk)) {
     /* Calling Proc.new without a block is not implemented yet */
     mrb_raise(mrb, E_ARGUMENT_ERROR, "tried to create Proc object without a block");
   }
-  else {
-    mrb_proc_copy(mrb_proc_ptr(self), mrb_proc_ptr(blk));
-  }
-  return self;
+  p = (struct RProc *)mrb_obj_alloc(mrb, MRB_TT_PROC, mrb_class_ptr(proc_class));
+  mrb_proc_copy(p, mrb_proc_ptr(blk));
+  proc = mrb_obj_value(p);
+  mrb_funcall_with_block(mrb, proc, mrb_intern_lit(mrb, "initialize"), 0, NULL, blk);
+  return proc;
 }
 
 static mrb_value
@@ -188,18 +192,13 @@ mrb_proc_call_cfunc(mrb_state *mrb, struct RProc *p, mrb_value self)
   return (p->body.func)(mrb, self);
 }
 
-mrb_code*
-mrb_proc_iseq(mrb_state *mrb, struct RProc *p)
-{
-  return p->body.irep->iseq;
-}
-
 /* 15.2.17.4.2 */
 static mrb_value
 mrb_proc_arity(mrb_state *mrb, mrb_value self)
 {
   struct RProc *p = mrb_proc_ptr(self);
-  mrb_code *iseq = mrb_proc_iseq(mrb, p);
+  struct mrb_irep *irep;
+  mrb_code *iseq;
   mrb_aspec aspec;
   int ma, op, ra, pa, arity;
 
@@ -208,6 +207,12 @@ mrb_proc_arity(mrb_state *mrb, mrb_value self)
     return mrb_fixnum_value(-1);
   }
 
+  irep = p->body.irep;
+  if (!irep) {
+    return mrb_fixnum_value(0);
+  }
+
+  iseq = irep->iseq;
   /* arity is depend on OP_ENTER */
   if (GET_OPCODE(*iseq) != OP_ENTER) {
     return mrb_fixnum_value(0);
@@ -266,8 +271,9 @@ mrb_init_proc(mrb_state *mrb)
   call_irep->flags = MRB_ISEQ_NO_FREE;
   call_irep->iseq = call_iseq;
   call_irep->ilen = 1;
+  call_irep->nregs = 2;         /* receiver and block */
 
-  mrb_define_method(mrb, mrb->proc_class, "initialize", mrb_proc_initialize, MRB_ARGS_NONE());
+  mrb_define_class_method(mrb, mrb->proc_class, "new", mrb_proc_s_new, MRB_ARGS_ANY());
   mrb_define_method(mrb, mrb->proc_class, "initialize_copy", mrb_proc_init_copy, MRB_ARGS_REQ(1));
   mrb_define_method(mrb, mrb->proc_class, "arity", mrb_proc_arity, MRB_ARGS_NONE());
 
index b58b6a1..2cb6f23 100644 (file)
@@ -4,14 +4,25 @@
 ** See Copyright Notice in mruby.h
 */
 
-#include "mruby.h"
-#include "mruby/class.h"
-#include "mruby/range.h"
-#include "mruby/string.h"
-#include "mruby/array.h"
+#include <mruby.h>
+#include <mruby/class.h>
+#include <mruby/range.h>
+#include <mruby/string.h>
+#include <mruby/array.h>
 
 #define RANGE_CLASS (mrb_class_get(mrb, "Range"))
 
+MRB_API struct RRange*
+mrb_range_ptr(mrb_state *mrb, mrb_value v)
+{
+  struct RRange *r = (struct RRange*)mrb_ptr(v);
+
+  if (r->edges == NULL) {
+    mrb_raise(mrb, E_ARGUMENT_ERROR, "uninitialized range");
+  }
+  return r;
+}
+
 static void
 range_check(mrb_state *mrb, mrb_value a, mrb_value b)
 {
@@ -57,7 +68,7 @@ mrb_range_new(mrb_state *mrb, mrb_value beg, mrb_value end, mrb_bool excl)
 mrb_value
 mrb_range_beg(mrb_state *mrb, mrb_value range)
 {
-  struct RRange *r = mrb_range_ptr(range);
+  struct RRange *r = mrb_range_ptr(mrb, range);
 
   return r->edges->beg;
 }
@@ -76,7 +87,7 @@ mrb_range_beg(mrb_state *mrb, mrb_value range)
 mrb_value
 mrb_range_end(mrb_state *mrb, mrb_value range)
 {
-  struct RRange *r = mrb_range_ptr(range);
+  struct RRange *r = mrb_range_ptr(mrb, range);
 
   return r->edges->end;
 }
@@ -90,7 +101,7 @@ mrb_range_end(mrb_state *mrb, mrb_value range)
 mrb_value
 mrb_range_excl(mrb_state *mrb, mrb_value range)
 {
-  struct RRange *r = mrb_range_ptr(range);
+  struct RRange *r = mrb_range_ptr(mrb, range);
 
   return mrb_bool_value(r->excl);
 }
@@ -98,7 +109,7 @@ mrb_range_excl(mrb_state *mrb, mrb_value range)
 static void
 range_init(mrb_state *mrb, mrb_value range, mrb_value beg, mrb_value end, mrb_bool exclude_end)
 {
-  struct RRange *r = mrb_range_ptr(range);
+  struct RRange *r = mrb_range_raw_ptr(range);
 
   range_check(mrb, beg, end);
   r->excl = exclude_end;
@@ -129,6 +140,9 @@ mrb_range_initialize(mrb_state *mrb, mrb_value range)
     exclusive = FALSE;
   }
   /* Ranges are immutable, so that they should be initialized only once. */
+  if (mrb_range_raw_ptr(range)->edges) {
+    mrb_name_error(mrb, mrb_intern_lit(mrb, "initialize"), "`initialize' called twice");
+  }
   range_init(mrb, range, beg, end, exclusive);
   return range;
 }
@@ -152,7 +166,7 @@ mrb_range_eq(mrb_state *mrb, mrb_value range)
 {
   struct RRange *rr;
   struct RRange *ro;
-  mrb_value obj;
+  mrb_value obj, v1, v2;
 
   mrb_get_args(mrb, "o", &obj);
 
@@ -161,11 +175,11 @@ mrb_range_eq(mrb_state *mrb, mrb_value range)
     return mrb_false_value();
   }
 
-  rr = mrb_range_ptr(range);
-  ro = mrb_range_ptr(obj);
-  if (!mrb_bool(mrb_funcall(mrb, rr->edges->beg, "==", 1, ro->edges->beg)) ||
-      !mrb_bool(mrb_funcall(mrb, rr->edges->end, "==", 1, ro->edges->end)) ||
-      rr->excl != ro->excl) {
+  rr = mrb_range_ptr(mrb, range);
+  ro = mrb_range_ptr(mrb, obj);
+  v1 = mrb_funcall(mrb, rr->edges->beg, "==", 1, ro->edges->beg);
+  v2 = mrb_funcall(mrb, rr->edges->end, "==", 1, ro->edges->end);
+  if (!mrb_bool(v1) || !mrb_bool(v2) || rr->excl != ro->excl) {
     return mrb_false_value();
   }
   return mrb_true_value();
@@ -219,7 +233,7 @@ mrb_value
 mrb_range_include(mrb_state *mrb, mrb_value range)
 {
   mrb_value val;
-  struct RRange *r = mrb_range_ptr(range);
+  struct RRange *r = mrb_range_ptr(mrb, range);
   mrb_value beg, end;
   mrb_bool include_p;
 
@@ -227,31 +241,32 @@ mrb_range_include(mrb_state *mrb, mrb_value range)
 
   beg = r->edges->beg;
   end = r->edges->end;
-  include_p = r_le(mrb, beg, val) && /* beg <= val */
-              ((r->excl && r_gt(mrb, end, val)) || /* end >  val */
-              (r_ge(mrb, end, val))); /* end >= val */
+  include_p = r_le(mrb, beg, val) &&           /* beg <= val */
+              (r->excl ? r_gt(mrb, end, val)   /* end >  val */
+                       : r_ge(mrb, end, val)); /* end >= val */
 
   return mrb_bool_value(include_p);
 }
 
-static mrb_bool
-range_beg_len(mrb_state *mrb, mrb_value range, mrb_int *begp, mrb_int *lenp, mrb_int len, mrb_bool trunc)
+MRB_API mrb_int
+mrb_range_beg_len(mrb_state *mrb, mrb_value range, mrb_int *begp, mrb_int *lenp, mrb_int len, mrb_bool trunc)
 {
   mrb_int beg, end;
-  struct RRange *r = mrb_range_ptr(range);
+  struct RRange *r;
 
-  if (mrb_type(range) != MRB_TT_RANGE) return FALSE;
+  if (mrb_type(range) != MRB_TT_RANGE) return 0;
+  r = mrb_range_ptr(mrb, range);
 
   beg = mrb_int(mrb, r->edges->beg);
   end = mrb_int(mrb, r->edges->end);
 
   if (beg < 0) {
     beg += len;
-    if (beg < 0) return FALSE;
+    if (beg < 0) return 2;
   }
 
   if (trunc) {
-    if (beg > len) return FALSE;
+    if (beg > len) return 2;
     if (end > len) end = len;
   }
 
@@ -263,13 +278,7 @@ range_beg_len(mrb_state *mrb, mrb_value range, mrb_int *begp, mrb_int *lenp, mrb
 
   *begp = beg;
   *lenp = len;
-  return TRUE;
-}
-
-MRB_API mrb_bool
-mrb_range_beg_len(mrb_state *mrb, mrb_value range, mrb_int *begp, mrb_int *lenp, mrb_int len)
-{
-  return range_beg_len(mrb, range, begp, lenp, len, TRUE);
+  return 1;
 }
 
 /* 15.2.14.4.12(x) */
@@ -284,7 +293,7 @@ static mrb_value
 range_to_s(mrb_state *mrb, mrb_value range)
 {
   mrb_value str, str2;
-  struct RRange *r = mrb_range_ptr(range);
+  struct RRange *r = mrb_range_ptr(mrb, range);
 
   str  = mrb_obj_as_string(mrb, r->edges->beg);
   str2 = mrb_obj_as_string(mrb, r->edges->end);
@@ -309,7 +318,7 @@ static mrb_value
 range_inspect(mrb_state *mrb, mrb_value range)
 {
   mrb_value str, str2;
-  struct RRange *r = mrb_range_ptr(range);
+  struct RRange *r = mrb_range_ptr(mrb, range);
 
   str  = mrb_inspect(mrb, r->edges->beg);
   str2 = mrb_inspect(mrb, r->edges->end);
@@ -349,8 +358,8 @@ range_eql(mrb_state *mrb, mrb_value range)
   }
   if (mrb_type(obj) != MRB_TT_RANGE) return mrb_false_value();
 
-  r = mrb_range_ptr(range);
-  o = mrb_range_ptr(obj);
+  r = mrb_range_ptr(mrb, range);
+  o = mrb_range_ptr(mrb, obj);
   if (!mrb_eql(mrb, r->edges->beg, o->edges->beg) ||
       !mrb_eql(mrb, r->edges->end, o->edges->end) ||
       (r->excl != o->excl)) {
@@ -373,7 +382,7 @@ range_initialize_copy(mrb_state *mrb, mrb_value copy)
     mrb_raise(mrb, E_TYPE_ERROR, "wrong argument class");
   }
 
-  r = mrb_range_ptr(src);
+  r = mrb_range_ptr(mrb, src);
   range_init(mrb, copy, r->edges->beg, r->edges->end, r->excl);
 
   return copy;
@@ -390,7 +399,7 @@ mrb_get_values_at(mrb_state *mrb, mrb_value obj, mrb_int olen, mrb_int argc, con
     if (mrb_fixnum_p(argv[i])) {
       mrb_ary_push(mrb, result, func(mrb, obj, mrb_fixnum(argv[i])));
     }
-    else if (range_beg_len(mrb, argv[i], &beg, &len, olen, FALSE)) {
+    else if (mrb_range_beg_len(mrb, argv[i], &beg, &len, olen, FALSE) == 1) {
       mrb_int const end = olen < beg + len ? olen : beg + len;
       for (j = beg; j < end; ++j) {
         mrb_ary_push(mrb, result, func(mrb, obj, j));
index 526856a..0be5a18 100644 (file)
@@ -6,11 +6,11 @@
 
 #include <stdlib.h>
 #include <string.h>
-#include "mruby.h"
-#include "mruby/irep.h"
-#include "mruby/variable.h"
-#include "mruby/debug.h"
-#include "mruby/string.h"
+#include <mruby.h>
+#include <mruby/irep.h>
+#include <mruby/variable.h>
+#include <mruby/debug.h>
+#include <mruby/string.h>
 
 void mrb_init_core(mrb_state*);
 void mrb_init_mrbgems(mrb_state*);
@@ -141,7 +141,7 @@ mrb_irep_free(mrb_state *mrb, mrb_irep *irep)
 
   if (!(irep->flags & MRB_ISEQ_NO_FREE))
     mrb_free(mrb, irep->iseq);
-  for (i=0; i<irep->plen; i++) {
+  if (irep->pool) for (i=0; i<irep->plen; i++) {
     if (mrb_type(irep->pool[i]) == MRB_TT_STRING) {
       mrb_gc_free_str(mrb, RSTRING(irep->pool[i]));
       mrb_free(mrb, mrb_obj_ptr(irep->pool[i]));
@@ -159,7 +159,9 @@ mrb_irep_free(mrb_state *mrb, mrb_irep *irep)
   }
   mrb_free(mrb, irep->reps);
   mrb_free(mrb, irep->lv);
-  mrb_free(mrb, (void *)irep->filename);
+  if (irep->own_filename) {
+    mrb_free(mrb, (void *)irep->filename);
+  }
   mrb_free(mrb, irep->lines);
   mrb_debug_info_free(mrb, irep->debug_info);
   mrb_free(mrb, irep);
@@ -215,6 +217,8 @@ mrb_str_pool(mrb_state *mrb, mrb_value str)
   return mrb_obj_value(ns);
 }
 
+void mrb_free_backtrace(mrb_state *mrb);
+
 MRB_API void
 mrb_free_context(mrb_state *mrb, struct mrb_context *c)
 {
@@ -258,6 +262,7 @@ mrb_add_irep(mrb_state *mrb)
   irep = (mrb_irep *)mrb_malloc(mrb, sizeof(mrb_irep));
   *irep = mrb_irep_zero;
   irep->refcnt = 1;
+  irep->own_filename = FALSE;
 
   return irep;
 }
@@ -286,7 +291,8 @@ mrb_state_atexit(mrb_state *mrb, mrb_atexit_func f)
   stack_size = sizeof(mrb_atexit_func) * (mrb->atexit_stack_len + 1);
   if (mrb->atexit_stack_len == 0) {
     mrb->atexit_stack = (mrb_atexit_func*)mrb_malloc(mrb, stack_size);
-  } else {
+  }
+  else {
     mrb->atexit_stack = (mrb_atexit_func*)mrb_realloc(mrb, mrb->atexit_stack, stack_size);
   }
 #endif
index ef84459..2cc54a8 100644 (file)
@@ -4,17 +4,21 @@
 ** See Copyright Notice in mruby.h
 */
 
+#ifdef _MSC_VER
+# define _CRT_NONSTDC_NO_DEPRECATE
+#endif
+
 #include <float.h>
 #include <limits.h>
 #include <stddef.h>
 #include <stdlib.h>
 #include <string.h>
-#include "mruby.h"
-#include "mruby/array.h"
-#include "mruby/class.h"
-#include "mruby/range.h"
-#include "mruby/string.h"
-#include "mruby/re.h"
+#include <mruby.h>
+#include <mruby/array.h>
+#include <mruby/class.h>
+#include <mruby/range.h>
+#include <mruby/string.h>
+#include <mruby/re.h>
 
 typedef struct mrb_shared_string {
   mrb_bool nofree : 1;
@@ -59,7 +63,8 @@ str_new(mrb_state *mrb, const char *p, size_t len)
     if (p) {
       memcpy(s->as.ary, p, len);
     }
-  } else {
+  }
+  else {
     if (len >= MRB_INT_MAX) {
       mrb_raise(mrb, E_ARGUMENT_ERROR, "string size too big");
     }
@@ -114,9 +119,12 @@ mrb_str_buf_new(mrb_state *mrb, size_t capa)
   return mrb_obj_value(s);
 }
 
-static inline void
-resize_capa(mrb_state *mrb, struct RString *s, mrb_int capacity)
+static void
+resize_capa(mrb_state *mrb, struct RString *s, size_t capacity)
 {
+#if SIZE_MAX > MRB_INT_MAX
+    mrb_assert(capacity < MRB_INT_MAX);
+#endif
   if (RSTR_EMBED_P(s)) {
     if (RSTRING_EMBED_LEN_MAX < capacity) {
       char *const tmp = (char *)mrb_malloc(mrb, capacity+1);
@@ -125,12 +133,12 @@ resize_capa(mrb_state *mrb, struct RString *s, mrb_int capacity)
       RSTR_UNSET_EMBED_FLAG(s);
       s->as.heap.ptr = tmp;
       s->as.heap.len = len;
-      s->as.heap.aux.capa = capacity;
+      s->as.heap.aux.capa = (mrb_int)capacity;
     }
   }
   else {
-    s->as.heap.ptr = (char *)mrb_realloc(mrb, RSTR_PTR(s), capacity+1);
-    s->as.heap.aux.capa = capacity;
+    s->as.heap.ptr = (char*)mrb_realloc(mrb, RSTR_PTR(s), capacity+1);
+    s->as.heap.aux.capa = (mrb_int)capacity;
   }
 }
 
@@ -147,22 +155,26 @@ str_buf_cat(mrb_state *mrb, struct RString *s, const char *ptr, size_t len)
       off = ptr - RSTR_PTR(s);
   }
 
-  if (RSTR_EMBED_P(s))
-    capa = RSTRING_EMBED_LEN_MAX;
-  else
-    capa = s->as.heap.aux.capa;
+  capa = RSTR_CAPA(s);
+  if (capa <= RSTRING_EMBED_LEN_MAX)
+    capa = RSTRING_EMBED_LEN_MAX+1;
 
-  if (RSTR_LEN(s) >= MRB_INT_MAX - (mrb_int)len) {
+  total = RSTR_LEN(s)+len;
+  if (total >= MRB_INT_MAX) {
+  size_error:
     mrb_raise(mrb, E_ARGUMENT_ERROR, "string size too big");
   }
-  total = RSTR_LEN(s)+len;
   if (capa <= total) {
     while (total > capa) {
-      if (capa + 1 >= MRB_INT_MAX / 2) {
-        capa = (total + 4095) / 4096;
-        break;
+      if (capa <= MRB_INT_MAX / 2) {
+        capa *= 2;
+      }
+      else {
+        capa = total;
       }
-      capa = (capa + 1) * 2;
+    }
+    if (capa < total || capa > MRB_INT_MAX) {
+      goto size_error;
     }
     resize_capa(mrb, s, capa);
   }
@@ -269,11 +281,17 @@ utf8_strlen(mrb_value str, mrb_int len)
   mrb_int total = 0;
   char* p = RSTRING_PTR(str);
   char* e = p;
+  if (RSTRING(str)->flags & MRB_STR_NO_UTF) {
+    return RSTRING_LEN(str);
+  }
   e += len < 0 ? RSTRING_LEN(str) : len;
   while (p<e) {
     p += utf8len(p, e);
     total++;
   }
+  if (RSTRING_LEN(str) == total) {
+    RSTRING(str)->flags |= MRB_STR_NO_UTF;
+  }
   return total;
 }
 
@@ -302,17 +320,20 @@ bytes2chars(char *p, mrb_int bi)
   mrb_int i, b, n;
 
   for (b=i=0; b<bi; i++) {
-    n = utf8len(p, p+bi);
+    n = utf8len_codepage[(unsigned char)*p];
     b += n;
     p += n;
   }
+  if (b != bi) return -1;
   return i;
 }
 
+#define BYTES_ALIGN_CHECK(pos) if (pos < 0) return mrb_nil_value();
 #else
 #define RSTRING_CHAR_LEN(s) RSTRING_LEN(s)
 #define chars2bytes(p, off, ci) (ci)
 #define bytes2chars(p, bi) (bi)
+#define BYTES_ALIGN_CHECK(pos)
 #endif
 
 static inline mrb_int
@@ -348,12 +369,12 @@ mrb_memsearch(const void *x0, mrb_int m, const void *y0, mrb_int n)
     return 0;
   }
   else if (m == 1) {
-    const unsigned char *ys = y, *ye = ys + n;
-    for (; y < ye; ++y) {
-      if (*x == *y)
-        return y - ys;
-    }
-    return -1;
+    const unsigned char *ys = (const unsigned char *)memchr(y, *x, n);
+
+    if (ys)
+      return ys - y;
+    else
+      return -1;
   }
   return mrb_memsearch_qs((const unsigned char *)x0, m, (const unsigned char *)y0, n);
 }
@@ -403,7 +424,7 @@ byte_subseq(mrb_state *mrb, mrb_value str, mrb_int beg, mrb_int len)
   mrb_shared_string *shared;
 
   orig = mrb_str_ptr(str);
-  if (RSTR_EMBED_P(orig)) {
+  if (RSTR_EMBED_P(orig) || RSTR_LEN(orig) == 0) {
     s = str_new(mrb, orig->as.ary+beg, len);
   }
   else {
@@ -449,7 +470,7 @@ str_substr(mrb_state *mrb, mrb_value str, mrb_int beg, mrb_int len)
     beg += clen;
     if (beg < 0) return mrb_nil_value();
   }
-  if (beg + len > clen)
+  if (len > clen - beg)
     len = clen - beg;
   if (len <= 0) {
     len = 0;
@@ -488,7 +509,7 @@ str_index(mrb_state *mrb, mrb_value str, mrb_value sub, mrb_int offset)
 static void
 check_frozen(mrb_state *mrb, struct RString *s)
 {
-  if (RSTR_FROZEN_P(s)) {
+  if (MRB_FROZEN_P(s)) {
     mrb_raise(mrb, E_RUNTIME_ERROR, "can't modify frozen string");
   }
 }
@@ -499,6 +520,9 @@ str_replace(mrb_state *mrb, struct RString *s1, struct RString *s2)
   long len;
 
   check_frozen(mrb, s1);
+  if (s1 == s2) return mrb_obj_value(s1);
+  s1->flags &= ~MRB_STR_NO_UTF;
+  s1->flags |= s2->flags&MRB_STR_NO_UTF;
   len = RSTR_LEN(s2);
   if (RSTR_SHARED_P(s1)) {
     str_decref(mrb, s1->as.heap.aux.shared);
@@ -645,10 +669,11 @@ MRB_API void
 mrb_str_modify(mrb_state *mrb, struct RString *s)
 {
   check_frozen(mrb, s);
+  s->flags &= ~MRB_STR_NO_UTF;
   if (RSTR_SHARED_P(s)) {
     mrb_shared_string *shared = s->as.heap.aux.shared;
 
-    if (shared->refcnt == 1 && s->as.heap.ptr == shared->ptr) {
+    if (shared->nofree == 0 && shared->refcnt == 1 && s->as.heap.ptr == shared->ptr) {
       s->as.heap.ptr = shared->ptr;
       s->as.heap.aux.capa = shared->len;
       RSTR_PTR(s)[s->as.heap.len] = '\0';
@@ -660,13 +685,20 @@ mrb_str_modify(mrb_state *mrb, struct RString *s)
 
       p = RSTR_PTR(s);
       len = s->as.heap.len;
-      ptr = (char *)mrb_malloc(mrb, (size_t)len + 1);
+      if (len < RSTRING_EMBED_LEN_MAX) {
+        RSTR_SET_EMBED_FLAG(s);
+        RSTR_SET_EMBED_LEN(s, len);
+        ptr = RSTR_PTR(s);
+      }
+      else {
+        ptr = (char *)mrb_malloc(mrb, (size_t)len + 1);
+        s->as.heap.ptr = ptr;
+        s->as.heap.aux.capa = len;
+      }
       if (p) {
         memcpy(ptr, p, len);
       }
       ptr[len] = '\0';
-      s->as.heap.ptr = ptr;
-      s->as.heap.aux.capa = len;
       str_decref(mrb, shared);
     }
     RSTR_UNSET_SHARED_FLAG(s);
@@ -674,27 +706,25 @@ mrb_str_modify(mrb_state *mrb, struct RString *s)
   }
   if (RSTR_NOFREE_P(s)) {
     char *p = s->as.heap.ptr;
+    mrb_int len = s->as.heap.len;
 
-    s->as.heap.ptr = (char *)mrb_malloc(mrb, (size_t)s->as.heap.len+1);
+    RSTR_UNSET_NOFREE_FLAG(s);
+    if (len < RSTRING_EMBED_LEN_MAX) {
+      RSTR_SET_EMBED_FLAG(s);
+      RSTR_SET_EMBED_LEN(s, len);
+    }
+    else {
+      s->as.heap.ptr = (char *)mrb_malloc(mrb, (size_t)len+1);
+      s->as.heap.aux.capa = len;
+    }
     if (p) {
-      memcpy(RSTR_PTR(s), p, s->as.heap.len);
+      memcpy(RSTR_PTR(s), p, len);
     }
-    RSTR_PTR(s)[s->as.heap.len] = '\0';
-    s->as.heap.aux.capa = s->as.heap.len;
-    RSTR_UNSET_NOFREE_FLAG(s);
+    RSTR_PTR(s)[len] = '\0';
     return;
   }
 }
 
-static mrb_value
-mrb_str_freeze(mrb_state *mrb, mrb_value str)
-{
-  struct RString *s = mrb_str_ptr(str);
-
-  RSTR_SET_FROZEN_FLAG(s);
-  return str;
-}
-
 MRB_API mrb_value
 mrb_str_resize(mrb_state *mrb, mrb_value str, mrb_int len)
 {
@@ -746,8 +776,14 @@ mrb_str_concat(mrb_state *mrb, mrb_value self, mrb_value other)
     other = mrb_str_to_str(mrb, other);
   }
   s2 = mrb_str_ptr(other);
+  if (RSTR_LEN(s2) == 0) {
+    return;
+  }
   len = RSTR_LEN(s1) + RSTR_LEN(s2);
 
+  if (len < 0 || len >= MRB_INT_MAX) {
+    mrb_raise(mrb, E_ARGUMENT_ERROR, "string size too big");
+  }
   if (RSTRING_CAPA(self) < len) {
     resize_capa(mrb, s1, len);
   }
@@ -931,8 +967,8 @@ mrb_str_cmp_m(mrb_state *mrb, mrb_value str1)
     else {
       mrb_value tmp = mrb_funcall(mrb, str2, "<=>", 1, str1);
 
-      if (mrb_nil_p(tmp)) return mrb_nil_value();
-      if (!mrb_fixnum(tmp)) {
+      if (!mrb_nil_p(tmp)) return mrb_nil_value();
+      if (!mrb_fixnum_p(tmp)) {
         return mrb_funcall(mrb, mrb_fixnum_value(0), "-", 1, tmp);
       }
       result = -mrb_fixnum(tmp);
@@ -1013,6 +1049,13 @@ mrb_string_value_ptr(mrb_state *mrb, mrb_value ptr)
   return RSTRING_PTR(str);
 }
 
+MRB_API mrb_int
+mrb_string_value_len(mrb_state *mrb, mrb_value ptr)
+{
+  mrb_value str = mrb_str_to_str(mrb, ptr);
+  return RSTRING_LEN(str);
+}
+
 void
 mrb_noregexp(mrb_state *mrb, mrb_value self)
 {
@@ -1058,22 +1101,25 @@ num_index:
       return mrb_nil_value();
 
     case MRB_TT_RANGE:
-      /* check if indx is Range */
-      {
-        mrb_int beg, len;
+      goto range_arg;
 
-        len = RSTRING_CHAR_LEN(str);
-        if (mrb_range_beg_len(mrb, indx, &beg, &len, len)) {
-          return str_subseq(mrb, str, beg, len);
-        }
-        else {
-          return mrb_nil_value();
-        }
-      }
-    case MRB_TT_FLOAT:
     default:
       indx = mrb_Integer(mrb, indx);
       if (mrb_nil_p(indx)) {
+      range_arg:
+        {
+          mrb_int beg, len;
+
+          len = RSTRING_CHAR_LEN(str);
+          switch (mrb_range_beg_len(mrb, indx, &beg, &len, len, TRUE)) {
+          case 1:
+            return str_subseq(mrb, str, beg, len);
+          case 2:
+            return mrb_nil_value();
+          default:
+            break;
+          }
+        }
         mrb_raise(mrb, E_TYPE_ERROR, "can't convert to Fixnum");
       }
       idx = mrb_fixnum(indx);
@@ -1130,8 +1176,11 @@ mrb_str_aref_m(mrb_state *mrb, mrb_value str)
 
   argc = mrb_get_args(mrb, "o|o", &a1, &a2);
   if (argc == 2) {
+    mrb_int n1, n2;
+
     mrb_regexp_check(mrb, a1);
-    return str_substr(mrb, str, mrb_fixnum(a1), mrb_fixnum(a2));
+    mrb_get_args(mrb, "ii", &n1, &n2);
+    return str_substr(mrb, str, n1, n2);
   }
   if (argc != 1) {
     mrb_raisef(mrb, E_ARGUMENT_ERROR, "wrong number of arguments (%S for 1)", mrb_fixnum_value(argc));
@@ -1214,11 +1263,13 @@ mrb_str_chomp_bang(mrb_state *mrb, mrb_value str)
   char *p, *pp;
   mrb_int rslen;
   mrb_int len;
+  mrb_int argc;
   struct RString *s = mrb_str_ptr(str);
 
   mrb_str_modify(mrb, s);
+  argc = mrb_get_args(mrb, "|S", &rs);
   len = RSTR_LEN(s);
-  if (mrb_get_args(mrb, "|S", &rs) == 0) {
+  if (argc == 0) {
     if (len == 0) return mrb_nil_value();
   smart_chomp:
     if (RSTR_PTR(s)[len-1] == '\n') {
@@ -1470,13 +1521,13 @@ mrb_str_hash(mrb_state *mrb, mrb_value str)
   struct RString *s = mrb_str_ptr(str);
   mrb_int len = RSTR_LEN(s);
   char *p = RSTR_PTR(s);
-  mrb_int key = 0;
+  uint64_t key = 0;
 
   while (len--) {
     key = key*65599 + *p;
     p++;
   }
-  return key + (key>>5);
+  return (mrb_int)(key + (key>>5));
 }
 
 /* 15.2.10.5.20 */
@@ -1509,22 +1560,12 @@ mrb_str_hash_m(mrb_state *mrb, mrb_value self)
 static mrb_value
 mrb_str_include(mrb_state *mrb, mrb_value self)
 {
-  mrb_int i;
   mrb_value str2;
-  mrb_bool include_p;
 
-  mrb_get_args(mrb, "o", &str2);
-  if (mrb_fixnum_p(str2)) {
-    include_p = (memchr(RSTRING_PTR(self), mrb_fixnum(str2), RSTRING_LEN(self)) != NULL);
-  }
-  else {
-    str2 = mrb_str_to_str(mrb, str2);
-    i = str_index(mrb, self, str2, 0);
-
-    include_p = (i != -1);
-  }
-
-  return mrb_bool_value(include_p);
+  mrb_get_args(mrb, "S", &str2);
+  if (str_index(mrb, self, str2, 0) < 0)
+    return mrb_bool_value(FALSE);
+  return mrb_bool_value(TRUE);
 }
 
 /* 15.2.10.5.22 */
@@ -1556,10 +1597,9 @@ mrb_str_index(mrb_state *mrb, mrb_value str)
   mrb_value sub;
   mrb_int pos, clen;
 
-  mrb_get_args(mrb, "*", &argv, &argc);
+  mrb_get_args(mrb, "*!", &argv, &argc);
   if (argc == 2) {
-    pos = mrb_fixnum(argv[1]);
-    sub = argv[0];
+    mrb_get_args(mrb, "oi", &sub, &pos);
   }
   else {
     pos = 0;
@@ -1576,7 +1616,7 @@ mrb_str_index(mrb_state *mrb, mrb_value str)
       return mrb_nil_value();
     }
   }
-  if (pos >= clen) return mrb_nil_value();
+  if (pos > clen) return mrb_nil_value();
   pos = chars2bytes(str, 0, pos);
 
   switch (mrb_type(sub)) {
@@ -1597,6 +1637,7 @@ mrb_str_index(mrb_state *mrb, mrb_value str)
 
   if (pos == -1) return mrb_nil_value();
   pos = bytes2chars(RSTRING_PTR(str), pos);
+  BYTES_ALIGN_CHECK(pos);
   return mrb_fixnum_value(pos);
 }
 
@@ -1632,9 +1673,11 @@ mrb_str_init(mrb_state *mrb, mrb_value self)
 {
   mrb_value str2;
 
-  if (mrb_get_args(mrb, "|S", &str2) == 1) {
-    str_replace(mrb, mrb_str_ptr(self), mrb_str_ptr(str2));
+  if (mrb_get_args(mrb, "|S", &str2) == 0) {
+    struct RString *s = str_new(mrb, 0, 0);
+    str2 = mrb_obj_value(s);
   }
+  str_replace(mrb, mrb_str_ptr(self), mrb_str_ptr(str2));
   return self;
 }
 
@@ -1742,7 +1785,7 @@ mrb_str_reverse_bang(mrb_state *mrb, mrb_value str)
 
     mrb_str_modify(mrb, mrb_str_ptr(str));
     len = RSTRING_LEN(str);
-    buf = mrb_malloc(mrb, (size_t)len);
+    buf = (char*)mrb_malloc(mrb, (size_t)len);
     p = buf;
     e = buf + len;
 
@@ -1823,14 +1866,11 @@ mrb_str_rindex(mrb_state *mrb, mrb_value str)
   mrb_value *argv;
   mrb_int argc;
   mrb_value sub;
-  mrb_value vpos;
   mrb_int pos, len = RSTRING_CHAR_LEN(str);
 
-  mrb_get_args(mrb, "*", &argv, &argc);
+  mrb_get_args(mrb, "*!", &argv, &argc);
   if (argc == 2) {
-    sub = argv[0];
-    vpos = argv[1];
-    pos = mrb_fixnum(vpos);
+    mrb_get_args(mrb, "oi", &sub, &pos);
     if (pos < 0) {
       pos += len;
       if (pos < 0) {
@@ -1848,7 +1888,6 @@ mrb_str_rindex(mrb_state *mrb, mrb_value str)
       sub = mrb_nil_value();
   }
   pos = chars2bytes(str, 0, pos);
-  len = chars2bytes(str, pos, len);
   mrb_regexp_check(mrb, sub);
 
   switch (mrb_type(sub)) {
@@ -1866,6 +1905,7 @@ mrb_str_rindex(mrb_state *mrb, mrb_value str)
       pos = str_rindex(mrb, str, sub, pos);
       if (pos >= 0) {
         pos = bytes2chars(RSTRING_PTR(str), pos);
+        BYTES_ALIGN_CHECK(pos);
         return mrb_fixnum_value(pos);
       }
       break;
@@ -1921,10 +1961,11 @@ mrb_str_split_m(mrb_state *mrb, mrb_value str)
   int argc;
   mrb_value spat = mrb_nil_value();
   enum {awk, string, regexp} split_type = string;
-  long i = 0, lim_p;
+  mrb_int i = 0;
   mrb_int beg;
   mrb_int end;
   mrb_int lim = 0;
+  mrb_bool lim_p;
   mrb_value result, tmp;
 
   argc = mrb_get_args(mrb, "|oi", &spat, &lim);
@@ -1997,7 +2038,8 @@ mrb_str_split_m(mrb_state *mrb, mrb_value str)
       if (pat_len > 0) {
         end = mrb_memsearch(RSTRING_PTR(spat), pat_len, RSTRING_PTR(str)+idx, str_len - idx);
         if (end < 0) break;
-      } else {
+      }
+      else {
         end = chars2bytes(str, idx, 1);
       }
       mrb_ary_push(mrb, result, byte_subseq(mrb, str, idx, end));
@@ -2030,11 +2072,12 @@ mrb_str_split_m(mrb_state *mrb, mrb_value str)
 }
 
 MRB_API mrb_value
-mrb_cstr_to_inum(mrb_state *mrb, const char *str, int base, int badcheck)
+mrb_str_len_to_inum(mrb_state *mrb, const char *str, size_t len, int base, int badcheck)
 {
-  const char *p;
+  const char *p = str;
+  const char *pend = str + len;
   char sign = 1;
-  int c, uscore;
+  int c;
   uint64_t n = 0;
   mrb_int val;
 
@@ -2044,26 +2087,23 @@ mrb_cstr_to_inum(mrb_state *mrb, const char *str, int base, int badcheck)
      ISUPPER(c) ? ((c) - 'A' + 10) : \
      -1)
 
-  if (!str) {
+  if (!p) {
     if (badcheck) goto bad;
     return mrb_fixnum_value(0);
   }
-  while (ISSPACE(*str)) str++;
+  while (p<pend && ISSPACE(*p))
+    p++;
 
-  if (str[0] == '+') {
-    str++;
+  if (p[0] == '+') {
+    p++;
   }
-  else if (str[0] == '-') {
-    str++;
+  else if (p[0] == '-') {
+    p++;
     sign = 0;
   }
-  if (str[0] == '+' || str[0] == '-') {
-    if (badcheck) goto bad;
-    return mrb_fixnum_value(0);
-  }
   if (base <= 0) {
-    if (str[0] == '0') {
-      switch (str[1]) {
+    if (p[0] == '0') {
+      switch (p[1]) {
         case 'x': case 'X':
           base = 16;
           break;
@@ -2078,6 +2118,7 @@ mrb_cstr_to_inum(mrb_state *mrb, const char *str, int base, int badcheck)
           break;
         default:
           base = 8;
+          break;
       }
     }
     else if (base < -1) {
@@ -2089,27 +2130,27 @@ mrb_cstr_to_inum(mrb_state *mrb, const char *str, int base, int badcheck)
   }
   switch (base) {
     case 2:
-      if (str[0] == '0' && (str[1] == 'b'||str[1] == 'B')) {
-        str += 2;
+      if (p[0] == '0' && (p[1] == 'b'||p[1] == 'B')) {
+        p += 2;
       }
       break;
     case 3:
       break;
     case 8:
-      if (str[0] == '0' && (str[1] == 'o'||str[1] == 'O')) {
-        str += 2;
+      if (p[0] == '0' && (p[1] == 'o'||p[1] == 'O')) {
+        p += 2;
       }
     case 4: case 5: case 6: case 7:
       break;
     case 10:
-      if (str[0] == '0' && (str[1] == 'd'||str[1] == 'D')) {
-        str += 2;
+      if (p[0] == '0' && (p[1] == 'd'||p[1] == 'D')) {
+        p += 2;
       }
     case 9: case 11: case 12: case 13: case 14: case 15:
       break;
     case 16:
-      if (str[0] == '0' && (str[1] == 'x'||str[1] == 'X')) {
-        str += 2;
+      if (p[0] == '0' && (p[1] == 'x'||p[1] == 'X')) {
+        p += 2;
       }
       break;
     default:
@@ -2118,61 +2159,83 @@ mrb_cstr_to_inum(mrb_state *mrb, const char *str, int base, int badcheck)
       }
       break;
   } /* end of switch (base) { */
-  if (*str == '0') {    /* squeeze preceding 0s */
-    uscore = 0;
-    while ((c = *++str) == '0' || c == '_') {
+  if (p>=pend) {
+    if (badcheck) goto bad;
+    return mrb_fixnum_value(0);
+  }
+  if (*p == '0') {    /* squeeze preceding 0s */
+    p++;
+    while (p<pend) {
+      c = *p++;
       if (c == '_') {
-        if (++uscore >= 2)
+        if (p<pend && *p == '_') {
+          if (badcheck) goto bad;
           break;
+        }
+        continue;
+      }
+      if (c != '0') {
+        p--;
+        break;
       }
-      else
-        uscore = 0;
     }
-    if (!(c = *str) || ISSPACE(c)) --str;
+    if (*(p - 1) == '0')
+      p--;
   }
-  c = *str;
-  c = conv_digit(c);
-  if (c < 0 || c >= base) {
+  if (p == pend) {
     if (badcheck) goto bad;
     return mrb_fixnum_value(0);
   }
-
-  uscore = 0;
-  for (p=str;*p;p++) {
+  for ( ;p<pend;p++) {
     if (*p == '_') {
-      if (uscore == 0) {
-        uscore++;
+      p++;
+      if (p==pend) {
+        if (badcheck) goto bad;
         continue;
       }
-      if (badcheck) goto bad;
-      break;
+      if (*p == '_') {
+        if (badcheck) goto bad;
+        break;
+      }
+    }
+    if (badcheck && *p == '\0') {
+      goto nullbyte;
     }
-    uscore = 0;
     c = conv_digit(*p);
     if (c < 0 || c >= base) {
-      if (badcheck) goto bad;
       break;
     }
     n *= base;
     n += c;
-    if (n > MRB_INT_MAX) {
-      mrb_raisef(mrb, E_ARGUMENT_ERROR, "string (%S) too big for integer", mrb_str_new_cstr(mrb, str));
+    if (n > (uint64_t)MRB_INT_MAX + (sign ? 0 : 1)) {
+      mrb_raisef(mrb, E_ARGUMENT_ERROR, "string (%S) too big for integer",
+                 mrb_str_new(mrb, str, pend-str));
     }
   }
-  val = n;
+  val = (mrb_int)n;
   if (badcheck) {
     if (p == str) goto bad; /* no number */
-    while (*p && ISSPACE(*p)) p++;
-    if (*p) goto bad;           /* trailing garbage */
+    while (p<pend && ISSPACE(*p)) p++;
+    if (p<pend) goto bad;       /* trailing garbage */
   }
 
   return mrb_fixnum_value(sign ? val : -val);
-bad:
-  mrb_raisef(mrb, E_ARGUMENT_ERROR, "invalid string for number(%S)", mrb_str_new_cstr(mrb, str));
+ nullbyte:
+  mrb_raise(mrb, E_ARGUMENT_ERROR, "string contains null byte");
+  /* not reached */
+ bad:
+  mrb_raisef(mrb, E_ARGUMENT_ERROR, "invalid string for number(%S)",
+             mrb_inspect(mrb, mrb_str_new(mrb, str, pend-str)));
   /* not reached */
   return mrb_fixnum_value(0);
 }
 
+MRB_API mrb_value
+mrb_cstr_to_inum(mrb_state *mrb, const char *str, int base, int badcheck)
+{
+  return mrb_str_len_to_inum(mrb, str, strlen(str), base, badcheck);
+}
+
 MRB_API const char*
 mrb_string_value_cstr(mrb_state *mrb, mrb_value *ptr)
 {
@@ -2182,6 +2245,10 @@ mrb_string_value_cstr(mrb_state *mrb, mrb_value *ptr)
   char *p = RSTR_PTR(ps);
 
   if (!p || p[len] != '\0') {
+    if (MRB_FROZEN_P(ps)) {
+      *ptr = str = mrb_str_dup(mrb, str);
+      ps = mrb_str_ptr(str);
+    }
     mrb_str_modify(mrb, ps);
     return RSTR_PTR(ps);
   }
@@ -2194,21 +2261,9 @@ mrb_str_to_inum(mrb_state *mrb, mrb_value str, mrb_int base, mrb_bool badcheck)
   const char *s;
   mrb_int len;
 
-  if (badcheck) {
-    /* Raises if the string contains a null character (the badcheck) */
-    s = mrb_string_value_cstr(mrb, &str);
-  }
-  else {
-    s = mrb_string_value_ptr(mrb, str);
-  }
-  if (s) {
-    len = RSTRING_LEN(str);
-    if (s[len]) {    /* no sentinel somehow */
-      struct RString *temp_str = str_new(mrb, s, len);
-      s = RSTR_PTR(temp_str);
-    }
-  }
-  return mrb_cstr_to_inum(mrb, s, base, badcheck);
+  s = mrb_string_value_ptr(mrb, str);
+  len = RSTRING_LEN(str);
+  return mrb_str_len_to_inum(mrb, s, len, base, badcheck);
 }
 
 /* 15.2.10.5.38 */
@@ -2259,7 +2314,7 @@ mrb_cstr_to_dbl(mrb_state *mrb, const char * p, mrb_bool badcheck)
   if (!badcheck && p[0] == '0' && (p[1] == 'x' || p[1] == 'X')) {
     return 0.0;
   }
-  d = strtod(p, &end);
+  d = mrb_float_read(p, &end);
   if (p == end) {
     if (badcheck) {
 bad:
@@ -2297,7 +2352,7 @@ bad:
       return 0.0;
     }
 
-    d = strtod(p, &end);
+    d = mrb_float_read(p, &end);
     if (badcheck) {
       if (!end || p == end) goto bad;
       while (*end && ISSPACE(*end)) end++;
@@ -2602,7 +2657,7 @@ mrb_str_inspect(mrb_state *mrb, mrb_value str)
     }
 #endif
     c = *p;
-    if (c == '"'|| c == '\\' || (c == '#' && IS_EVSTR(p, pend))) {
+    if (c == '"'|| c == '\\' || (c == '#' && IS_EVSTR(p+1, pend))) {
       buf[0] = '\\'; buf[1] = c;
       mrb_str_cat(mrb, result, buf, 2);
       continue;
@@ -2688,8 +2743,8 @@ mrb_init_string(mrb_state *mrb)
   mrb_define_method(mrb, s, "capitalize!",     mrb_str_capitalize_bang, MRB_ARGS_NONE()); /* 15.2.10.5.8  */
   mrb_define_method(mrb, s, "chomp",           mrb_str_chomp,           MRB_ARGS_ANY());  /* 15.2.10.5.9  */
   mrb_define_method(mrb, s, "chomp!",          mrb_str_chomp_bang,      MRB_ARGS_ANY());  /* 15.2.10.5.10 */
-  mrb_define_method(mrb, s, "chop",            mrb_str_chop,            MRB_ARGS_REQ(1)); /* 15.2.10.5.11 */
-  mrb_define_method(mrb, s, "chop!",           mrb_str_chop_bang,       MRB_ARGS_REQ(1)); /* 15.2.10.5.12 */
+  mrb_define_method(mrb, s, "chop",            mrb_str_chop,            MRB_ARGS_NONE()); /* 15.2.10.5.11 */
+  mrb_define_method(mrb, s, "chop!",           mrb_str_chop_bang,       MRB_ARGS_NONE()); /* 15.2.10.5.12 */
   mrb_define_method(mrb, s, "downcase",        mrb_str_downcase,        MRB_ARGS_NONE()); /* 15.2.10.5.13 */
   mrb_define_method(mrb, s, "downcase!",       mrb_str_downcase_bang,   MRB_ARGS_NONE()); /* 15.2.10.5.14 */
   mrb_define_method(mrb, s, "empty?",          mrb_str_empty_p,         MRB_ARGS_NONE()); /* 15.2.10.5.16 */
@@ -2719,6 +2774,249 @@ mrb_init_string(mrb_state *mrb)
   mrb_define_method(mrb, s, "upcase!",         mrb_str_upcase_bang,     MRB_ARGS_NONE()); /* 15.2.10.5.43 */
   mrb_define_method(mrb, s, "inspect",         mrb_str_inspect,         MRB_ARGS_NONE()); /* 15.2.10.5.46(x) */
   mrb_define_method(mrb, s, "bytes",           mrb_str_bytes,           MRB_ARGS_NONE());
+}
+
+/*
+ *     Source code for the "strtod" library procedure.
+ *
+ * Copyright (c) 1988-1993 The Regents of the University of California.
+ * Copyright (c) 1994 Sun Microsystems, Inc.
+ *
+ * Permission to use, copy, modify, and distribute this
+ * software and its documentation for any purpose and without
+ * fee is hereby granted, provided that the above copyright
+ * notice appear in all copies.  The University of California
+ * makes no representations about the suitability of this
+ * software for any purpose.  It is provided "as is" without
+ * express or implied warranty.
+ *
+ * RCS: @(#) $Id: strtod.c 11708 2007-02-12 23:01:19Z shyouhei $
+ */
+
+#include <ctype.h>
+#include <errno.h>
+
+static const int maxExponent = 511; /* Largest possible base 10 exponent.  Any
+                                     * exponent larger than this will already
+                                     * produce underflow or overflow, so there's
+                                     * no need to worry about additional digits.
+                                     */
+static const double powersOf10[] = {/* Table giving binary powers of 10.  Entry */
+    10.,                            /* is 10^2^i.  Used to convert decimal */
+    100.,                          /* exponents into floating-point numbers. */
+    1.0e4,
+    1.0e8,
+    1.0e16,
+    1.0e32,
+    1.0e64,
+    1.0e128,
+    1.0e256
+};
+
+MRB_API double
+mrb_float_read(const char *string, char **endPtr)
+/*  const char *string;                   A decimal ASCII floating-point number,
+                                * optionally preceded by white space.
+                                * Must have form "-I.FE-X", where I is the
+                                * integer part of the mantissa, F is the
+                                * fractional part of the mantissa, and X
+                                * is the exponent.  Either of the signs
+                                * may be "+", "-", or omitted.  Either I
+                                * or F may be omitted, or both.  The decimal
+                                * point isn't necessary unless F is present.
+                                * The "E" may actually be an "e".  E and X
+                                * may both be omitted (but not just one).
+                                */
+/*  char **endPtr;                If non-NULL, store terminating character's
+                                * address here. */
+{
+    int sign, expSign = FALSE;
+    double fraction, dblExp;
+    const double *d;
+    register const char *p;
+    register int c;
+    int exp = 0;               /* Exponent read from "EX" field. */
+    int fracExp = 0;           /* Exponent that derives from the fractional
+                                * part.  Under normal circumstatnces, it is
+                                * the negative of the number of digits in F.
+                                * However, if I is very long, the last digits
+                                * of I get dropped (otherwise a long I with a
+                                * large negative exponent could cause an
+                                * unnecessary overflow on I alone).  In this
+                                * case, fracExp is incremented one for each
+                                * dropped digit. */
+    int mantSize;              /* Number of digits in mantissa. */
+    int decPt;                 /* Number of mantissa digits BEFORE decimal
+                                * point. */
+    const char *pExp;          /* Temporarily holds location of exponent
+                                * in string. */
+
+    /*
+     * Strip off leading blanks and check for a sign.
+     */
+
+    p = string;
+    while (isspace(*p)) {
+       p += 1;
+    }
+    if (*p == '-') {
+       sign = TRUE;
+       p += 1;
+    }
+    else {
+       if (*p == '+') {
+           p += 1;
+       }
+       sign = FALSE;
+    }
+
+    /*
+     * Count the number of digits in the mantissa (including the decimal
+     * point), and also locate the decimal point.
+     */
+
+    decPt = -1;
+    for (mantSize = 0; ; mantSize += 1)
+    {
+       c = *p;
+       if (!isdigit(c)) {
+           if ((c != '.') || (decPt >= 0)) {
+               break;
+           }
+           decPt = mantSize;
+       }
+       p += 1;
+    }
+
+    /*
+     * Now suck up the digits in the mantissa.  Use two integers to
+     * collect 9 digits each (this is faster than using floating-point).
+     * If the mantissa has more than 18 digits, ignore the extras, since
+     * they can't affect the value anyway.
+     */
+
+    pExp  = p;
+    p -= mantSize;
+    if (decPt < 0) {
+       decPt = mantSize;
+    }
+    else {
+       mantSize -= 1;                  /* One of the digits was the point. */
+    }
+    if (mantSize > 18) {
+       if (decPt - 18 > 29999) {
+           fracExp = 29999;
+       }
+        else {
+           fracExp = decPt - 18;
+       }
+       mantSize = 18;
+    }
+    else {
+       fracExp = decPt - mantSize;
+    }
+    if (mantSize == 0) {
+       fraction = 0.0;
+       p = string;
+       goto done;
+    }
+    else {
+       int frac1, frac2;
+       frac1 = 0;
+       for ( ; mantSize > 9; mantSize -= 1)
+       {
+           c = *p;
+           p += 1;
+           if (c == '.') {
+               c = *p;
+               p += 1;
+           }
+           frac1 = 10*frac1 + (c - '0');
+       }
+       frac2 = 0;
+       for (; mantSize > 0; mantSize -= 1)
+       {
+           c = *p;
+           p += 1;
+           if (c == '.') {
+               c = *p;
+               p += 1;
+           }
+           frac2 = 10*frac2 + (c - '0');
+       }
+       fraction = (1.0e9 * frac1) + frac2;
+    }
+
+    /*
+     * Skim off the exponent.
+     */
+
+    p = pExp;
+    if ((*p == 'E') || (*p == 'e')) {
+       p += 1;
+       if (*p == '-') {
+           expSign = TRUE;
+           p += 1;
+       }
+        else {
+           if (*p == '+') {
+               p += 1;
+           }
+           expSign = FALSE;
+       }
+       while (isdigit(*p)) {
+           exp = exp * 10 + (*p - '0');
+           if (exp > 19999) {
+               exp = 19999;
+           }
+           p += 1;
+       }
+    }
+    if (expSign) {
+       exp = fracExp - exp;
+    }
+    else {
+       exp = fracExp + exp;
+    }
+
+    /*
+     * Generate a floating-point number that represents the exponent.
+     * Do this by processing the exponent one bit at a time to combine
+     * many powers of 2 of 10. Then combine the exponent with the
+     * fraction.
+     */
+
+    if (exp < 0) {
+       expSign = TRUE;
+       exp = -exp;
+    }
+    else {
+       expSign = FALSE;
+    }
+    if (exp > maxExponent) {
+       exp = maxExponent;
+       errno = ERANGE;
+    }
+    dblExp = 1.0;
+    for (d = powersOf10; exp != 0; exp >>= 1, d += 1) {
+       if (exp & 01) {
+           dblExp *= *d;
+       }
+    }
+    if (expSign) {
+       fraction /= dblExp;
+    }
+    else {
+       fraction *= dblExp;
+    }
+
+done:
+    if (endPtr != NULL) {
+       *endPtr = (char *) p;
+    }
 
-  mrb_define_method(mrb, s, "freeze",          mrb_str_freeze,          MRB_ARGS_NONE());
+    if (sign) {
+       return -fraction;
+    }
+    return fraction;
 }
index e380a5b..a3ab05c 100644 (file)
@@ -6,10 +6,11 @@
 
 #include <limits.h>
 #include <string.h>
-#include "mruby.h"
-#include "mruby/khash.h"
-#include "mruby/string.h"
-#include "mruby/dump.h"
+#include <mruby.h>
+#include <mruby/khash.h>
+#include <mruby/string.h>
+#include <mruby/dump.h>
+#include <mruby/class.h>
 
 /* ------------------------------------------------------ */
 typedef struct symbol_name {
@@ -354,7 +355,9 @@ symname_p(const char *name)
       if (*++m == '*') ++m;
       break;
     case '!':
-      if (*++m == '=') ++m;
+      switch (*++m) {
+        case '=': case '~': ++m;
+      }
       break;
     case '+': case '-':
       if (*++m == '@') ++m;
@@ -479,6 +482,8 @@ mrb_init_symbol(mrb_state *mrb)
   struct RClass *sym;
 
   mrb->symbol_class = sym = mrb_define_class(mrb, "Symbol", mrb->object_class);                 /* 15.2.11 */
+  MRB_SET_INSTANCE_TT(sym, MRB_TT_SYMBOL);
+  mrb_undef_class_method(mrb,  sym, "new");
 
   mrb_define_method(mrb, sym, "===",             sym_equal,      MRB_ARGS_REQ(1));              /* 15.2.11.3.1  */
   mrb_define_method(mrb, sym, "id2name",         mrb_sym_to_s,   MRB_ARGS_NONE());              /* 15.2.11.3.2  */
index cabd242..bc5f28b 100644 (file)
@@ -1,7 +1,7 @@
 #ifndef MRB_VALUE_ARRAY_H__
 #define MRB_VALUE_ARRAY_H__
 
-#include "mruby.h"
+#include <mruby.h>
 
 static inline void
 value_move(mrb_value *s1, const mrb_value *s2, size_t n)
index fa1389c..86ac32e 100644 (file)
@@ -4,23 +4,21 @@
 ** See Copyright Notice in mruby.h
 */
 
-#include "mruby.h"
-#include "mruby/array.h"
-#include "mruby/class.h"
-#include "mruby/proc.h"
-#include "mruby/string.h"
+#include <mruby.h>
+#include <mruby/array.h>
+#include <mruby/class.h>
+#include <mruby/proc.h>
+#include <mruby/string.h>
 
 typedef int (iv_foreach_func)(mrb_state*,mrb_sym,mrb_value,void*);
 
-#ifdef MRB_USE_IV_SEGLIST
-
-#ifndef MRB_SEGMENT_SIZE
-#define MRB_SEGMENT_SIZE 4
+#ifndef MRB_IV_SEGMENT_SIZE
+#define MRB_IV_SEGMENT_SIZE 4
 #endif
 
 typedef struct segment {
-  mrb_sym key[MRB_SEGMENT_SIZE];
-  mrb_value val[MRB_SEGMENT_SIZE];
+  mrb_sym key[MRB_IV_SEGMENT_SIZE];
+  mrb_value val[MRB_IV_SEGMENT_SIZE];
   struct segment *next;
 } segment;
 
@@ -44,7 +42,7 @@ iv_new(mrb_state *mrb)
 {
   iv_tbl *t;
 
-  t = mrb_malloc(mrb, sizeof(iv_tbl));
+  t = (iv_tbl*)mrb_malloc(mrb, sizeof(iv_tbl));
   t->size = 0;
   t->rootseg =  NULL;
   t->last_len = 0;
@@ -71,7 +69,7 @@ iv_put(mrb_state *mrb, iv_tbl *t, mrb_sym sym, mrb_value val)
   size_t i;
 
   while (seg) {
-    for (i=0; i<MRB_SEGMENT_SIZE; i++) {
+    for (i=0; i<MRB_IV_SEGMENT_SIZE; i++) {
       mrb_sym key = seg->key[i];
       /* Found room in last segment after last_len */
       if (!seg->next && i >= t->last_len) {
@@ -102,7 +100,7 @@ iv_put(mrb_state *mrb, iv_tbl *t, mrb_sym sym, mrb_value val)
     return;
   }
 
-  seg = mrb_malloc(mrb, sizeof(segment));
+  seg = (segment*)mrb_malloc(mrb, sizeof(segment));
   if (!seg) return;
   seg->next = NULL;
   seg->key[0] = sym;
@@ -136,7 +134,7 @@ iv_get(mrb_state *mrb, iv_tbl *t, mrb_sym sym, mrb_value *vp)
 
   seg = t->rootseg;
   while (seg) {
-    for (i=0; i<MRB_SEGMENT_SIZE; i++) {
+    for (i=0; i<MRB_IV_SEGMENT_SIZE; i++) {
       mrb_sym key = seg->key[i];
 
       if (!seg->next && i >= t->last_len) {
@@ -171,7 +169,7 @@ iv_del(mrb_state *mrb, iv_tbl *t, mrb_sym sym, mrb_value *vp)
 
   seg = t->rootseg;
   while (seg) {
-    for (i=0; i<MRB_SEGMENT_SIZE; i++) {
+    for (i=0; i<MRB_IV_SEGMENT_SIZE; i++) {
       mrb_sym key = seg->key[i];
 
       if (!seg->next && i >= t->last_len) {
@@ -198,7 +196,7 @@ iv_foreach(mrb_state *mrb, iv_tbl *t, iv_foreach_func *func, void *p)
 
   seg = t->rootseg;
   while (seg) {
-    for (i=0; i<MRB_SEGMENT_SIZE; i++) {
+    for (i=0; i<MRB_IV_SEGMENT_SIZE; i++) {
       mrb_sym key = seg->key[i];
 
       /* no value in last segment after last_len */
@@ -234,7 +232,7 @@ iv_size(mrb_state *mrb, iv_tbl *t)
       return size;
     }
     seg = seg->next;
-    size += MRB_SEGMENT_SIZE;
+    size += MRB_IV_SEGMENT_SIZE;
   }
   /* empty iv_tbl */
   return 0;
@@ -252,7 +250,7 @@ iv_copy(mrb_state *mrb, iv_tbl *t)
   t2 = iv_new(mrb);
 
   while (seg != NULL) {
-    for (i=0; i<MRB_SEGMENT_SIZE; i++) {
+    for (i=0; i<MRB_IV_SEGMENT_SIZE; i++) {
       mrb_sym key = seg->key[i];
       mrb_value val = seg->val[i];
 
@@ -280,115 +278,6 @@ iv_free(mrb_state *mrb, iv_tbl *t)
   mrb_free(mrb, t);
 }
 
-#else
-
-#include "mruby/khash.h"
-
-#ifndef MRB_IVHASH_INIT_SIZE
-#define MRB_IVHASH_INIT_SIZE 8
-#endif
-
-KHASH_DECLARE(iv, mrb_sym, mrb_value, TRUE)
-KHASH_DEFINE(iv, mrb_sym, mrb_value, TRUE, kh_int_hash_func, kh_int_hash_equal)
-
-typedef struct iv_tbl {
-  khash_t(iv) h;
-} iv_tbl;
-
-static iv_tbl*
-iv_new(mrb_state *mrb)
-{
-  return (iv_tbl*)kh_init_size(iv, mrb, MRB_IVHASH_INIT_SIZE);
-}
-
-static void
-iv_put(mrb_state *mrb, iv_tbl *t, mrb_sym sym, mrb_value val)
-{
-  khash_t(iv) *h = &t->h;
-  khiter_t k;
-
-  k = kh_put(iv, mrb, h, sym);
-  kh_value(h, k) = val;
-}
-
-static mrb_bool
-iv_get(mrb_state *mrb, iv_tbl *t, mrb_sym sym, mrb_value *vp)
-{
-  khash_t(iv) *h = &t->h;
-  khiter_t k;
-
-  k = kh_get(iv, mrb, h, sym);
-  if (k != kh_end(h)) {
-    if (vp) *vp = kh_value(h, k);
-    return TRUE;
-  }
-  return FALSE;
-}
-
-static mrb_bool
-iv_del(mrb_state *mrb, iv_tbl *t, mrb_sym sym, mrb_value *vp)
-{
-  khash_t(iv) *h = &t->h;
-  khiter_t k;
-
-  if (h) {
-    k = kh_get(iv, mrb, h, sym);
-    if (k != kh_end(h)) {
-      mrb_value val = kh_value(h, k);
-      kh_del(iv, mrb, h, k);
-      if (vp) *vp = val;
-      return TRUE;
-    }
-  }
-  return FALSE;
-}
-
-static mrb_bool
-iv_foreach(mrb_state *mrb, iv_tbl *t, iv_foreach_func *func, void *p)
-{
-  khash_t(iv) *h = &t->h;
-  khiter_t k;
-  int n;
-
-  if (h) {
-    for (k = kh_begin(h); k != kh_end(h); k++) {
-      if (kh_exist(h, k)) {
-        n = (*func)(mrb, kh_key(h, k), kh_value(h, k), p);
-        if (n > 0) return FALSE;
-        if (n < 0) {
-          kh_del(iv, mrb, h, k);
-        }
-      }
-    }
-  }
-  return TRUE;
-}
-
-static size_t
-iv_size(mrb_state *mrb, iv_tbl *t)
-{
-  khash_t(iv) *h;
-
-  if (t && (h = &t->h)) {
-    return kh_size(h);
-  }
-  return 0;
-}
-
-static iv_tbl*
-iv_copy(mrb_state *mrb, iv_tbl *t)
-{
-  return (iv_tbl*)kh_copy(iv, mrb, &t->h);
-}
-
-static void
-iv_free(mrb_state *mrb, iv_tbl *t)
-{
-  kh_destroy(iv, mrb, &t->h);
-}
-
-#endif
-
 static int
 iv_mark_i(mrb_state *mrb, mrb_sym sym, mrb_value v, void *p)
 {
@@ -489,6 +378,9 @@ mrb_obj_iv_set(mrb_state *mrb, struct RObject *obj, mrb_sym sym, mrb_value v)
 {
   iv_tbl *t = obj->iv;
 
+  if (MRB_FROZEN_P(obj)) {
+    mrb_raisef(mrb, E_RUNTIME_ERROR, "can't modify frozen %S", mrb_obj_value(obj));
+  }
   if (!t) {
     t = obj->iv = iv_new(mrb);
   }
@@ -756,30 +648,34 @@ mrb_mod_class_variables(mrb_state *mrb, mrb_value mod)
 }
 
 MRB_API mrb_value
-mrb_mod_cv_get(mrb_state *mrb, struct RClass * c, mrb_sym sym)
+mrb_mod_cv_get(mrb_state *mrb, struct RClass *c, mrb_sym sym)
 {
   struct RClass * cls = c;
   mrb_value v;
+  int given = FALSE;
 
   while (c) {
     if (c->iv && iv_get(mrb, c->iv, sym, &v)) {
-      return v;
+      given = TRUE;
     }
     c = c->super;
   }
+  if (given) return v;
   if (cls && cls->tt == MRB_TT_SCLASS) {
     mrb_value klass;
 
     klass = mrb_obj_iv_get(mrb, (struct RObject *)cls,
                            mrb_intern_lit(mrb, "__attached__"));
     c = mrb_class_ptr(klass);
-    if (c->tt == MRB_TT_CLASS) {
+    if (c->tt == MRB_TT_CLASS || c->tt == MRB_TT_MODULE) {
+      given = FALSE;
       while (c) {
         if (c->iv && iv_get(mrb, c->iv, sym, &v)) {
-          return v;
+          given = TRUE;
         }
         c = c->super;
       }
+      if (given) return v;
     }
   }
   mrb_name_error(mrb, sym, "uninitialized class variable %S in %S",
@@ -812,12 +708,32 @@ mrb_mod_cv_set(mrb_state *mrb, struct RClass *c, mrb_sym sym, mrb_value v)
     c = c->super;
   }
 
-  if (!cls->iv) {
-    cls->iv = iv_new(mrb);
+  if (cls && cls->tt == MRB_TT_SCLASS) {
+    mrb_value klass;
+
+    klass = mrb_obj_iv_get(mrb, (struct RObject*)cls,
+                           mrb_intern_lit(mrb, "__attached__"));
+    switch (mrb_type(klass)) {
+    case MRB_TT_CLASS:
+    case MRB_TT_MODULE:
+    case MRB_TT_SCLASS:
+      c = mrb_class_ptr(klass);
+      break;
+    default:
+      c = cls;
+      break;
+    }
+  }
+  else{
+    c = cls;
   }
 
-  mrb_write_barrier(mrb, (struct RBasic*)cls);
-  iv_put(mrb, cls->iv, sym, v);
+  if (!c->iv) {
+    c->iv = iv_new(mrb);
+  }
+
+  mrb_write_barrier(mrb, (struct RBasic*)c);
+  iv_put(mrb, c->iv, sym, v);
 }
 
 MRB_API void
@@ -926,14 +842,14 @@ mrb_vm_const_get(mrb_state *mrb, mrb_sym sym)
     if (c->iv && iv_get(mrb, c->iv, sym, &v)) {
       return v;
     }
-    if (c->tt == MRB_TT_SCLASS) {
+    c2 = c;
+    while (c2 && c2->tt == MRB_TT_SCLASS) {
       mrb_value klass;
-      klass = mrb_obj_iv_get(mrb, (struct RObject *)c,
+      klass = mrb_obj_iv_get(mrb, (struct RObject *)c2,
                              mrb_intern_lit(mrb, "__attached__"));
       c2 = mrb_class_ptr(klass);
-      if (c2->tt == MRB_TT_CLASS)
-        c = c2;
     }
+    if (c2->tt == MRB_TT_CLASS || c2->tt == MRB_TT_MODULE) c = c2;
     c2 = c;
     for (;;) {
       c2 = mrb_class_outer_module(mrb, c2);
index 31a75f1..350bc16 100644 (file)
@@ -1,5 +1,5 @@
-#include "mruby.h"
-#include "mruby/variable.h"
+#include <mruby.h>
+#include <mruby/variable.h>
 
 void
 mrb_init_version(mrb_state* mrb)
@@ -9,7 +9,7 @@ mrb_init_version(mrb_state* mrb)
   mrb_define_global_const(mrb, "RUBY_VERSION", mrb_str_new_lit(mrb, MRUBY_RUBY_VERSION));
   mrb_define_global_const(mrb, "RUBY_ENGINE", mrb_str_new_lit(mrb, MRUBY_RUBY_ENGINE));
   mrb_define_global_const(mrb, "RUBY_ENGINE_VERSION", mruby_version);
-  mrb_define_global_const(mrb, "MRUBY_VERSION", mrb_str_new_lit(mrb, MRUBY_VERSION));
+  mrb_define_global_const(mrb, "MRUBY_VERSION", mruby_version);
   mrb_define_global_const(mrb, "MRUBY_RELEASE_NO", mrb_fixnum_value(MRUBY_RELEASE_NO));
   mrb_define_global_const(mrb, "MRUBY_RELEASE_DATE", mrb_str_new_lit(mrb, MRUBY_RELEASE_DATE));
   mrb_define_global_const(mrb, "MRUBY_DESCRIPTION", mrb_str_new_lit(mrb, MRUBY_DESCRIPTION));
index 174c041..45f9bf9 100644 (file)
@@ -7,22 +7,22 @@
 #include <stddef.h>
 #include <stdarg.h>
 #include <math.h>
-#include "mruby.h"
-#include "mruby/array.h"
-#include "mruby/class.h"
-#include "mruby/hash.h"
-#include "mruby/irep.h"
-#include "mruby/numeric.h"
-#include "mruby/proc.h"
-#include "mruby/range.h"
-#include "mruby/string.h"
-#include "mruby/variable.h"
-#include "mruby/error.h"
-#include "mruby/opcode.h"
+#include <mruby.h>
+#include <mruby/array.h>
+#include <mruby/class.h>
+#include <mruby/hash.h>
+#include <mruby/irep.h>
+#include <mruby/numeric.h>
+#include <mruby/proc.h>
+#include <mruby/range.h>
+#include <mruby/string.h>
+#include <mruby/variable.h>
+#include <mruby/error.h>
+#include <mruby/opcode.h>
 #include "value_array.h"
-#include "mruby/throw.h"
+#include <mruby/throw.h>
 
-#ifndef MRB_DISABLE_STDIO
+#ifdef MRB_DISABLE_STDIO
 #if defined(__cplusplus)
 extern "C" {
 #endif
@@ -35,11 +35,24 @@ void abort(void);
 #define STACK_INIT_SIZE 128
 #define CALLINFO_INIT_SIZE 32
 
+#ifndef ENSURE_STACK_INIT_SIZE
+#define ENSURE_STACK_INIT_SIZE 16
+#endif
+
+#ifndef RESCUE_STACK_INIT_SIZE
+#define RESCUE_STACK_INIT_SIZE 16
+#endif
+
 /* Define amount of linear stack growth. */
 #ifndef MRB_STACK_GROWTH
 #define MRB_STACK_GROWTH 128
 #endif
 
+/* Maximum mrb_funcall() depth. Should be set lower on memory constrained systems. */
+#ifndef MRB_FUNCALL_DEPTH_MAX
+#define MRB_FUNCALL_DEPTH_MAX 512
+#endif
+
 /* Maximum stack depth. Should be set lower on memory constrained systems.
 The value below allows about 60000 recursive calls in the simplest case. */
 #ifndef MRB_STACK_MAX
@@ -52,7 +65,32 @@ The value below allows about 60000 recursive calls in the simplest case. */
 # define DEBUG(x)
 #endif
 
-#define ARENA_RESTORE(mrb,ai) (mrb)->gc.arena_idx = (ai)
+
+#ifndef MRB_GC_FIXED_ARENA
+static void
+mrb_gc_arena_shrink(mrb_state *mrb, int idx)
+{
+  mrb_gc *gc = &mrb->gc;
+  int capa = gc->arena_capa;
+
+  if (idx < capa / 4) {
+    capa >>= 2;
+    if (capa < MRB_GC_ARENA_SIZE) {
+      capa = MRB_GC_ARENA_SIZE;
+    }
+    if (capa != gc->arena_capa) {
+      gc->arena = (struct RBasic**)mrb_realloc(mrb, gc->arena, sizeof(struct RBasic*)*capa);
+      gc->arena_capa = capa;
+    }
+  }
+}
+#else
+#define mrb_gc_arena_shrink(mrb,idx)
+#endif
+
+#define CALL_MAXARGS 127
+
+void mrb_method_missing(mrb_state *mrb, mrb_sym name, mrb_value self, mrb_value args);
 
 static inline void
 stack_clear(mrb_value *from, size_t count)
@@ -98,14 +136,17 @@ stack_init(mrb_state *mrb)
 }
 
 static inline void
-envadjust(mrb_state *mrb, mrb_value *oldbase, mrb_value *newbase)
+envadjust(mrb_state *mrb, mrb_value *oldbase, mrb_value *newbase, size_t size)
 {
   mrb_callinfo *ci = mrb->c->cibase;
 
   if (newbase == oldbase) return;
   while (ci <= mrb->c->ci) {
     struct REnv *e = ci->env;
-    if (e && MRB_ENV_STACK_SHARED_P(e)) {
+    mrb_value *st;
+
+    if (e && MRB_ENV_STACK_SHARED_P(e) &&
+        (st = e->stack) && oldbase <= st && st < oldbase+size) {
       ptrdiff_t off = e->stack - oldbase;
 
       e->stack = newbase + off;
@@ -115,24 +156,18 @@ envadjust(mrb_state *mrb, mrb_value *oldbase, mrb_value *newbase)
   }
 }
 
-static inline void
-init_new_stack_space(mrb_state *mrb, int room, int keep)
-{
-  if (room > keep) {
-    /* do not leave uninitialized malloc region */
-    stack_clear(&(mrb->c->stack[keep]), room - keep);
-  }
-}
-
 /** def rec ; $deep =+ 1 ; if $deep > 1000 ; return 0 ; end ; rec ; end  */
 
 static void
-stack_extend_alloc(mrb_state *mrb, int room, int keep)
+stack_extend_alloc(mrb_state *mrb, int room)
 {
   mrb_value *oldbase = mrb->c->stbase;
-  int size = mrb->c->stend - mrb->c->stbase;
-  int off = mrb->c->stack - mrb->c->stbase;
+  mrb_value *newstack;
+  size_t oldsize = mrb->c->stend - mrb->c->stbase;
+  size_t size = oldsize;
+  size_t off = mrb->c->stack - mrb->c->stbase;
 
+  if (off > size) size = off;
 #ifdef MRB_STACK_EXTEND_DOUBLING
   if (room <= size)
     size *= 2;
@@ -148,26 +183,29 @@ stack_extend_alloc(mrb_state *mrb, int room, int keep)
     size += room;
 #endif
 
-  mrb->c->stbase = (mrb_value *)mrb_realloc(mrb, mrb->c->stbase, sizeof(mrb_value) * size);
+  newstack = (mrb_value *)mrb_realloc(mrb, mrb->c->stbase, sizeof(mrb_value) * size);
+  if (newstack == NULL) {
+    mrb_exc_raise(mrb, mrb_obj_value(mrb->stack_err));
+  }
+  stack_clear(&(newstack[oldsize]), size - oldsize);
+  envadjust(mrb, oldbase, newstack, size);
+  mrb->c->stbase = newstack;
   mrb->c->stack = mrb->c->stbase + off;
   mrb->c->stend = mrb->c->stbase + size;
-  envadjust(mrb, oldbase, mrb->c->stbase);
 
   /* Raise an exception if the new stack size will be too large,
      to prevent infinite recursion. However, do this only after resizing the stack, so mrb_raise has stack space to work with. */
   if (size > MRB_STACK_MAX) {
-    init_new_stack_space(mrb, room, keep);
-    mrb_raise(mrb, E_SYSSTACK_ERROR, "stack level too deep. (limit=" MRB_STRINGIZE(MRB_STACK_MAX) ")");
+    mrb_exc_raise(mrb, mrb_obj_value(mrb->stack_err));
   }
 }
 
 static inline void
-stack_extend(mrb_state *mrb, int room, int keep)
+stack_extend(mrb_state *mrb, int room)
 {
   if (mrb->c->stack + room >= mrb->c->stend) {
-    stack_extend_alloc(mrb, room, keep);
+    stack_extend_alloc(mrb, room);
   }
-  init_new_stack_space(mrb, room, keep);
 }
 
 static inline struct REnv*
@@ -187,8 +225,8 @@ is_strict(mrb_state *mrb, struct REnv *e)
 {
   int cioff = e->cioff;
 
-  if (MRB_ENV_STACK_SHARED_P(e) && mrb->c->cibase[cioff].proc &&
-      MRB_PROC_STRICT_P(mrb->c->cibase[cioff].proc)) {
+  if (MRB_ENV_STACK_SHARED_P(e) && e->cxt.c->cibase[cioff].proc &&
+      MRB_PROC_STRICT_P(e->cxt.c->cibase[cioff].proc)) {
     return TRUE;
   }
   return FALSE;
@@ -209,14 +247,15 @@ top_env(mrb_state *mrb, struct RProc *proc)
 
 #define CI_ACC_SKIP    -1
 #define CI_ACC_DIRECT  -2
+#define CI_ACC_RESUMED -3
 
-static mrb_callinfo*
+static inline mrb_callinfo*
 cipush(mrb_state *mrb)
 {
   struct mrb_context *c = mrb->c;
+  static const mrb_callinfo ci_zero = { 0 };
   mrb_callinfo *ci = c->ci;
 
-  int eidx = ci->eidx;
   int ridx = ci->ridx;
 
   if (ci + 1 == c->ciend) {
@@ -227,50 +266,70 @@ cipush(mrb_state *mrb)
     c->ciend = c->cibase + size * 2;
   }
   ci = ++c->ci;
-  ci->eidx = eidx;
+  *ci = ci_zero;
+  ci->epos = mrb->c->eidx;
   ci->ridx = ridx;
-  ci->env = 0;
-  ci->pc = 0;
-  ci->err = 0;
-  ci->proc = 0;
 
   return ci;
 }
 
-static void
-cipop(mrb_state *mrb)
+MRB_API void
+mrb_env_unshare(mrb_state *mrb, struct REnv *e)
 {
-  struct mrb_context *c = mrb->c;
-
-  if (c->ci->env) {
-    struct REnv *e = c->ci->env;
+  if (e == NULL) return;
+  else {
     size_t len = (size_t)MRB_ENV_STACK_LEN(e);
-    mrb_value *p = (mrb_value *)mrb_malloc(mrb, sizeof(mrb_value)*len);
+    ptrdiff_t cioff = e->cioff;
+    mrb_value *p;
 
+    if (!MRB_ENV_STACK_SHARED_P(e)) return;
+    if (e->cxt.c != mrb->c) return;
+    if (e->cioff == 0 && e->cxt.c == mrb->root_c) return;
     MRB_ENV_UNSHARE_STACK(e);
+    if (!e->c) {
+      /* save block argument position (negated) */
+      e->cioff = -e->cxt.c->cibase[cioff].argc-1;
+    }
+    e->cxt.mid = e->cxt.c->cibase[cioff].mid;
+    p = (mrb_value *)mrb_malloc(mrb, sizeof(mrb_value)*len);
     if (len > 0) {
       stack_copy(p, e->stack, len);
     }
     e->stack = p;
     mrb_write_barrier(mrb, (struct RBasic *)e);
   }
+}
+
+static inline void
+cipop(mrb_state *mrb)
+{
+  struct mrb_context *c = mrb->c;
+  struct REnv *env = c->ci->env;
 
   c->ci--;
+  mrb_env_unshare(mrb, env);
 }
 
+void mrb_exc_set(mrb_state *mrb, mrb_value exc);
+
 static void
 ecall(mrb_state *mrb, int i)
 {
   struct RProc *p;
-  mrb_callinfo *ci;
+  mrb_callinfo *ci = mrb->c->ci;
   mrb_value *self = mrb->c->stack;
   struct RObject *exc;
+  ptrdiff_t cioff;
+  int ai = mrb_gc_arena_save(mrb);
 
   if (i<0) return;
+  if (ci - mrb->c->cibase > MRB_FUNCALL_DEPTH_MAX) {
+    mrb_exc_raise(mrb, mrb_obj_value(mrb->stack_err));
+  }
   p = mrb->c->ensure[i];
   if (!p) return;
-  if (mrb->c->ci->eidx > i)
-    mrb->c->ci->eidx = i;
+  mrb->c->ensure[i] = NULL;
+  cioff = ci - mrb->c->cibase;
   ci = cipush(mrb);
   ci->stackent = mrb->c->stack;
   ci->mid = ci[-1].mid;
@@ -281,9 +340,13 @@ ecall(mrb_state *mrb, int i)
   ci->target_class = p->target_class;
   mrb->c->stack = mrb->c->stack + ci[-1].nregs;
   exc = mrb->exc; mrb->exc = 0;
+  if (exc) {
+    mrb_gc_protect(mrb, mrb_obj_value(exc));
+  }
   mrb_run(mrb, p, *self);
-  mrb->c->ensure[i] = NULL;
+  mrb->c->ci = mrb->c->cibase + cioff;
   if (!mrb->exc) mrb->exc = exc;
+  mrb_gc_arena_restore(mrb, ai);
 }
 
 #ifndef MRB_FUNCALL_ARGC_MAX
@@ -334,11 +397,11 @@ mrb_funcall_with_block(mrb_state *mrb, mrb_value self, mrb_sym mid, mrb_int argc
       val = mrb_obj_value(mrb->exc);
     }
     MRB_END_EXC(&c_jmp);
+    mrb->jmp = 0;
   }
   else {
     struct RProc *p;
     struct RClass *c;
-    mrb_sym undef = 0;
     mrb_callinfo *ci;
     int n;
     ptrdiff_t voff = -1;
@@ -353,10 +416,19 @@ mrb_funcall_with_block(mrb_state *mrb, mrb_value self, mrb_sym mid, mrb_int argc
     c = mrb_class(mrb, self);
     p = mrb_method_search_vm(mrb, &c, mid);
     if (!p) {
-      undef = mid;
-      mid = mrb_intern_lit(mrb, "method_missing");
-      p = mrb_method_search_vm(mrb, &c, mid);
-      n++; argc++;
+      mrb_sym missing = mrb_intern_lit(mrb, "method_missing");
+      mrb_value args = mrb_ary_new_from_values(mrb, argc, argv);
+      p = mrb_method_search_vm(mrb, &c, missing);
+      if (!p) {
+        mrb_method_missing(mrb, mid, self, args);
+      }
+      mrb_ary_unshift(mrb, args, mrb_symbol_value(mid));
+      stack_extend(mrb, n+2);
+      mrb->c->stack[n+1] = args;
+      argc = -1;
+    }
+    if (mrb->c->ci - mrb->c->cibase > MRB_FUNCALL_DEPTH_MAX) {
+      mrb_exc_raise(mrb, mrb_obj_value(mrb->stack_err));
     }
     ci = cipush(mrb);
     ci->mid = mid;
@@ -370,23 +442,25 @@ mrb_funcall_with_block(mrb_state *mrb, mrb_value self, mrb_sym mid, mrb_int argc
     }
     if (MRB_PROC_CFUNC_P(p)) {
       ci->nregs = argc + 2;
-      stack_extend(mrb, ci->nregs, 0);
+      stack_extend(mrb, ci->nregs);
+    }
+    else if (argc >= CALL_MAXARGS) {
+      mrb_value args = mrb_ary_new_from_values(mrb, argc, argv);
+      stack_extend(mrb, ci->nregs);
+      mrb->c->stack[1] = args;
+      ci->argc = -1;
+      argc = 1;
     }
     else {
-      ci->nregs = p->body.irep->nregs + n;
-      stack_extend(mrb, ci->nregs, argc+2);
+      if (argc < 0) argc = 1;
+      ci->nregs = p->body.irep->nregs + argc;
+      stack_extend(mrb, ci->nregs);
     }
     if (voff >= 0) {
       argv = mrb->c->stbase + voff;
     }
     mrb->c->stack[0] = self;
-    if (undef) {
-      mrb->c->stack[1] = mrb_symbol_value(undef);
-      if (argc > 1) {
-        stack_copy(mrb->c->stack+2, argv, argc-1);
-      }
-    }
-    else if (argc > 0) {
+    if (ci->argc > 0) {
       stack_copy(mrb->c->stack+1, argv, argc);
     }
     mrb->c->stack[argc+1] = blk;
@@ -415,6 +489,35 @@ mrb_funcall_argv(mrb_state *mrb, mrb_value self, mrb_sym mid, mrb_int argc, cons
   return mrb_funcall_with_block(mrb, self, mid, argc, argv, mrb_nil_value());
 }
 
+mrb_value
+mrb_exec_irep(mrb_state *mrb, mrb_value self, struct RProc *p)
+{
+  mrb_callinfo *ci = mrb->c->ci;
+
+  mrb->c->stack[0] = self;
+  ci->proc = p;
+  ci->target_class = p->target_class;
+  if (MRB_PROC_CFUNC_P(p)) {
+    return p->body.func(mrb, self);
+  }
+  if (ci->argc < 0) {
+    stack_extend(mrb, (p->body.irep->nregs < 3) ? 3 : p->body.irep->nregs);
+  }
+  else {
+    stack_extend(mrb, p->body.irep->nregs);
+  }
+
+  ci->nregs = p->body.irep->nregs;
+  ci = cipush(mrb);
+  ci->nregs = 0;
+  ci->target_class = 0;
+  ci->pc = p->body.irep->iseq;
+  ci->stackent = mrb->c->stack;
+  ci->acc = 0;
+
+  return self;
+}
+
 /* 15.3.1.3.4  */
 /* 15.3.1.3.44 */
 /*
@@ -445,18 +548,21 @@ mrb_f_send(mrb_state *mrb, mrb_value self)
   mrb_callinfo *ci;
 
   mrb_get_args(mrb, "n*&", &name, &argv, &argc, &block);
+  ci = mrb->c->ci;
+  if (ci->acc < 0) {
+  funcall:
+    return mrb_funcall_with_block(mrb, self, name, argc, argv, block);
+  }
 
   c = mrb_class(mrb, self);
   p = mrb_method_search_vm(mrb, &c, name);
 
   if (!p) {                     /* call method_mising */
-    return mrb_funcall_with_block(mrb, self, name, argc, argv, block);
+    goto funcall;
   }
 
-  ci = mrb->c->ci;
   ci->mid = name;
   ci->target_class = c;
-  ci->proc = p;
   regs = mrb->c->stack+1;
   /* remove first symbol from arguments */
   if (ci->argc >= 0) {
@@ -469,26 +575,7 @@ mrb_f_send(mrb_state *mrb, mrb_value self)
     mrb_ary_shift(mrb, regs[0]);
   }
 
-  if (MRB_PROC_CFUNC_P(p)) {
-    return p->body.func(mrb, self);
-  }
-
-  if (ci->argc < 0) {
-    stack_extend(mrb, (p->body.irep->nregs < 3) ? 3 : p->body.irep->nregs, 3);
-  }
-  else {
-    stack_extend(mrb, p->body.irep->nregs, ci->argc+2);
-  }
-
-  ci->nregs = p->body.irep->nregs;
-  ci = cipush(mrb);
-  ci->nregs = 0;
-  ci->target_class = 0;
-  ci->pc = p->body.irep->iseq;
-  ci->stackent = mrb->c->stack;
-  ci->acc = 0;
-
-  return self;
+  return mrb_exec_irep(mrb, self, p);
 }
 
 static mrb_value
@@ -496,21 +583,34 @@ eval_under(mrb_state *mrb, mrb_value self, mrb_value blk, struct RClass *c)
 {
   struct RProc *p;
   mrb_callinfo *ci;
+  mrb_int max = 3;
 
   if (mrb_nil_p(blk)) {
     mrb_raise(mrb, E_ARGUMENT_ERROR, "no block given");
   }
   ci = mrb->c->ci;
   if (ci->acc == CI_ACC_DIRECT) {
-    return mrb_yield_with_class(mrb, blk, 0, 0, self, c);
+    ci->target_class = c;
+    return mrb_yield_cont(mrb, blk, self, 1, &self);
   }
   ci->target_class = c;
   p = mrb_proc_ptr(blk);
   ci->proc = p;
+  ci->argc = 1;
+  ci->mid = ci[-1].mid;
   if (MRB_PROC_CFUNC_P(p)) {
+    stack_extend(mrb, 3);
+    mrb->c->stack[0] = self;
+    mrb->c->stack[1] = self;
+    mrb->c->stack[2] = mrb_nil_value();
     return p->body.func(mrb, self);
   }
   ci->nregs = p->body.irep->nregs;
+  if (max < ci->nregs) max = ci->nregs;
+  stack_extend(mrb, max);
+  mrb->c->stack[0] = self;
+  mrb->c->stack[1] = self;
+  mrb->c->stack[2] = mrb_nil_value();
   ci = cipush(mrb);
   ci->nregs = 0;
   ci->target_class = 0;
@@ -599,6 +699,9 @@ mrb_yield_with_class(mrb_state *mrb, mrb_value b, mrb_int argc, const mrb_value
   if (mrb_nil_p(b)) {
     mrb_raise(mrb, E_ARGUMENT_ERROR, "no block given");
   }
+  if (mrb->c->ci - mrb->c->cibase > MRB_FUNCALL_DEPTH_MAX) {
+    mrb_exc_raise(mrb, mrb_obj_value(mrb->stack_err));
+  }
   p = mrb_proc_ptr(b);
   ci = cipush(mrb);
   ci->mid = mid;
@@ -610,11 +713,11 @@ mrb_yield_with_class(mrb_state *mrb, mrb_value b, mrb_int argc, const mrb_value
   mrb->c->stack = mrb->c->stack + n;
   if (MRB_PROC_CFUNC_P(p)) {
     ci->nregs = argc + 2;
-    stack_extend(mrb, ci->nregs, 0);
+    stack_extend(mrb, ci->nregs);
   }
   else {
     ci->nregs = p->body.irep->nregs;
-    stack_extend(mrb, ci->nregs, argc+2);
+    stack_extend(mrb, ci->nregs);
   }
 
   mrb->c->stack[0] = self;
@@ -626,11 +729,13 @@ mrb_yield_with_class(mrb_state *mrb, mrb_value b, mrb_int argc, const mrb_value
   if (MRB_PROC_CFUNC_P(p)) {
     val = p->body.func(mrb, self);
     mrb->c->stack = mrb->c->ci->stackent;
-    cipop(mrb);
   }
   else {
+    int cioff = mrb->c->ci - mrb->c->cibase;
     val = mrb_run(mrb, p, self);
+    mrb->c->ci = mrb->c->cibase + cioff;
   }
+  cipop(mrb);
   return val;
 }
 
@@ -650,6 +755,42 @@ mrb_yield(mrb_state *mrb, mrb_value b, mrb_value arg)
   return mrb_yield_with_class(mrb, b, 1, &arg, p->env->stack[0], p->target_class);
 }
 
+mrb_value
+mrb_yield_cont(mrb_state *mrb, mrb_value b, mrb_value self, mrb_int argc, const mrb_value *argv)
+{
+  struct RProc *p;
+  mrb_callinfo *ci;
+
+  if (mrb_nil_p(b)) {
+    mrb_raise(mrb, E_ARGUMENT_ERROR, "no block given");
+  }
+  if (mrb_type(b) != MRB_TT_PROC) {
+    mrb_raise(mrb, E_TYPE_ERROR, "not a block");
+  }
+
+  p = mrb_proc_ptr(b);
+  ci = mrb->c->ci;
+
+  stack_extend(mrb, 3);
+  mrb->c->stack[1] = mrb_ary_new_from_values(mrb, argc, argv);
+  mrb->c->stack[2] = mrb_nil_value();
+  ci->argc = -1;
+  return mrb_exec_irep(mrb, self, p);
+}
+
+static struct RBreak*
+break_new(mrb_state *mrb, struct RProc *p, mrb_value val)
+{
+  struct RBreak *brk;
+
+  brk = (struct RBreak*)mrb_obj_alloc(mrb, MRB_TT_BREAK, NULL);
+  brk->iv = NULL;
+  brk->proc = p;
+  brk->val = val;
+
+  return brk;
+}
+
 typedef enum {
   LOCALJUMP_ERROR_RETURN = 0,
   LOCALJUMP_ERROR_BREAK = 1,
@@ -669,7 +810,7 @@ localjump_error(mrb_state *mrb, localjump_error_kind kind)
   mrb_str_cat(mrb, msg, lead, sizeof(lead) - 1);
   mrb_str_cat(mrb, msg, kind_str[kind], kind_str_len[kind]);
   exc = mrb_exc_new_str(mrb, E_LOCALJUMP_ERROR, msg);
-  mrb->exc = mrb_obj_ptr(exc);
+  mrb_exc_set(mrb, exc);
 }
 
 static void
@@ -677,18 +818,25 @@ argnum_error(mrb_state *mrb, mrb_int num)
 {
   mrb_value exc;
   mrb_value str;
+  mrb_int argc = mrb->c->ci->argc;
 
+  if (argc < 0) {
+    mrb_value args = mrb->c->stack[1];
+    if (mrb_array_p(args)) {
+      argc = RARRAY_LEN(args);
+    }
+  }
   if (mrb->c->ci->mid) {
     str = mrb_format(mrb, "'%S': wrong number of arguments (%S for %S)",
                   mrb_sym2str(mrb, mrb->c->ci->mid),
-                  mrb_fixnum_value(mrb->c->ci->argc), mrb_fixnum_value(num));
+                  mrb_fixnum_value(argc), mrb_fixnum_value(num));
   }
   else {
     str = mrb_format(mrb, "wrong number of arguments (%S for %S)",
-                  mrb_fixnum_value(mrb->c->ci->argc), mrb_fixnum_value(num));
+                     mrb_fixnum_value(argc), mrb_fixnum_value(num));
   }
   exc = mrb_exc_new_str(mrb, E_ARGUMENT_ERROR, str);
-  mrb->exc = mrb_obj_ptr(exc);
+  mrb_exc_set(mrb, exc);
 }
 
 #define ERR_PC_SET(mrb, pc) mrb->c->ci->err = pc;
@@ -699,13 +847,20 @@ argnum_error(mrb_state *mrb, mrb_int num)
 #define CODE_FETCH_HOOK(mrb, irep, pc, regs)
 #endif
 
+#ifdef MRB_BYTECODE_DECODE_OPTION
+#define BYTECODE_DECODER(x) ((mrb)->bytecode_decoder)?(mrb)->bytecode_decoder((mrb), (x)):(x)
+#else
+#define BYTECODE_DECODER(x) (x)
+#endif
+
+
 #if defined __GNUC__ || defined __clang__ || defined __INTEL_COMPILER
 #define DIRECT_THREADED
 #endif
 
 #ifndef DIRECT_THREADED
 
-#define INIT_DISPATCH for (;;) { i = *pc; CODE_FETCH_HOOK(mrb, irep, pc, regs); switch (GET_OPCODE(i)) {
+#define INIT_DISPATCH for (;;) { i = BYTECODE_DECODER(*pc); CODE_FETCH_HOOK(mrb, irep, pc, regs); switch (GET_OPCODE(i)) {
 #define CASE(op) case op:
 #define NEXT pc++; break
 #define JUMP break
@@ -715,26 +870,50 @@ argnum_error(mrb_state *mrb, mrb_int num)
 
 #define INIT_DISPATCH JUMP; return mrb_nil_value();
 #define CASE(op) L_ ## op:
-#define NEXT i=*++pc; CODE_FETCH_HOOK(mrb, irep, pc, regs); goto *optable[GET_OPCODE(i)]
-#define JUMP i=*pc; CODE_FETCH_HOOK(mrb, irep, pc, regs); goto *optable[GET_OPCODE(i)]
+#define NEXT i=BYTECODE_DECODER(*++pc); CODE_FETCH_HOOK(mrb, irep, pc, regs); goto *optable[GET_OPCODE(i)]
+#define JUMP i=BYTECODE_DECODER(*pc); CODE_FETCH_HOOK(mrb, irep, pc, regs); goto *optable[GET_OPCODE(i)]
 
 #define END_DISPATCH
 
 #endif
 
-#define CALL_MAXARGS 127
+MRB_API mrb_value
+mrb_vm_run(mrb_state *mrb, struct RProc *proc, mrb_value self, unsigned int stack_keep)
+{
+  mrb_irep *irep = proc->body.irep;
+  mrb_value result;
+  struct mrb_context *c = mrb->c;
+  int cioff = c->ci - c->cibase;
+  unsigned int nregs = irep->nregs;
 
-void mrb_method_missing(mrb_state *mrb, mrb_sym name, mrb_value self, mrb_value args);
+  if (!c->stack) {
+    stack_init(mrb);
+  }
+  if (stack_keep > nregs)
+    nregs = stack_keep;
+  stack_extend(mrb, nregs);
+  stack_clear(c->stack + stack_keep, nregs - stack_keep);
+  c->stack[0] = self;
+  result = mrb_vm_exec(mrb, proc, irep->iseq);
+  if (c->ci - c->cibase > cioff) {
+    c->ci = c->cibase + cioff;
+  }
+  if (mrb->c != c) {
+    if (mrb->c->fib) {
+      mrb_write_barrier(mrb, (struct RBasic*)mrb->c->fib);
+    }
+    mrb->c = c;
+  }
+  return result;
+}
 
 MRB_API mrb_value
-mrb_context_run(mrb_state *mrb, struct RProc *proc, mrb_value self, unsigned int stack_keep)
+mrb_vm_exec(mrb_state *mrb, struct RProc *proc, mrb_code *pc)
 {
   /* mrb_assert(mrb_proc_cfunc_p(proc)) */
   mrb_irep *irep = proc->body.irep;
-  mrb_code *pc = irep->iseq;
   mrb_value *pool = irep->pool;
   mrb_sym *syms = irep->syms;
-  mrb_value *regs = NULL;
   mrb_code i;
   int ai = mrb_gc_arena_save(mrb);
   struct mrb_jmpbuf *prev_jmp = mrb->jmp;
@@ -772,18 +951,15 @@ RETRY_TRY_BLOCK:
 
   if (exc_catched) {
     exc_catched = FALSE;
+    if (mrb->exc && mrb->exc->tt == MRB_TT_BREAK)
+      goto L_BREAK;
     goto L_RAISE;
   }
   mrb->jmp = &c_jmp;
-  if (!mrb->c->stack) {
-    stack_init(mrb);
-  }
-  stack_extend(mrb, irep->nregs, stack_keep);
   mrb->c->ci->proc = proc;
   mrb->c->ci->nregs = irep->nregs;
-  regs = mrb->c->stack;
-  regs[0] = self;
 
+#define regs (mrb->c->stack)
   INIT_DISPATCH {
     CASE(OP_NOP) {
       /* do nothing */
@@ -792,13 +968,25 @@ RETRY_TRY_BLOCK:
 
     CASE(OP_MOVE) {
       /* A B    R(A) := R(B) */
-      regs[GETARG_A(i)] = regs[GETARG_B(i)];
+      int a = GETARG_A(i);
+      int b = GETARG_B(i);
+      regs[a] = regs[b];
       NEXT;
     }
 
     CASE(OP_LOADL) {
       /* A Bx   R(A) := Pool(Bx) */
-      regs[GETARG_A(i)] = pool[GETARG_Bx(i)];
+      int a = GETARG_A(i);
+      int bx = GETARG_Bx(i);
+#ifdef MRB_WORD_BOXING
+      mrb_value val = pool[bx];
+      if (mrb_float_p(val)) {
+        val = mrb_float_value(mrb, mrb_float(val));
+      }
+      regs[a] = val;
+#else
+      regs[a] = pool[bx];
+#endif
       NEXT;
     }
 
@@ -810,93 +998,123 @@ RETRY_TRY_BLOCK:
 
     CASE(OP_LOADSYM) {
       /* A Bx   R(A) := Syms(Bx) */
-      SET_SYM_VALUE(regs[GETARG_A(i)], syms[GETARG_Bx(i)]);
+      int a = GETARG_A(i);
+      int bx = GETARG_Bx(i);
+      SET_SYM_VALUE(regs[a], syms[bx]);
       NEXT;
     }
 
     CASE(OP_LOADSELF) {
       /* A      R(A) := self */
-      regs[GETARG_A(i)] = regs[0];
+      int a = GETARG_A(i);
+      regs[a] = regs[0];
       NEXT;
     }
 
     CASE(OP_LOADT) {
       /* A      R(A) := true */
-      SET_TRUE_VALUE(regs[GETARG_A(i)]);
+      int a = GETARG_A(i);
+      SET_TRUE_VALUE(regs[a]);
       NEXT;
     }
 
     CASE(OP_LOADF) {
       /* A      R(A) := false */
-      SET_FALSE_VALUE(regs[GETARG_A(i)]);
+      int a = GETARG_A(i);
+      SET_FALSE_VALUE(regs[a]);
       NEXT;
     }
 
     CASE(OP_GETGLOBAL) {
       /* A Bx   R(A) := getglobal(Syms(Bx)) */
-      regs[GETARG_A(i)] = mrb_gv_get(mrb, syms[GETARG_Bx(i)]);
+      int a = GETARG_A(i);
+      int bx = GETARG_Bx(i);
+      mrb_value val = mrb_gv_get(mrb, syms[bx]);
+      regs[a] = val;
       NEXT;
     }
 
     CASE(OP_SETGLOBAL) {
-      /* setglobal(Syms(Bx), R(A)) */
-      mrb_gv_set(mrb, syms[GETARG_Bx(i)], regs[GETARG_A(i)]);
+      /* A Bx   setglobal(Syms(Bx), R(A)) */
+      int a = GETARG_A(i);
+      int bx = GETARG_Bx(i);
+      mrb_gv_set(mrb, syms[bx], regs[a]);
       NEXT;
     }
 
     CASE(OP_GETSPECIAL) {
       /* A Bx   R(A) := Special[Bx] */
-      regs[GETARG_A(i)] = mrb_vm_special_get(mrb, GETARG_Bx(i));
+      int a = GETARG_A(i);
+      int bx = GETARG_Bx(i);
+      mrb_value val = mrb_vm_special_get(mrb, bx);
+      regs[a] = val;
       NEXT;
     }
 
     CASE(OP_SETSPECIAL) {
       /* A Bx   Special[Bx] := R(A) */
-      mrb_vm_special_set(mrb, GETARG_Bx(i), regs[GETARG_A(i)]);
+      int a = GETARG_A(i);
+      int bx = GETARG_Bx(i);
+      mrb_vm_special_set(mrb, bx, regs[a]);
       NEXT;
     }
 
     CASE(OP_GETIV) {
       /* A Bx   R(A) := ivget(Bx) */
-      regs[GETARG_A(i)] = mrb_vm_iv_get(mrb, syms[GETARG_Bx(i)]);
+      int a = GETARG_A(i);
+      int bx = GETARG_Bx(i);
+      mrb_value val = mrb_vm_iv_get(mrb, syms[bx]);
+      regs[a] = val;
       NEXT;
     }
 
     CASE(OP_SETIV) {
-      /* ivset(Syms(Bx),R(A)) */
-      mrb_vm_iv_set(mrb, syms[GETARG_Bx(i)], regs[GETARG_A(i)]);
+      /* A Bx   ivset(Syms(Bx),R(A)) */
+      int a = GETARG_A(i);
+      int bx = GETARG_Bx(i);
+      mrb_vm_iv_set(mrb, syms[bx], regs[a]);
       NEXT;
     }
 
     CASE(OP_GETCV) {
       /* A Bx   R(A) := cvget(Syms(Bx)) */
+      int a = GETARG_A(i);
+      int bx = GETARG_Bx(i);
+      mrb_value val;
       ERR_PC_SET(mrb, pc);
-      regs[GETARG_A(i)] = mrb_vm_cv_get(mrb, syms[GETARG_Bx(i)]);
+      val = mrb_vm_cv_get(mrb, syms[bx]);
       ERR_PC_CLR(mrb);
+      regs[a] = val;
       NEXT;
     }
 
     CASE(OP_SETCV) {
-      /* cvset(Syms(Bx),R(A)) */
-      mrb_vm_cv_set(mrb, syms[GETARG_Bx(i)], regs[GETARG_A(i)]);
+      /* A Bx   cvset(Syms(Bx),R(A)) */
+      int a = GETARG_A(i);
+      int bx = GETARG_Bx(i);
+      mrb_vm_cv_set(mrb, syms[bx], regs[a]);
       NEXT;
     }
 
     CASE(OP_GETCONST) {
       /* A Bx    R(A) := constget(Syms(Bx)) */
       mrb_value val;
+      int a = GETARG_A(i);
+      int bx = GETARG_Bx(i);
+      mrb_sym sym = syms[bx];
 
       ERR_PC_SET(mrb, pc);
-      val = mrb_vm_const_get(mrb, syms[GETARG_Bx(i)]);
+      val = mrb_vm_const_get(mrb, sym);
       ERR_PC_CLR(mrb);
-      regs = mrb->c->stack;
-      regs[GETARG_A(i)] = val;
+      regs[a] = val;
       NEXT;
     }
 
     CASE(OP_SETCONST) {
       /* A Bx   constset(Syms(Bx),R(A)) */
-      mrb_vm_const_set(mrb, syms[GETARG_Bx(i)], regs[GETARG_A(i)]);
+      int a = GETARG_A(i);
+      int bx = GETARG_Bx(i);
+      mrb_vm_const_set(mrb, syms[bx], regs[a]);
       NEXT;
     }
 
@@ -904,11 +1122,11 @@ RETRY_TRY_BLOCK:
       /* A Bx   R(A) := R(A)::Syms(Bx) */
       mrb_value val;
       int a = GETARG_A(i);
+      int bx = GETARG_Bx(i);
 
       ERR_PC_SET(mrb, pc);
-      val = mrb_const_get(mrb, regs[a], syms[GETARG_Bx(i)]);
+      val = mrb_const_get(mrb, regs[a], syms[bx]);
       ERR_PC_CLR(mrb);
-      regs = mrb->c->stack;
       regs[a] = val;
       NEXT;
     }
@@ -916,53 +1134,60 @@ RETRY_TRY_BLOCK:
     CASE(OP_SETMCNST) {
       /* A Bx    R(A+1)::Syms(Bx) := R(A) */
       int a = GETARG_A(i);
-
-      mrb_const_set(mrb, regs[a+1], syms[GETARG_Bx(i)], regs[a]);
+      int bx = GETARG_Bx(i);
+      mrb_const_set(mrb, regs[a+1], syms[bx], regs[a]);
       NEXT;
     }
 
     CASE(OP_GETUPVAR) {
       /* A B C  R(A) := uvget(B,C) */
-      mrb_value *regs_a = regs + GETARG_A(i);
-      int up = GETARG_C(i);
-
-      struct REnv *e = uvenv(mrb, up);
+      int a = GETARG_A(i);
+      int b = GETARG_B(i);
+      int c = GETARG_C(i);
+      mrb_value *regs_a = regs + a;
+      struct REnv *e = uvenv(mrb, c);
 
       if (!e) {
         *regs_a = mrb_nil_value();
       }
       else {
-        int idx = GETARG_B(i);
-        *regs_a = e->stack[idx];
+        *regs_a = e->stack[b];
       }
       NEXT;
     }
 
     CASE(OP_SETUPVAR) {
       /* A B C  uvset(B,C,R(A)) */
-      int up = GETARG_C(i);
+      int a = GETARG_A(i);
+      int b = GETARG_B(i);
+      int c = GETARG_C(i);
 
-      struct REnv *e = uvenv(mrb, up);
+      struct REnv *e = uvenv(mrb, c);
 
       if (e) {
-        mrb_value *regs_a = regs + GETARG_A(i);
-        int idx = GETARG_B(i);
-        e->stack[idx] = *regs_a;
-        mrb_write_barrier(mrb, (struct RBasic*)e);
+        mrb_value *regs_a = regs + a;
+
+        if (b < MRB_ENV_STACK_LEN(e)) {
+          e->stack[b] = *regs_a;
+          mrb_write_barrier(mrb, (struct RBasic*)e);
+        }
       }
       NEXT;
     }
 
     CASE(OP_JMP) {
       /* sBx    pc+=sBx */
-      pc += GETARG_sBx(i);
+      int sbx = GETARG_sBx(i);
+      pc += sbx;
       JUMP;
     }
 
     CASE(OP_JMPIF) {
       /* A sBx  if R(A) pc+=sBx */
-      if (mrb_test(regs[GETARG_A(i)])) {
-        pc += GETARG_sBx(i);
+      int a = GETARG_A(i);
+      int sbx = GETARG_sBx(i);
+      if (mrb_test(regs[a])) {
+        pc += sbx;
         JUMP;
       }
       NEXT;
@@ -970,8 +1195,10 @@ RETRY_TRY_BLOCK:
 
     CASE(OP_JMPNOT) {
       /* A sBx  if !R(A) pc+=sBx */
-      if (!mrb_test(regs[GETARG_A(i)])) {
-        pc += GETARG_sBx(i);
+      int a = GETARG_A(i);
+      int sbx = GETARG_sBx(i);
+      if (!mrb_test(regs[a])) {
+        pc += sbx;
         JUMP;
       }
       NEXT;
@@ -979,19 +1206,54 @@ RETRY_TRY_BLOCK:
 
     CASE(OP_ONERR) {
       /* sBx    pc+=sBx on exception */
+      int sbx = GETARG_sBx(i);
       if (mrb->c->rsize <= mrb->c->ci->ridx) {
-        if (mrb->c->rsize == 0) mrb->c->rsize = 16;
+        if (mrb->c->rsize == 0) mrb->c->rsize = RESCUE_STACK_INIT_SIZE;
         else mrb->c->rsize *= 2;
         mrb->c->rescue = (mrb_code **)mrb_realloc(mrb, mrb->c->rescue, sizeof(mrb_code*) * mrb->c->rsize);
       }
-      mrb->c->rescue[mrb->c->ci->ridx++] = pc + GETARG_sBx(i);
+      mrb->c->rescue[mrb->c->ci->ridx++] = pc + sbx;
       NEXT;
     }
 
     CASE(OP_RESCUE) {
-      /* A      R(A) := exc; clear(exc) */
-      SET_OBJ_VALUE(regs[GETARG_A(i)], mrb->exc);
-      mrb->exc = 0;
+      /* A B    R(A) := exc; clear(exc); R(B) := matched (bool) */
+      int a = GETARG_A(i);
+      int b = GETARG_B(i);
+      int c = GETARG_C(i);
+      mrb_value exc;
+
+      if (c == 0) {
+        exc = mrb_obj_value(mrb->exc);
+        mrb->exc = 0;
+      }
+      else {           /* continued; exc taken from R(A) */
+        exc = regs[a];
+      }
+      if (b != 0) {
+        mrb_value e = regs[b];
+        struct RClass *ec;
+
+        switch (mrb_type(e)) {
+        case MRB_TT_CLASS:
+        case MRB_TT_MODULE:
+          break;
+        default:
+          {
+            mrb_value exc;
+
+            exc = mrb_exc_new_str_lit(mrb, E_TYPE_ERROR,
+                  "class or module required for rescue clause");
+            mrb_exc_set(mrb, exc);
+            goto L_RAISE;
+          }
+        }
+        ec = mrb_class_ptr(e);
+        regs[b] = mrb_bool_value(mrb_obj_is_kind_of(mrb, exc, ec));
+      }
+      if (a != 0 && c == 0) {
+        regs[a] = exc;
+      }
       NEXT;
     }
 
@@ -999,31 +1261,33 @@ RETRY_TRY_BLOCK:
       /* A      A.times{rescue_pop()} */
       int a = GETARG_A(i);
 
-      while (a--) {
-        mrb->c->ci->ridx--;
-      }
+      mrb->c->ci->ridx -= a;
       NEXT;
     }
 
     CASE(OP_RAISE) {
       /* A      raise(R(A)) */
-      mrb->exc = mrb_obj_ptr(regs[GETARG_A(i)]);
+      int a = GETARG_A(i);
+
+      mrb_exc_set(mrb, regs[a]);
       goto L_RAISE;
     }
 
     CASE(OP_EPUSH) {
       /* Bx     ensure_push(SEQ[Bx]) */
+      int bx = GETARG_Bx(i);
       struct RProc *p;
 
-      p = mrb_closure_new(mrb, irep->reps[GETARG_Bx(i)]);
+      p = mrb_closure_new(mrb, irep->reps[bx]);
       /* push ensure_stack */
-      if (mrb->c->esize <= mrb->c->ci->eidx) {
-        if (mrb->c->esize == 0) mrb->c->esize = 16;
+      if (mrb->c->esize <= mrb->c->eidx+1) {
+        if (mrb->c->esize == 0) mrb->c->esize = ENSURE_STACK_INIT_SIZE;
         else mrb->c->esize *= 2;
         mrb->c->ensure = (struct RProc **)mrb_realloc(mrb, mrb->c->ensure, sizeof(struct RProc*) * mrb->c->esize);
       }
-      mrb->c->ensure[mrb->c->ci->eidx++] = p;
-      ARENA_RESTORE(mrb, ai);
+      mrb->c->ensure[mrb->c->eidx++] = p;
+      mrb->c->ensure[mrb->c->eidx] = NULL;
+      mrb_gc_arena_restore(mrb, ai);
       NEXT;
     }
 
@@ -1031,11 +1295,11 @@ RETRY_TRY_BLOCK:
       /* A      A.times{ensure_pop().call} */
       int a = GETARG_A(i);
       mrb_callinfo *ci = mrb->c->ci;
-      int n, eidx = ci->eidx;
+      int n, epos = ci->epos;
 
-      for (n=0; n<a && (ci == mrb->c->cibase || eidx > ci[-1].eidx); n++) {
-        ecall(mrb, --eidx);
-        ARENA_RESTORE(mrb, ai);
+      for (n=0; n<a && mrb->c->eidx > epos; n++) {
+        ecall(mrb, --mrb->c->eidx);
+        mrb_gc_arena_restore(mrb, ai);
       }
       NEXT;
     }
@@ -1060,17 +1324,36 @@ RETRY_TRY_BLOCK:
       int n = GETARG_C(i);
       struct RProc *m;
       struct RClass *c;
-      mrb_callinfo *ci;
+      mrb_callinfo *ci = mrb->c->ci;
       mrb_value recv, result;
       mrb_sym mid = syms[GETARG_B(i)];
+      int bidx;
+      mrb_value blk;
 
       recv = regs[a];
+      if (n == CALL_MAXARGS) {
+        bidx = a+2;
+      }
+      else {
+        bidx = a+n+1;
+      }
       if (GET_OPCODE(i) != OP_SENDB) {
-        if (n == CALL_MAXARGS) {
-          SET_NIL_VALUE(regs[a+2]);
+        if (bidx >= ci->nregs) {
+          stack_extend(mrb, bidx+1);
+          ci->nregs = bidx+1;
         }
-        else {
-          SET_NIL_VALUE(regs[a+n+1]);
+        SET_NIL_VALUE(regs[bidx]);
+        blk = mrb_nil_value();
+      }
+      else {
+        blk = regs[bidx];
+        if (!mrb_nil_p(blk) && mrb_type(blk) != MRB_TT_PROC) {
+          if (bidx >= ci->nregs) {
+            stack_extend(mrb, bidx+1);
+            ci->nregs = bidx+1;
+          }
+          result = mrb_convert_type(mrb, blk, MRB_TT_PROC, "Proc", "to_proc");
+          blk = regs[bidx] = result;
         }
       }
       c = mrb_class(mrb, recv);
@@ -1089,16 +1372,19 @@ RETRY_TRY_BLOCK:
           else {
             args = mrb_ary_new_from_values(mrb, n, regs+a+1);
           }
+          ERR_PC_SET(mrb, pc);
           mrb_method_missing(mrb, mid, recv, args);
         }
         mid = missing;
-        if (n == CALL_MAXARGS) {
-          mrb_ary_unshift(mrb, regs[a+1], sym);
-        }
-        else {
-          value_move(regs+a+2, regs+a+1, ++n);
-          regs[a+1] = sym;
+        if (n != CALL_MAXARGS) {
+          if (a+2 >= irep->nregs) {
+            stack_extend(mrb, a+3);
+          }
+          regs[a+1] = mrb_ary_new_from_values(mrb, n, regs+a+1);
+          regs[a+2] = blk;
+          n = CALL_MAXARGS;
         }
+        mrb_ary_unshift(mrb, regs[a+1], sym);
       }
 
       /* push callinfo */
@@ -1124,20 +1410,35 @@ RETRY_TRY_BLOCK:
           ci->nregs = n + 2;
         }
         result = m->body.func(mrb, recv);
-        mrb->c->stack[0] = result;
         mrb_gc_arena_restore(mrb, ai);
+        mrb_gc_arena_shrink(mrb, ai);
         if (mrb->exc) goto L_RAISE;
-        /* pop stackpos */
         ci = mrb->c->ci;
+        if (GET_OPCODE(i) == OP_SENDB) {
+          if (mrb_type(blk) == MRB_TT_PROC) {
+            struct RProc *p = mrb_proc_ptr(blk);
+
+            if (p && !MRB_PROC_STRICT_P(p) && p->env == ci[-1].env) {
+              p->flags |= MRB_PROC_ORPHAN;
+            }
+          }
+        }
         if (!ci->target_class) { /* return from context modifying method (resume/yield) */
-          if (!MRB_PROC_CFUNC_P(ci[-1].proc)) {
+          if (ci->acc == CI_ACC_RESUMED) {
+            mrb->jmp = prev_jmp;
+            return result;
+          }
+          else {
+            mrb_assert(!MRB_PROC_CFUNC_P(ci[-1].proc));
             proc = ci[-1].proc;
             irep = proc->body.irep;
             pool = irep->pool;
             syms = irep->syms;
           }
         }
-        regs = mrb->c->stack = ci->stackent;
+        mrb->c->stack[0] = result;
+        /* pop stackpos */
+        mrb->c->stack = ci->stackent;
         pc = ci->pc;
         cipop(mrb);
         JUMP;
@@ -1151,13 +1452,12 @@ RETRY_TRY_BLOCK:
         ci->nregs = irep->nregs;
         if (n == CALL_MAXARGS) {
           ci->argc = -1;
-          stack_extend(mrb, (irep->nregs < 3) ? 3 : irep->nregs, 3);
+          stack_extend(mrb, (irep->nregs < 3) ? 3 : irep->nregs);
         }
         else {
           ci->argc = n;
-          stack_extend(mrb, irep->nregs,  n+2);
+          stack_extend(mrb, irep->nregs);
         }
-        regs = mrb->c->stack;
         pc = irep->iseq;
         JUMP;
       }
@@ -1165,6 +1465,7 @@ RETRY_TRY_BLOCK:
 
     CASE(OP_FSEND) {
       /* A B C  R(A) := fcall(R(A),Syms(B),R(A+1),... ,R(A+C-1)) */
+      /* not implemented yet */
       NEXT;
     }
 
@@ -1179,9 +1480,15 @@ RETRY_TRY_BLOCK:
       ci->target_class = m->target_class;
       ci->proc = m;
       if (m->env) {
-        if (m->env->mid) {
-          ci->mid = m->env->mid;
+        mrb_sym mid;
+
+        if (MRB_ENV_STACK_SHARED_P(m->env)) {
+          mid = m->env->cxt.c->cibase[m->env->cioff].mid;
+        }
+        else {
+          mid = m->env->cxt.mid;
         }
+        if (mid) ci->mid = mid;
         if (!m->env->stack) {
           m->env->stack = mrb->c->stack;
         }
@@ -1191,10 +1498,11 @@ RETRY_TRY_BLOCK:
       if (MRB_PROC_CFUNC_P(m)) {
         recv = m->body.func(mrb, recv);
         mrb_gc_arena_restore(mrb, ai);
+        mrb_gc_arena_shrink(mrb, ai);
         if (mrb->exc) goto L_RAISE;
         /* pop stackpos */
         ci = mrb->c->ci;
-        regs = mrb->c->stack = ci->stackent;
+        mrb->c->stack = ci->stackent;
         regs[ci->acc] = recv;
         pc = ci->pc;
         cipop(mrb);
@@ -1214,14 +1522,18 @@ RETRY_TRY_BLOCK:
         pool = irep->pool;
         syms = irep->syms;
         ci->nregs = irep->nregs;
+        stack_extend(mrb, irep->nregs);
         if (ci->argc < 0) {
-          stack_extend(mrb, (irep->nregs < 3) ? 3 : irep->nregs, 3);
+          if (irep->nregs > 3) {
+            stack_clear(regs+3, irep->nregs-3);
+          }
         }
-        else {
-          stack_extend(mrb, irep->nregs, ci->argc+2);
+        else if (ci->argc+2 < irep->nregs) {
+          stack_clear(regs+ci->argc+2, irep->nregs-ci->argc-2);
+        }
+        if (m->env) {
+          regs[0] = m->env->stack[0];
         }
-        regs = mrb->c->stack;
-        regs[0] = m->env->stack[0];
         pc = irep->iseq;
         JUMP;
       }
@@ -1236,20 +1548,39 @@ RETRY_TRY_BLOCK:
       mrb_sym mid = ci->mid;
       int a = GETARG_A(i);
       int n = GETARG_C(i);
+      mrb_value blk;
+      int bidx;
 
-      if (mid == 0) {
+      if (mid == 0 || !ci->target_class) {
         mrb_value exc;
 
         exc = mrb_exc_new_str_lit(mrb, E_NOMETHOD_ERROR, "super called outside of method");
-        mrb->exc = mrb_obj_ptr(exc);
+        mrb_exc_set(mrb, exc);
         goto L_RAISE;
       }
       recv = regs[0];
       c = mrb->c->ci->target_class->super;
       m = mrb_method_search_vm(mrb, &c, mid);
       if (!m) {
-        mid = mrb_intern_lit(mrb, "method_missing");
-        m = mrb_method_search_vm(mrb, &c, mid);
+        mrb_sym missing = mrb_intern_lit(mrb, "method_missing");
+        m = mrb_method_search_vm(mrb, &c, missing);
+        if (!m) {
+          mrb_value args;
+
+          if (n == CALL_MAXARGS) {
+            args = regs[a+1];
+          }
+          else {
+            args = mrb_ary_new_from_values(mrb, n, regs+a+1);
+          }
+          ERR_PC_SET(mrb, pc);
+          mrb_method_missing(mrb, mid, recv, args);
+        }
+        mid = missing;
+        if (n == CALL_MAXARGS-1) {
+          regs[a+1] = mrb_ary_new_from_values(mrb, n, regs+a+1);
+          n++;
+        }
         if (n == CALL_MAXARGS) {
           mrb_ary_unshift(mrb, regs[a+1], mrb_symbol_value(ci->mid));
         }
@@ -1259,38 +1590,74 @@ RETRY_TRY_BLOCK:
         }
       }
 
+      if (n == CALL_MAXARGS) {
+        bidx = a+2;
+      }
+      else {
+        bidx = a+n+1;
+      }
+      blk = regs[bidx];
+      if (!mrb_nil_p(blk) && mrb_type(blk) != MRB_TT_PROC) {
+        mrb_value result;
+
+        if (bidx >= ci->nregs) {
+          stack_extend(mrb, bidx+1);
+          ci->nregs = bidx+1;
+        }
+        result = mrb_convert_type(mrb, blk, MRB_TT_PROC, "Proc", "to_proc");
+        regs[bidx] = result;
+      }
+
       /* push callinfo */
       ci = cipush(mrb);
       ci->mid = mid;
       ci->proc = m;
       ci->stackent = mrb->c->stack;
+      ci->target_class = c;
+      ci->pc = pc + 1;
       if (n == CALL_MAXARGS) {
         ci->argc = -1;
       }
       else {
         ci->argc = n;
       }
-      ci->target_class = c;
-      ci->pc = pc + 1;
 
       /* prepare stack */
       mrb->c->stack += a;
       mrb->c->stack[0] = recv;
 
       if (MRB_PROC_CFUNC_P(m)) {
+        mrb_value v;
+
         if (n == CALL_MAXARGS) {
           ci->nregs = 3;
         }
         else {
           ci->nregs = n + 2;
         }
-        mrb->c->stack[0] = m->body.func(mrb, recv);
+        v = m->body.func(mrb, recv);
         mrb_gc_arena_restore(mrb, ai);
         if (mrb->exc) goto L_RAISE;
+        ci = mrb->c->ci;
+        if (!ci->target_class) { /* return from context modifying method (resume/yield) */
+          if (ci->acc == CI_ACC_RESUMED) {
+            mrb->jmp = prev_jmp;
+            return v;
+          }
+          else {
+            mrb_assert(!MRB_PROC_CFUNC_P(ci[-1].proc));
+            proc = ci[-1].proc;
+            irep = proc->body.irep;
+            pool = irep->pool;
+            syms = irep->syms;
+          }
+        }
+        mrb->c->stack[0] = v;
         /* pop stackpos */
-        regs = mrb->c->stack = mrb->c->ci->stackent;
+        mrb->c->stack = ci->stackent;
+        pc = ci->pc;
         cipop(mrb);
-        NEXT;
+        JUMP;
       }
       else {
         /* fill callinfo */
@@ -1303,12 +1670,11 @@ RETRY_TRY_BLOCK:
         syms = irep->syms;
         ci->nregs = irep->nregs;
         if (n == CALL_MAXARGS) {
-          stack_extend(mrb, (irep->nregs < 3) ? 3 : irep->nregs, 3);
+          stack_extend(mrb, (irep->nregs < 3) ? 3 : irep->nregs);
         }
         else {
-          stack_extend(mrb, irep->nregs, ci->argc+2);
+          stack_extend(mrb, irep->nregs);
         }
-        regs = mrb->c->stack;
         pc = irep->iseq;
         JUMP;
       }
@@ -1324,16 +1690,20 @@ RETRY_TRY_BLOCK:
       int lv = (bx>>0)&0xf;
       mrb_value *stack;
 
+      if (mrb->c->ci->mid == 0 || mrb->c->ci->target_class == NULL) {
+        mrb_value exc;
+
+      L_NOSUPER:
+        exc = mrb_exc_new_str_lit(mrb, E_NOMETHOD_ERROR, "super called outside of method");
+        mrb_exc_set(mrb, exc);
+        goto L_RAISE;
+      }
       if (lv == 0) stack = regs + 1;
       else {
         struct REnv *e = uvenv(mrb, lv-1);
-        if (!e) {
-          mrb_value exc;
-
-          exc = mrb_exc_new_str_lit(mrb, E_NOMETHOD_ERROR, "super called outside of method");
-          mrb->exc = mrb_obj_ptr(exc);
-          goto L_RAISE;
-        }
+        if (!e) goto L_NOSUPER;
+        if (MRB_ENV_STACK_LEN(e) <= m1+r+m2+1)
+          goto L_NOSUPER;
         stack = e->stack + 1;
       }
       if (r == 0) {
@@ -1347,24 +1717,24 @@ RETRY_TRY_BLOCK:
         if (mrb_array_p(stack[m1])) {
           struct RArray *ary = mrb_ary_ptr(stack[m1]);
 
-          pp = ary->ptr;
-          len = ary->len;
+          pp = ARY_PTR(ary);
+          len = ARY_LEN(ary);
         }
         regs[a] = mrb_ary_new_capa(mrb, m1+len+m2);
         rest = mrb_ary_ptr(regs[a]);
         if (m1 > 0) {
-          stack_copy(rest->ptr, stack, m1);
+          stack_copy(ARY_PTR(rest), stack, m1);
         }
         if (len > 0) {
-          stack_copy(rest->ptr+m1, pp, len);
+          stack_copy(ARY_PTR(rest)+m1, pp, len);
         }
         if (m2 > 0) {
-          stack_copy(rest->ptr+m1+len, stack+m1+1, m2);
+          stack_copy(ARY_PTR(rest)+m1+len, stack+m1+1, m2);
         }
-        rest->len = m1+len+m2;
+        ARY_SET_LEN(rest, m1+len+m2);
       }
       regs[a+1] = stack[m1+r+m2];
-      ARENA_RESTORE(mrb, ai);
+      mrb_gc_arena_restore(mrb, ai);
       NEXT;
     }
 
@@ -1387,13 +1757,10 @@ RETRY_TRY_BLOCK:
       int len = m1 + o + r + m2;
       mrb_value *blk = &argv[argc < 0 ? 1 : argc];
 
-      if (!mrb_nil_p(*blk) && mrb_type(*blk) != MRB_TT_PROC) {
-        *blk = mrb_convert_type(mrb, *blk, MRB_TT_PROC, "Proc", "to_proc");
-      }
       if (argc < 0) {
         struct RArray *ary = mrb_ary_ptr(regs[1]);
-        argv = ary->ptr;
-        argc = ary->len;
+        argv = ARY_PTR(ary);
+        argc = ARY_LEN(ary);
         mrb_gc_protect(mrb, regs[1]);
       }
       if (mrb->c->ci->proc && MRB_PROC_STRICT_P(mrb->c->ci->proc)) {
@@ -1406,10 +1773,9 @@ RETRY_TRY_BLOCK:
       }
       else if (len > 1 && argc == 1 && mrb_array_p(argv[0])) {
         mrb_gc_protect(mrb, argv[0]);
-        argc = mrb_ary_ptr(argv[0])->len;
-        argv = mrb_ary_ptr(argv[0])->ptr;
+        argc = RARRAY_LEN(argv[0]);
+        argv = RARRAY_PTR(argv[0]);
       }
-      mrb->c->ci->argc = len;
       if (argc < len) {
         int mlen = m2;
         if (argc < m1+m2) {
@@ -1423,9 +1789,15 @@ RETRY_TRY_BLOCK:
         if (argv0 != argv) {
           value_move(&regs[1], argv, argc-mlen); /* m1 + o */
         }
+        if (argc < m1) {
+          stack_clear(&regs[argc+1], m1-argc);
+        }
         if (mlen) {
           value_move(&regs[len-m2+1], &argv[argc-mlen], mlen);
         }
+        if (mlen < m2) {
+          stack_clear(&regs[len-m2+mlen+1], m2-mlen);
+        }
         if (r) {
           regs[m1+o+1] = mrb_ary_new_capa(mrb, 0);
         }
@@ -1453,6 +1825,11 @@ RETRY_TRY_BLOCK:
         }
         pc += o + 1;
       }
+      mrb->c->ci->argc = len;
+      /* clear local (but non-argument) variables */
+      if (irep->nlocals-len-2 > 0) {
+        stack_clear(&regs[len+2], irep->nlocals-len-2);
+      }
       JUMP;
     }
 
@@ -1473,39 +1850,61 @@ RETRY_TRY_BLOCK:
       /* fall through */
     CASE(OP_RETURN) {
       /* A B     return R(A) (B=normal,in-block return/break) */
+      mrb_callinfo *ci;
+
+      ci = mrb->c->ci;
+      if (ci->mid) {
+        mrb_value blk;
+
+        if (ci->argc < 0) {
+          blk = regs[2];
+        }
+        else {
+          blk = regs[ci->argc+1];
+        }
+        if (mrb_type(blk) == MRB_TT_PROC) {
+          struct RProc *p = mrb_proc_ptr(blk);
+
+          if (!MRB_PROC_STRICT_P(proc) &&
+              ci > mrb->c->cibase && p->env == ci[-1].env) {
+            p->flags |= MRB_PROC_ORPHAN;
+          }
+        }
+      }
+
       if (mrb->exc) {
-        mrb_callinfo *ci;
-        int eidx;
+        mrb_callinfo *ci0;
+        mrb_value *stk;
 
       L_RAISE:
-        ci = mrb->c->ci;
-        mrb_obj_iv_ifnone(mrb, mrb->exc, mrb_intern_lit(mrb, "lastpc"), mrb_cptr_value(mrb, pc));
-        mrb_obj_iv_ifnone(mrb, mrb->exc, mrb_intern_lit(mrb, "ciidx"), mrb_fixnum_value(ci - mrb->c->cibase));
-        eidx = ci->eidx;
+        ci0 = ci = mrb->c->ci;
         if (ci == mrb->c->cibase) {
-          if (ci->ridx == 0) goto L_STOP;
+          if (ci->ridx == 0) goto L_FTOP;
           goto L_RESCUE;
         }
+        stk = mrb->c->stack;
         while (ci[0].ridx == ci[-1].ridx) {
           cipop(mrb);
-          ci = mrb->c->ci;
-          mrb->c->stack = ci[1].stackent;
-          if (ci[1].acc == CI_ACC_SKIP && prev_jmp) {
+          mrb->c->stack = ci->stackent;
+          if (ci->acc == CI_ACC_SKIP && prev_jmp) {
             mrb->jmp = prev_jmp;
             MRB_THROW(prev_jmp);
           }
+          ci = mrb->c->ci;
           if (ci == mrb->c->cibase) {
-            while (eidx > 0) {
-              ecall(mrb, --eidx);
-            }
+            mrb->c->stack = stk;
             if (ci->ridx == 0) {
+            L_FTOP:             /* fiber top */
               if (mrb->c == mrb->root_c) {
-                regs = mrb->c->stack = mrb->c->stbase;
+                mrb->c->stack = mrb->c->stbase;
                 goto L_STOP;
               }
               else {
                 struct mrb_context *c = mrb->c;
 
+                if (c->fib) {
+                  mrb_write_barrier(mrb, (struct RBasic*)c->fib);
+                }
                 mrb->c = c->prev;
                 c->prev = NULL;
                 goto L_RAISE;
@@ -1515,8 +1914,13 @@ RETRY_TRY_BLOCK:
           }
           /* call ensure only when we skip this callinfo */
           if (ci[0].ridx == ci[-1].ridx) {
-            while (eidx > ci[-1].eidx) {
-              ecall(mrb, --eidx);
+           mrb_value *org_stbase = mrb->c->stbase;
+            while (mrb->c->eidx > ci->epos) {
+              ecall(mrb, --mrb->c->eidx);
+              ci = mrb->c->ci;
+             if (org_stbase != mrb->c->stbase) {
+               stk = mrb->c->stack;
+             }
             }
           }
         }
@@ -1526,34 +1930,50 @@ RETRY_TRY_BLOCK:
         irep = proc->body.irep;
         pool = irep->pool;
         syms = irep->syms;
-        regs = mrb->c->stack = ci[1].stackent;
+        if (ci != ci0) {
+          mrb->c->stack = ci[1].stackent;
+        }
+        stack_extend(mrb, irep->nregs);
         pc = mrb->c->rescue[--ci->ridx];
       }
       else {
-        mrb_callinfo *ci = mrb->c->ci;
-        int acc, eidx = mrb->c->ci->eidx;
-        mrb_value v = regs[GETARG_A(i)];
+        int acc;
+        mrb_value v;
 
+        v = regs[GETARG_A(i)];
+        mrb_gc_protect(mrb, v);
         switch (GETARG_B(i)) {
         case OP_R_RETURN:
           /* Fall through to OP_R_NORMAL otherwise */
-          if (proc->env && !MRB_PROC_STRICT_P(proc)) {
+          if (ci->acc >=0 && proc->env && !MRB_PROC_STRICT_P(proc)) {
             struct REnv *e = top_env(mrb, proc);
+            mrb_callinfo *ce;
 
-            if (!MRB_ENV_STACK_SHARED_P(e)) {
+            if (!MRB_ENV_STACK_SHARED_P(e) || e->cxt.c != mrb->c) {
               localjump_error(mrb, LOCALJUMP_ERROR_RETURN);
               goto L_RAISE;
             }
-            ci = mrb->c->cibase + e->cioff;
-            if (ci == mrb->c->cibase) {
+            
+            ce = mrb->c->cibase + e->cioff;
+            while (ci > ce) {
+              mrb_env_unshare(mrb, ci->env);
+              if (ci->acc < 0) {
+                localjump_error(mrb, LOCALJUMP_ERROR_RETURN);
+                goto L_RAISE;
+              }
+              ci--;
+            }
+            mrb_env_unshare(mrb, ci->env);
+            if (ce == mrb->c->cibase) {
               localjump_error(mrb, LOCALJUMP_ERROR_RETURN);
               goto L_RAISE;
             }
             mrb->c->stack = mrb->c->ci->stackent;
-            mrb->c->ci = ci;
+            mrb->c->ci = ce;
             break;
           }
         case OP_R_NORMAL:
+        NORMAL_RETURN:
           if (ci == mrb->c->cibase) {
             if (!mrb->c->prev) { /* toplevel return */
               localjump_error(mrb, LOCALJUMP_ERROR_RETURN);
@@ -1561,9 +1981,12 @@ RETRY_TRY_BLOCK:
             }
             if (mrb->c->prev->ci == mrb->c->prev->cibase) {
               mrb_value exc = mrb_exc_new_str_lit(mrb, E_FIBER_ERROR, "double resume");
-              mrb->exc = mrb_obj_ptr(exc);
+              mrb_exc_set(mrb, exc);
               goto L_RAISE;
             }
+            while (mrb->c->eidx > 0) {
+              ecall(mrb, --mrb->c->eidx);
+            }
             /* automatic yield at the end */
             mrb->c->status = MRB_FIBER_TERMINATED;
             mrb->c = mrb->c->prev;
@@ -1572,50 +1995,90 @@ RETRY_TRY_BLOCK:
           ci = mrb->c->ci;
           break;
         case OP_R_BREAK:
-          if (!proc->env || !MRB_ENV_STACK_SHARED_P(proc->env)) {
-            localjump_error(mrb, LOCALJUMP_ERROR_BREAK);
+          if (MRB_PROC_STRICT_P(proc)) goto NORMAL_RETURN;
+          if (MRB_PROC_ORPHAN_P(proc)) { 
+            mrb_value exc;
+
+          L_BREAK_ERROR:
+            exc = mrb_exc_new_str_lit(mrb, E_LOCALJUMP_ERROR,
+                                      "break from proc-closure");
+            mrb_exc_set(mrb, exc);
             goto L_RAISE;
           }
+          if (!proc->env || !MRB_ENV_STACK_SHARED_P(proc->env)) {
+            goto L_BREAK_ERROR;
+          }
+          if (proc->env->cxt.c != mrb->c) {
+            goto L_BREAK_ERROR;
+          }
+          while (mrb->c->eidx > mrb->c->ci->epos) {
+            ecall(mrb, --mrb->c->eidx);
+          }
           /* break from fiber block */
           if (mrb->c->ci == mrb->c->cibase && mrb->c->ci->pc) {
             struct mrb_context *c = mrb->c;
 
             mrb->c = c->prev;
             c->prev = NULL;
+            ci = mrb->c->ci;
+          }
+          if (ci->acc < 0) {
+            mrb_gc_arena_restore(mrb, ai);
+            mrb->c->vmexec = FALSE;
+            mrb->exc = (struct RObject*)break_new(mrb, proc, v);
+            mrb->jmp = prev_jmp;
+            MRB_THROW(prev_jmp);
+          }
+          if (FALSE) {
+          L_BREAK:
+            v = ((struct RBreak*)mrb->exc)->val;
+            proc = ((struct RBreak*)mrb->exc)->proc;
+            mrb->exc = NULL;
+            ci = mrb->c->ci;
           }
-          ci = mrb->c->ci;
           mrb->c->stack = ci->stackent;
           mrb->c->ci = mrb->c->cibase + proc->env->cioff + 1;
           while (ci > mrb->c->ci) {
+            mrb_env_unshare(mrb, ci->env);
             if (ci[-1].acc == CI_ACC_SKIP) {
               mrb->c->ci = ci;
-              break;
+              goto L_BREAK_ERROR;
             }
             ci--;
           }
+          mrb_env_unshare(mrb, ci->env);
           break;
         default:
           /* cannot happen */
           break;
         }
-        while (eidx > mrb->c->ci[-1].eidx) {
-          ecall(mrb, --eidx);
+        while (mrb->c->eidx > mrb->c->ci->epos) {
+          ecall(mrb, --mrb->c->eidx);
         }
-        cipop(mrb);
+        if (mrb->c->vmexec && !mrb->c->ci->target_class) {
+          mrb_gc_arena_restore(mrb, ai);
+          mrb->c->vmexec = FALSE;
+          mrb->jmp = prev_jmp;
+          return v;
+        }
+        ci = mrb->c->ci;
         acc = ci->acc;
-        pc = ci->pc;
-        regs = mrb->c->stack = ci->stackent;
-        if (acc == CI_ACC_SKIP) {
+        mrb->c->stack = ci->stackent;
+        cipop(mrb);
+        if (acc == CI_ACC_SKIP || acc == CI_ACC_DIRECT) {
+          mrb_gc_arena_restore(mrb, ai);
           mrb->jmp = prev_jmp;
           return v;
         }
-        DEBUG(printf("from :%s\n", mrb_sym2name(mrb, ci->mid)));
+        pc = ci->pc;
+        DEBUG(fprintf(stderr, "from :%s\n", mrb_sym2name(mrb, ci->mid)));
         proc = mrb->c->ci->proc;
         irep = proc->body.irep;
         pool = irep->pool;
         syms = irep->syms;
 
         regs[acc] = v;
+        mrb_gc_arena_restore(mrb, ai);
       }
       JUMP;
     }
@@ -1635,9 +2098,21 @@ RETRY_TRY_BLOCK:
       m = mrb_method_search_vm(mrb, &c, mid);
       if (!m) {
         mrb_value sym = mrb_symbol_value(mid);
+        mrb_sym missing = mrb_intern_lit(mrb, "method_missing");
+        m = mrb_method_search_vm(mrb, &c, missing);
+        if (!m) {
+          mrb_value args;
 
-        mid = mrb_intern_lit(mrb, "method_missing");
-        m = mrb_method_search_vm(mrb, &c, mid);
+          if (n == CALL_MAXARGS) {
+            args = regs[a+1];
+          }
+          else {
+            args = mrb_ary_new_from_values(mrb, n, regs+a+1);
+          }
+          ERR_PC_SET(mrb, pc);
+          mrb_method_missing(mrb, mid, recv, args);
+        }
+        mid = missing;
         if (n == CALL_MAXARGS) {
           mrb_ary_unshift(mrb, regs[a+1], sym);
         }
@@ -1662,7 +2137,8 @@ RETRY_TRY_BLOCK:
       value_move(mrb->c->stack, &regs[a], ci->argc+1);
 
       if (MRB_PROC_CFUNC_P(m)) {
-        mrb->c->stack[0] = m->body.func(mrb, recv);
+        mrb_value v = m->body.func(mrb, recv);
+        mrb->c->stack[0] = v;
         mrb_gc_arena_restore(mrb, ai);
         goto L_RETURN;
       }
@@ -1672,12 +2148,11 @@ RETRY_TRY_BLOCK:
         pool = irep->pool;
         syms = irep->syms;
         if (ci->argc < 0) {
-          stack_extend(mrb, (irep->nregs < 3) ? 3 : irep->nregs, 3);
+          stack_extend(mrb, (irep->nregs < 3) ? 3 : irep->nregs);
         }
         else {
-          stack_extend(mrb, irep->nregs, ci->argc+2);
+          stack_extend(mrb, irep->nregs);
         }
-        regs = mrb->c->stack;
         pc = irep->iseq;
       }
       JUMP;
@@ -1696,12 +2171,18 @@ RETRY_TRY_BLOCK:
       if (lv == 0) stack = regs + 1;
       else {
         struct REnv *e = uvenv(mrb, lv-1);
-        if (!e) {
+        if (!e || e->cioff == 0 ||
+            (!MRB_ENV_STACK_SHARED_P(e) && e->cxt.mid == 0) ||
+            MRB_ENV_STACK_LEN(e) <= m1+r+m2+1) {
           localjump_error(mrb, LOCALJUMP_ERROR_YIELD);
           goto L_RAISE;
         }
         stack = e->stack + 1;
       }
+      if (mrb_nil_p(stack[m1+r+m2])) {
+        localjump_error(mrb, LOCALJUMP_ERROR_YIELD);
+        goto L_RAISE;
+      }
       regs[a] = stack[m1+r+m2];
       NEXT;
     }
@@ -1766,7 +2247,7 @@ RETRY_TRY_BLOCK:
       default:
         goto L_SEND;
       }
-      ARENA_RESTORE(mrb, ai);
+      mrb_gc_arena_restore(mrb, ai);
       NEXT;
     }
 
@@ -1832,25 +2313,15 @@ RETRY_TRY_BLOCK:
       switch (TYPES2(mrb_type(regs[a]),mrb_type(regs[a+1]))) {
       case TYPES2(MRB_TT_FIXNUM,MRB_TT_FIXNUM):
         {
-          mrb_value z;
-
-          z = mrb_fixnum_mul(mrb, regs[a], regs[a+1]);
+          mrb_int x, y, z;
 
-          switch (mrb_type(z)) {
-          case MRB_TT_FIXNUM:
-            {
-              SET_INT_VALUE(regs[a], mrb_fixnum(z));
-            }
-            break;
-          case MRB_TT_FLOAT:
-            {
-              SET_FLOAT_VALUE(mrb, regs[a], mrb_float(z));
-            }
-            break;
-          default:
-            /* cannot happen */
+          x = mrb_fixnum(regs[a]);
+          y = mrb_fixnum(regs[a+1]);
+          if (mrb_int_mul_overflow(x, y, &z)) {
+            SET_FLOAT_VALUE(mrb, regs[a], (mrb_float)x * (mrb_float)y);
             break;
           }
+          SET_INT_VALUE(regs[a], z);
         }
         break;
       case TYPES2(MRB_TT_FIXNUM,MRB_TT_FLOAT):
@@ -1898,7 +2369,14 @@ RETRY_TRY_BLOCK:
         {
           mrb_int x = mrb_fixnum(regs[a]);
           mrb_int y = mrb_fixnum(regs[a+1]);
-          SET_FLOAT_VALUE(mrb, regs[a], (mrb_float)x / (mrb_float)y);
+          double f;
+          if (y == 0 && x != 0) {
+            f = INFINITY;
+          }
+          else {
+            f = (mrb_float)x / (mrb_float)y;
+          }
+          SET_FLOAT_VALUE(mrb, regs[a], f);
         }
         break;
       case TYPES2(MRB_TT_FIXNUM,MRB_TT_FLOAT):
@@ -1913,7 +2391,14 @@ RETRY_TRY_BLOCK:
         {
           mrb_float x = mrb_float(regs[a]);
           mrb_int y = mrb_fixnum(regs[a+1]);
-          SET_FLOAT_VALUE(mrb, regs[a], x / y);
+          double f;
+          if (y == 0) {
+            f = INFINITY;
+          }
+          else {
+            f = x / y;
+          }
+          SET_FLOAT_VALUE(mrb, regs[a], f);
         }
 #else
         OP_MATH_BODY(/,mrb_float,mrb_fixnum);
@@ -1935,7 +2420,8 @@ RETRY_TRY_BLOCK:
       }
 #ifdef MRB_NAN_BOXING
       if (isnan(mrb_float(regs[a]))) {
-        regs[a] = mrb_float_value(mrb, mrb_float(regs[a]));
+        mrb_value v = mrb_float_value(mrb, mrb_float(regs[a]));
+        regs[a] = v;
       }
 #endif
       NEXT;
@@ -2088,48 +2574,61 @@ RETRY_TRY_BLOCK:
 
     CASE(OP_ARRAY) {
       /* A B C          R(A) := ary_new(R(B),R(B+1)..R(B+C)) */
-      regs[GETARG_A(i)] = mrb_ary_new_from_values(mrb, GETARG_C(i), &regs[GETARG_B(i)]);
-      ARENA_RESTORE(mrb, ai);
+      int a = GETARG_A(i);
+      int b = GETARG_B(i);
+      int c = GETARG_C(i);
+      mrb_value v = mrb_ary_new_from_values(mrb, c, &regs[b]);
+      regs[a] = v;
+      mrb_gc_arena_restore(mrb, ai);
       NEXT;
     }
 
     CASE(OP_ARYCAT) {
       /* A B            mrb_ary_concat(R(A),R(B)) */
-      mrb_ary_concat(mrb, regs[GETARG_A(i)],
-                     mrb_ary_splat(mrb, regs[GETARG_B(i)]));
-      ARENA_RESTORE(mrb, ai);
+      int a = GETARG_A(i);
+      int b = GETARG_B(i);
+      mrb_value splat = mrb_ary_splat(mrb, regs[b]);
+      mrb_ary_concat(mrb, regs[a], splat);
+      mrb_gc_arena_restore(mrb, ai);
       NEXT;
     }
 
     CASE(OP_ARYPUSH) {
       /* A B            R(A).push(R(B)) */
-      mrb_ary_push(mrb, regs[GETARG_A(i)], regs[GETARG_B(i)]);
+      int a = GETARG_A(i);
+      int b = GETARG_B(i);
+      mrb_ary_push(mrb, regs[a], regs[b]);
       NEXT;
     }
 
     CASE(OP_AREF) {
       /* A B C          R(A) := R(B)[C] */
       int a = GETARG_A(i);
+      int b = GETARG_B(i);
       int c = GETARG_C(i);
-      mrb_value v = regs[GETARG_B(i)];
+      mrb_value v = regs[b];
 
       if (!mrb_array_p(v)) {
         if (c == 0) {
-          regs[GETARG_A(i)] = v;
+          regs[a] = v;
         }
         else {
           SET_NIL_VALUE(regs[a]);
         }
       }
       else {
-        regs[GETARG_A(i)] = mrb_ary_ref(mrb, v, c);
+        v = mrb_ary_ref(mrb, v, c);
+        regs[a] = v;
       }
       NEXT;
     }
 
     CASE(OP_ASET) {
       /* A B C          R(B)[C] := R(A) */
-      mrb_ary_set(mrb, regs[GETARG_B(i)], GETARG_C(i), regs[GETARG_A(i)]);
+      int a = GETARG_A(i);
+      int b = GETARG_B(i);
+      int c = GETARG_C(i);
+      mrb_ary_set(mrb, regs[b], c, regs[a]);
       NEXT;
     }
 
@@ -2139,7 +2638,6 @@ RETRY_TRY_BLOCK:
       mrb_value v = regs[a];
       int pre  = GETARG_B(i);
       int post = GETARG_C(i);
-
       struct RArray *ary;
       int len, idx;
 
@@ -2147,38 +2645,40 @@ RETRY_TRY_BLOCK:
         v = mrb_ary_new_from_values(mrb, 1, &regs[a]);
       }
       ary = mrb_ary_ptr(v);
-      len = ary->len;
+      len = ARY_LEN(ary);
       if (len > pre + post) {
-        regs[a++] = mrb_ary_new_from_values(mrb, len - pre - post, ary->ptr+pre);
+        v = mrb_ary_new_from_values(mrb, len - pre - post, ARY_PTR(ary)+pre);
+        regs[a++] = v;
         while (post--) {
-          regs[a++] = ary->ptr[len-post-1];
+          regs[a++] = ARY_PTR(ary)[len-post-1];
         }
       }
       else {
-        regs[a++] = mrb_ary_new_capa(mrb, 0);
+        v = mrb_ary_new_capa(mrb, 0);
+        regs[a++] = v;
         for (idx=0; idx+pre<len; idx++) {
-          regs[a+idx] = ary->ptr[pre+idx];
+          regs[a+idx] = ARY_PTR(ary)[pre+idx];
         }
         while (idx < post) {
           SET_NIL_VALUE(regs[a+idx]);
           idx++;
         }
       }
-      ARENA_RESTORE(mrb, ai);
+      mrb_gc_arena_restore(mrb, ai);
       NEXT;
     }
 
     CASE(OP_STRING) {
       /* A Bx           R(A) := str_new(Lit(Bx)) */
-      regs[GETARG_A(i)] = mrb_str_dup(mrb, pool[GETARG_Bx(i)]);
-      ARENA_RESTORE(mrb, ai);
+      mrb_value str = mrb_str_dup(mrb, pool[GETARG_Bx(i)]);
+      regs[GETARG_A(i)] = str;
+      mrb_gc_arena_restore(mrb, ai);
       NEXT;
     }
 
     CASE(OP_STRCAT) {
       /* A B    R(A).concat(R(B)) */
       mrb_str_concat(mrb, regs[GETARG_A(i)], regs[GETARG_B(i)]);
-      regs = mrb->c->stack;
       NEXT;
     }
 
@@ -2194,7 +2694,7 @@ RETRY_TRY_BLOCK:
         b+=2;
       }
       regs[GETARG_A(i)] = hash;
-      ARENA_RESTORE(mrb, ai);
+      mrb_gc_arena_restore(mrb, ai);
       NEXT;
     }
 
@@ -2211,7 +2711,7 @@ RETRY_TRY_BLOCK:
       }
       if (c & OP_L_STRICT) p->flags |= MRB_PROC_STRICT;
       regs[GETARG_A(i)] = mrb_obj_value(p);
-      ARENA_RESTORE(mrb, ai);
+      mrb_gc_arena_restore(mrb, ai);
       NEXT;
     }
 
@@ -2223,7 +2723,7 @@ RETRY_TRY_BLOCK:
 
     CASE(OP_CLASS) {
       /* A B    R(A) := newclass(R(A),Syms(B),R(A+1)) */
-      struct RClass *c = 0;
+      struct RClass *c = 0, *baseclass;
       int a = GETARG_A(i);
       mrb_value base, super;
       mrb_sym id = syms[GETARG_B(i)];
@@ -2231,28 +2731,34 @@ RETRY_TRY_BLOCK:
       base = regs[a];
       super = regs[a+1];
       if (mrb_nil_p(base)) {
-        base = mrb_obj_value(mrb->c->ci->target_class);
+        baseclass = mrb->c->ci->proc->target_class;
+        if (!baseclass) baseclass = mrb->c->ci->target_class;
+
+        base = mrb_obj_value(baseclass);
       }
       c = mrb_vm_define_class(mrb, base, super, id);
       regs[a] = mrb_obj_value(c);
-      ARENA_RESTORE(mrb, ai);
+      mrb_gc_arena_restore(mrb, ai);
       NEXT;
     }
 
     CASE(OP_MODULE) {
       /* A B            R(A) := newmodule(R(A),Syms(B)) */
-      struct RClass *c = 0;
+      struct RClass *c = 0, *baseclass;
       int a = GETARG_A(i);
       mrb_value base;
       mrb_sym id = syms[GETARG_B(i)];
 
       base = regs[a];
       if (mrb_nil_p(base)) {
-        base = mrb_obj_value(mrb->c->ci->target_class);
+        baseclass = mrb->c->ci->proc->target_class;
+        if (!baseclass) baseclass = mrb->c->ci->target_class;
+
+        base = mrb_obj_value(baseclass);
       }
       c = mrb_vm_define_module(mrb, base, id);
       regs[a] = mrb_obj_value(c);
-      ARENA_RESTORE(mrb, ai);
+      mrb_gc_arena_restore(mrb, ai);
       NEXT;
     }
 
@@ -2263,6 +2769,10 @@ RETRY_TRY_BLOCK:
       mrb_value recv = regs[a];
       struct RProc *p;
 
+      /* prepare closure */
+      p = mrb_closure_new(mrb, irep->reps[GETARG_Bx(i)]);
+      p->c = NULL;
+
       /* prepare stack */
       ci = cipush(mrb);
       ci->pc = pc + 1;
@@ -2275,30 +2785,18 @@ RETRY_TRY_BLOCK:
       /* prepare stack */
       mrb->c->stack += a;
 
-      p = mrb_proc_new(mrb, irep->reps[GETARG_Bx(i)]);
+      /* setup closure */
       p->target_class = ci->target_class;
       ci->proc = p;
 
-      if (MRB_PROC_CFUNC_P(p)) {
-        ci->nregs = 0;
-        mrb->c->stack[0] = p->body.func(mrb, recv);
-        mrb_gc_arena_restore(mrb, ai);
-        if (mrb->exc) goto L_RAISE;
-        /* pop stackpos */
-        regs = mrb->c->stack = mrb->c->ci->stackent;
-        cipop(mrb);
-        NEXT;
-      }
-      else {
-        irep = p->body.irep;
-        pool = irep->pool;
-        syms = irep->syms;
-        stack_extend(mrb, irep->nregs, 1);
-        ci->nregs = irep->nregs;
-        regs = mrb->c->stack;
-        pc = irep->iseq;
-        JUMP;
-      }
+      irep = p->body.irep;
+      pool = irep->pool;
+      syms = irep->syms;
+      stack_extend(mrb, irep->nregs);
+      stack_clear(regs+1, irep->nregs-1);
+      ci->nregs = irep->nregs;
+      pc = irep->iseq;
+      JUMP;
     }
 
     CASE(OP_METHOD) {
@@ -2308,14 +2806,14 @@ RETRY_TRY_BLOCK:
       struct RProc *p = mrb_proc_ptr(regs[a+1]);
 
       mrb_define_method_raw(mrb, c, syms[GETARG_B(i)], p);
-      ARENA_RESTORE(mrb, ai);
+      mrb_gc_arena_restore(mrb, ai);
       NEXT;
     }
 
     CASE(OP_SCLASS) {
       /* A B    R(A) := R(B).singleton_class */
       regs[GETARG_A(i)] = mrb_singleton_class(mrb, regs[GETARG_B(i)]);
-      ARENA_RESTORE(mrb, ai);
+      mrb_gc_arena_restore(mrb, ai);
       NEXT;
     }
 
@@ -2323,7 +2821,7 @@ RETRY_TRY_BLOCK:
       /* A      R(A) := target_class */
       if (!mrb->c->ci->target_class) {
         mrb_value exc = mrb_exc_new_str_lit(mrb, E_TYPE_ERROR, "no target class or module");
-        mrb->exc = mrb_obj_ptr(exc);
+        mrb_exc_set(mrb, exc);
         goto L_RAISE;
       }
       regs[GETARG_A(i)] = mrb_obj_value(mrb->c->ci->target_class);
@@ -2333,8 +2831,9 @@ RETRY_TRY_BLOCK:
     CASE(OP_RANGE) {
       /* A B C  R(A) := range_new(R(B),R(B+1),C) */
       int b = GETARG_B(i);
-      regs[GETARG_A(i)] = mrb_range_new(mrb, regs[b], regs[b+1], GETARG_C(i));
-      ARENA_RESTORE(mrb, ai);
+      mrb_value val = mrb_range_new(mrb, regs[b], regs[b+1], GETARG_C(i));
+      regs[GETARG_A(i)] = val;
+      mrb_gc_arena_restore(mrb, ai);
       NEXT;
     }
 
@@ -2356,10 +2855,10 @@ RETRY_TRY_BLOCK:
       /*        stop VM */
     L_STOP:
       {
-        int eidx_stop = mrb->c->ci == mrb->c->cibase ? 0 : mrb->c->ci[-1].eidx;
-        int eidx = mrb->c->ci->eidx;
-        while (eidx > eidx_stop) {
-          ecall(mrb, --eidx);
+        int epos = mrb->c->ci->epos;
+
+        while (mrb->c->eidx > epos) {
+          ecall(mrb, --mrb->c->eidx);
         }
       }
       ERR_PC_CLR(mrb);
@@ -2381,11 +2880,12 @@ RETRY_TRY_BLOCK:
       else {
         exc = mrb_exc_new_str(mrb, E_LOCALJUMP_ERROR, msg);
       }
-      mrb->exc = mrb_obj_ptr(exc);
+      mrb_exc_set(mrb, exc);
       goto L_RAISE;
     }
   }
   END_DISPATCH;
+#undef regs
 
   }
   MRB_CATCH(&c_jmp) {
@@ -2398,30 +2898,44 @@ RETRY_TRY_BLOCK:
 MRB_API mrb_value
 mrb_run(mrb_state *mrb, struct RProc *proc, mrb_value self)
 {
-  return mrb_context_run(mrb, proc, self, mrb->c->ci->argc + 2); /* argc + 2 (receiver and block) */
+  if (mrb->c->ci->argc < 0) {
+    return mrb_vm_run(mrb, proc, self, 3); /* receiver, args and block) */
+  }
+  else {
+    return mrb_vm_run(mrb, proc, self, mrb->c->ci->argc + 2); /* argc + 2 (receiver and block) */
+  }
 }
 
 MRB_API mrb_value
-mrb_toplevel_run_keep(mrb_state *mrb, struct RProc *proc, unsigned int stack_keep)
+mrb_top_run(mrb_state *mrb, struct RProc *proc, mrb_value self, unsigned int stack_keep)
 {
   mrb_callinfo *ci;
   mrb_value v;
 
-  if (!mrb->c->cibase || mrb->c->ci == mrb->c->cibase) {
-    return mrb_context_run(mrb, proc, mrb_top_self(mrb), stack_keep);
+  if (!mrb->c->cibase) {
+    return mrb_vm_run(mrb, proc, self, stack_keep);
+  }
+  if (mrb->c->ci == mrb->c->cibase) {
+    mrb->c->ci->env = NULL;
+    return mrb_vm_run(mrb, proc, self, stack_keep);
   }
   ci = cipush(mrb);
+  ci->mid = 0;
   ci->nregs = 1;   /* protect the receiver */
   ci->acc = CI_ACC_SKIP;
   ci->target_class = mrb->object_class;
-  v = mrb_context_run(mrb, proc, mrb_top_self(mrb), stack_keep);
+  v = mrb_vm_run(mrb, proc, self, stack_keep);
   cipop(mrb);
 
   return v;
 }
 
-MRB_API mrb_value
-mrb_toplevel_run(mrb_state *mrb, struct RProc *proc)
-{
-  return mrb_toplevel_run_keep(mrb, proc, 0);
-}
+#if defined(MRB_ENABLE_CXX_EXCEPTION) && defined(__cplusplus)
+# if !defined(MRB_ENABLE_CXX_ABI)
+} /* end of extern "C" */
+# endif
+mrb_int mrb_jmpbuf::jmpbuf_id = 0;
+# if !defined(MRB_ENABLE_CXX_ABI)
+extern "C" {
+# endif
+#endif
diff --git a/third-party/mruby/tasks/gitlab.rake b/third-party/mruby/tasks/gitlab.rake
new file mode 100644 (file)
index 0000000..4711723
--- /dev/null
@@ -0,0 +1,118 @@
+CI_VERSION = '0.7'.freeze
+CI_BASE = 'ubuntu:16.10'.freeze
+CI_COMPILERS = ['gcc-4.7',
+                'gcc-4.8',
+                'gcc-4.9',
+                'gcc-5',
+                'gcc-6',
+                'clang-3.5',
+                'clang-3.6',
+                'clang-3.7',
+                'clang-3.8',
+                'clang-3.9'].freeze
+
+def ci_image_tag(compiler)
+  compiler.tr('+', 'c').delete('-').delete('.')
+end
+
+def ci_docker_tag(compiler)
+  tag = ci_image_tag(compiler)
+  "registry.gitlab.com/dabroz/mruby:#{tag}_#{CI_VERSION}"
+end
+
+def run_cmd(cmd)
+  puts cmd
+  raise 'error' unless system cmd
+end
+
+desc 'recreate docker images for GitLab builds'
+task :gitlab_dockers do
+  CI_COMPILERS.each do |compiler|
+    tag = ci_image_tag(compiler)
+    filename = "Dockerfile.#{tag}"
+    File.open(filename, 'wb') do |f|
+      f << "# #{compiler} - #{tag}\n"
+      f << "FROM #{CI_BASE}\n"
+      f << "RUN apt-get update && apt-get install -y git ruby2.3 ruby2.3-dev bison\n"
+      f << "RUN apt-get update && apt-get install -y binutils manpages\n"
+      f << "RUN apt-get update && apt-get install -y #{compiler}\n"
+      if compiler['gcc']
+        f << "RUN apt-get update && apt-get install -y libx32#{compiler}-dev\n"
+        f << "RUN apt-get update && apt-get install --no-install-recommends -y #{compiler}-multilib\n"
+      end
+      f << "RUN dpkg --add-architecture i386\n"
+      f << "RUN apt-get update && apt-get install -y linux-libc-dev:i386\n"
+      if compiler['clang']
+        f << "RUN apt-get update && apt-get install --no-install-recommends -y libc6-dev-i386\n"
+        f << "RUN apt-get update && apt-get install -y gcc gcc-multilib\n"
+      end
+    end
+    docker_tag = ci_docker_tag(compiler)
+    cmd1 = "docker build -t #{docker_tag} -f #{filename} ."
+    cmd2 = "docker push #{docker_tag}"
+    run_cmd cmd1
+    run_cmd cmd2
+    File.delete(filename)
+  end
+end
+
+desc 'create build configurations and update .gitlab-ci.yml'
+task :gitlab_config do
+  require 'yaml'
+
+  configs = []
+  [true, false].each do |mode_32|
+    ['', 'MRB_USE_FLOAT'].each do |float_conf|
+      ['', 'MRB_INT16', 'MRB_INT64'].each do |int_conf|
+        ['', 'MRB_NAN_BOXING', 'MRB_WORD_BOXING'].each do |boxing_conf|
+          ['', 'MRB_UTF8_STRING'].each do |utf8_conf|
+            next if (float_conf == 'MRB_USE_FLOAT') && (boxing_conf == 'MRB_NAN_BOXING')
+            next if (int_conf == 'MRB_INT64') && (boxing_conf == 'MRB_NAN_BOXING')
+            next if (int_conf == 'MRB_INT16') && (boxing_conf == 'MRB_WORD_BOXING')
+            next if (int_conf == 'MRB_INT64') && (boxing_conf == 'MRB_WORD_BOXING') && mode_32
+            env = [float_conf, int_conf, boxing_conf, utf8_conf].map do |conf|
+              conf == '' ? nil : "-D#{conf}=1"
+            end.compact.join(' ')
+            bit = mode_32 ? '-m32 ' : ''
+            _info = ''
+            _info += mode_32 ? '32bit ' : '64bit '
+            _info += float_conf['USE'] ? 'float ' : ''
+            _info += int_conf['16'] ? 'int16 ' : ''
+            _info += int_conf['64'] ? 'int64 ' : ''
+            _info += boxing_conf['NAN'] ? 'nan ' : ''
+            _info += boxing_conf['word'] ? 'word ' : ''
+            _info += utf8_conf['UTF8'] ? 'utf8 ' : ''
+            _info = _info.gsub(/ +/, ' ').strip.tr(' ', '_')
+            configs << { '_info' => _info, 'CFLAGS' => "#{bit}#{env}", 'LDFLAGS' => bit.strip.to_s }
+          end
+        end
+      end
+    end
+  end
+  path = './.gitlab-ci.yml'
+  data = YAML.load_file(path)
+  data.keys.select do |key|
+    key.start_with? 'Test'
+  end.each do |key|
+    data.delete(key)
+  end
+  CI_COMPILERS.each do |compiler|
+    configs.each do |config|
+      name = "Test #{compiler} #{config['_info']}"
+      hash = {
+        'CC' => compiler,
+        'CXX' => compiler.gsub('gcc', 'g++').gsub('clang', 'clang++'),
+        'LD' => compiler
+      }
+      hash = hash.merge(config)
+      hash.delete('_info')
+      data[name] = {
+        'stage' => 'test',
+        'image' => ci_docker_tag(compiler),
+        'variables' => hash,
+        'script' => 'env; ./minirake --verbose all test'
+      }
+    end
+  end
+  File.open(path, 'w') { |f| YAML.dump(data, f) }
+end
index 095bedd..540aa3e 100644 (file)
@@ -5,17 +5,19 @@ MRuby.each_target do
 
   file "#{build_dir}/lib/libmruby.flags.mak" => [__FILE__, libfile("#{build_dir}/lib/libmruby")] do |t|
     open(t.name, 'w') do |f|
-      f.puts "MRUBY_CFLAGS = #{cc.all_flags.gsub('"', '\\"')}"
+      f.puts "MRUBY_CFLAGS = #{cc.all_flags}"
 
       gem_flags = gems.map { |g| g.linker.flags }
       gem_library_paths = gems.map { |g| g.linker.library_paths }
-      f.puts "MRUBY_LDFLAGS = #{linker.all_flags(gem_library_paths, gem_flags).gsub('"', '\\"')} #{linker.option_library_path % "#{build_dir}/lib"}"
+      f.puts "MRUBY_LDFLAGS = #{linker.all_flags(gem_library_paths, gem_flags)} #{linker.option_library_path % "#{build_dir}/lib"}"
 
       gem_flags_before_libraries = gems.map { |g| g.linker.flags_before_libraries }
-      f.puts "MRUBY_LDFLAGS_BEFORE_LIBS = #{[linker.flags_before_libraries, gem_flags_before_libraries].flatten.join(' ').gsub('"', '\\"')}"
+      f.puts "MRUBY_LDFLAGS_BEFORE_LIBS = #{[linker.flags_before_libraries, gem_flags_before_libraries].flatten.join(' ')}"
 
       gem_libraries = gems.map { |g| g.linker.libraries }
-      f.puts "MRUBY_LIBS = #{linker.option_library % 'mruby'} #{linker.library_flags(gem_libraries).gsub('"', '\\"')}"
+      f.puts "MRUBY_LIBS = #{linker.option_library % 'mruby'} #{linker.library_flags(gem_libraries)}"
+
+      f.puts "MRUBY_LIBMRUBY_PATH = #{libfile("#{build_dir}/lib/libmruby")}"
     end
   end
   task :all => "#{build_dir}/lib/libmruby.flags.mak"
index 4fcfc8f..27a1d35 100644 (file)
@@ -1,6 +1,7 @@
 require 'pathname'
 require 'forwardable'
 require 'tsort'
+require 'shellwords'
 
 module MRuby
   module Gem
@@ -17,6 +18,7 @@ module MRuby
       attr_accessor :name, :dir, :build
       alias mruby build
       attr_accessor :build_config_initializer
+      attr_accessor :mrblib_dir, :objs_dir
 
       attr_accessor :version
       attr_accessor :description, :summary
@@ -44,6 +46,8 @@ module MRuby
         @name = name
         @initializer = block
         @version = "0.0.0"
+        @mrblib_dir = "mrblib"
+        @objs_dir = "src"
         MRuby::Gem.current = self
       end
 
@@ -54,14 +58,11 @@ module MRuby
         end
         @linker = LinkerConfig.new([], [], [], [], [])
 
-        @rbfiles = Dir.glob("#{dir}/mrblib/**/*.rb").sort
-        @objs = Dir.glob("#{dir}/src/*.{c,cpp,cxx,cc,m,asm,s,S}").map do |f|
+        @rbfiles = Dir.glob("#{@dir}/#{@mrblib_dir}/**/*.rb").sort
+        @objs = Dir.glob("#{@dir}/#{@objs_dir}/*.{c,cpp,cxx,cc,m,asm,s,S}").map do |f|
           objfile(f.relative_path_from(@dir).to_s.pathmap("#{build_dir}/%X"))
         end
 
-        @generate_functions = !(@rbfiles.empty? && @objs.empty?)
-        @objs << objfile("#{build_dir}/gem_init") if @generate_functions
-
         @test_rbfiles = Dir.glob("#{dir}/test/**/*.rb")
         @test_objs = Dir.glob("#{dir}/test/*.{c,cpp,cxx,cc,m,asm,s,S}").map do |f|
           objfile(f.relative_path_from(dir).to_s.pathmap("#{build_dir}/%X"))
@@ -79,6 +80,9 @@ module MRuby
 
         instance_eval(&@initializer)
 
+        @generate_functions = !(@rbfiles.empty? && @objs.empty?)
+        @objs << objfile("#{build_dir}/gem_init") if @generate_functions
+
         if !name || !licenses || !authors
           fail "#{name || dir} required to set name, license(s) and author(s)"
         end
@@ -86,7 +90,9 @@ module MRuby
         build.libmruby << @objs
 
         instance_eval(&@build_config_initializer) if @build_config_initializer
+      end
 
+      def setup_compilers
         compilers.each do |compiler|
           compiler.define_rules build_dir, "#{dir}"
           compiler.defines << %Q[MRBGEM_#{funcname.upcase}_VERSION=#{version}]
@@ -123,6 +129,21 @@ module MRuby
         "#{build_dir}/gem_test.c"
       end
 
+      def search_package(name, version_query=nil)
+        package_query = name
+        package_query += " #{version_query}" if version_query
+        _pp "PKG-CONFIG", package_query
+        escaped_package_query = Shellwords.escape(package_query)
+        if system("pkg-config --exists #{escaped_package_query}")
+          cc.flags += [`pkg-config --cflags #{escaped_package_query}`.strip]
+          cxx.flags += [`pkg-config --cflags #{escaped_package_query}`.strip]
+          linker.flags_before_libraries += [`pkg-config --libs #{escaped_package_query}`.strip]
+          true
+        else
+          false
+        end
+      end
+
       def funcname
         @funcname ||= @name.gsub('-', '_')
       end
@@ -181,18 +202,18 @@ module MRuby
       def print_gem_init_header(f)
         print_gem_comment(f)
         f.puts %Q[#include <stdlib.h>] unless rbfiles.empty?
-        f.puts %Q[#include "mruby.h"]
-        f.puts %Q[#include "mruby/irep.h"] unless rbfiles.empty?
+        f.puts %Q[#include <mruby.h>]
+        f.puts %Q[#include <mruby/irep.h>] unless rbfiles.empty?
       end
 
       def print_gem_test_header(f)
         print_gem_comment(f)
         f.puts %Q[#include <stdio.h>]
         f.puts %Q[#include <stdlib.h>]
-        f.puts %Q[#include "mruby.h"]
-        f.puts %Q[#include "mruby/irep.h"]
-        f.puts %Q[#include "mruby/variable.h"]
-        f.puts %Q[#include "mruby/hash.h"] unless test_args.empty?
+        f.puts %Q[#include <mruby.h>]
+        f.puts %Q[#include <mruby/irep.h>]
+        f.puts %Q[#include <mruby/variable.h>]
+        f.puts %Q[#include <mruby/hash.h>] unless test_args.empty?
       end
 
       def test_dependencies
@@ -400,6 +421,8 @@ module MRuby
 
         @ary = tsort_dependencies gem_table.keys, gem_table, true
 
+        each(&:setup_compilers)
+
         each do |g|
           import_include_paths(g)
         end
@@ -413,9 +436,12 @@ module MRuby
           # as circular dependency has already detected in the caller.
           import_include_paths(dep_g)
 
+          dep_g.export_include_paths.uniq!
           g.compilers.each do |compiler|
             compiler.include_paths += dep_g.export_include_paths
             g.export_include_paths += dep_g.export_include_paths
+            compiler.include_paths.uniq!
+            g.export_include_paths.uniq!
           end
         end
       end
index 1cf05ee..65368c3 100644 (file)
@@ -31,7 +31,7 @@ MRuby.each_target do
         f.puts %Q[ *   All manual changes will get lost.]
         f.puts %Q[ */]
         f.puts %Q[]
-        f.puts %Q[#include "mruby.h"]
+        f.puts %Q[#include <mruby.h>]
         f.puts %Q[]
         f.write gem_func_decls
         f.puts %Q[]
index cff45dd..f5d4374 100644 (file)
@@ -83,8 +83,9 @@ module MRuby
         @bins = []
         @gems, @libmruby = MRuby::Gem::List.new, []
         @build_mrbtest_lib_only = false
-        @cxx_abi_enabled = false
+        @cxx_exception_enabled = false
         @cxx_exception_disabled = false
+        @cxx_abi_enabled = false
         @enable_bintest = false
         @enable_test = false
         @toolchains = []
@@ -110,16 +111,44 @@ module MRuby
     end
 
     def disable_cxx_exception
+      if @cxx_exception_enabled or @cxx_abi_enabled
+        raise "cxx_exception already enabled"
+      end
       @cxx_exception_disabled = true
     end
 
+    def enable_cxx_exception
+      return if @cxx_exception_enabled
+      return if @cxx_abi_enabled
+      if @cxx_exception_disabled
+        raise "cxx_exception disabled"
+      end
+      @cxx_exception_enabled = true
+      compilers.each { |c|
+        c.defines += %w(MRB_ENABLE_CXX_EXCEPTION)
+        c.flags << c.cxx_exception_flag
+      }
+      linker.command = cxx.command if toolchains.find { |v| v == 'gcc' }
+    end
+
+    def cxx_exception_enabled?
+      @cxx_exception_enabled
+    end
+
     def cxx_abi_enabled?
       @cxx_abi_enabled
     end
 
     def enable_cxx_abi
-      return if @cxx_exception_disabled or @cxx_abi_enabled
-      compilers.each { |c| c.defines += %w(MRB_ENABLE_CXX_EXCEPTION) }
+      return if @cxx_abi_enabled
+      if @cxx_exception_enabled
+        raise "cxx_exception already enabled"
+      end
+      compilers.each { |c|
+        c.defines += %w(MRB_ENABLE_CXX_EXCEPTION MRB_ENABLE_CXX_ABI)
+        c.flags << c.cxx_compile_flag
+      }
+      compilers.each { |c| c.flags << c.cxx_compile_flag }
       linker.command = cxx.command if toolchains.find { |v| v == 'gcc' }
       @cxx_abi_enabled = true
     end
@@ -135,11 +164,13 @@ module MRuby
 #define __STDC_CONSTANT_MACROS
 #define __STDC_LIMIT_MACROS
 
+#ifndef MRB_ENABLE_CXX_ABI
 extern "C" {
+#endif
 #include "#{src}"
+#ifndef MRB_ENABLE_CXX_ABI
 }
-
-#{src == "#{MRUBY_ROOT}/src/error.c"? 'mrb_int mrb_jmpbuf::jmpbuf_id = 0;' : ''}
+#endif
 EOS
       end
 
index f4805f4..694b4a2 100644 (file)
@@ -41,6 +41,7 @@ module MRuby
   class Command::Compiler < Command
     attr_accessor :flags, :include_paths, :defines, :source_exts
     attr_accessor :compile_options, :option_define, :option_include_path, :out_ext
+    attr_accessor :cxx_compile_flag, :cxx_exception_flag
 
     def initialize(build, source_exts=[])
       super(build)
index 9a80209..b48df65 100644 (file)
@@ -38,7 +38,7 @@ module MRuby
       cxx_srcs = ['src', 'test', 'tools'].map do |subdir|
         Dir.glob("#{Gem.current.dir}/#{subdir}/*.{cpp,cxx,cc}")
       end.flatten
-      enable_cxx_abi unless cxx_srcs.empty?
+      enable_cxx_exception unless cxx_srcs.empty?
 
       Gem.current
     end
index aadc5b3..c59da7f 100644 (file)
@@ -1,15 +1,24 @@
 class MRuby::Toolchain::Android
-  DEFAULT_ARCH = 'armeabi'
-  DEFAULT_PLATFORM = 'android-14'
-  DEFAULT_TOOLCHAIN = :gcc
+
+  DEFAULT_ARCH = 'armeabi' # TODO : Revise if arch should have a default
+
+  DEFAULT_TOOLCHAIN = :clang
+
   DEFAULT_NDK_HOMES = %w{
+    /usr/local/opt/android-sdk/ndk-bundle
     /usr/local/opt/android-ndk
+    %LOCALAPPDATA%/Android/android-sdk/ndk-bundle
+    %LOCALAPPDATA%/Android/android-ndk
+    ~/Library/Android/sdk/ndk-bundle
+    ~/Library/Android/ndk
   }
-  TOOLCHAINS = [:gcc, :clang]
+
+  TOOLCHAINS = [:clang, :gcc]
+
   ARCHITECTURES = %w{
     armeabi armeabi-v7a arm64-v8a
-    mips mips64
     x86 x86_64
+    mips mips64
   }
 
   class AndroidNDKHomeNotFound < StandardError
@@ -21,196 +30,292 @@ Set ANDROID_NDK_HOME environment variable or set :ndk_home parameter
     end
   end
 
+  class PlatformDirNotFound < StandardError
+    def message
+        <<-EOM
+Couldn't find Android NDK platform directories.
+Set ANDROID_PLATFORM environment variable or set :platform parameter
+        EOM
+    end
+  end
+
   attr_reader :params
 
   def initialize(params)
     @params = params
   end
 
-  def home_path
-    @home_path ||= Pathname(
-      params[:ndk_home] ||
-        ENV['ANDROID_NDK_HOME'] ||
-        DEFAULT_NDK_HOMES.find{ |path| File.directory?(path) } ||
-        raise(AndroidNDKHomeNotFound)
-    )
+  def bin_gcc(command)
+    command = command.to_s
+
+    command = case arch
+      when /armeabi/    then 'arm-linux-androideabi-'
+      when /arm64-v8a/  then 'aarch64-linux-android-'
+      when /x86_64/     then 'x86_64-linux-android-'
+      when /x86/        then 'i686-linux-android-'
+      when /mips64/     then 'mips64el-linux-android-'
+      when /mips/       then 'mipsel-linux-android-'
+      end + command
+
+    gcc_toolchain_path.join('bin', command).to_s
   end
 
-  def arch
-    params.fetch(:arch){ DEFAULT_ARCH }
+  def bin(command)
+    command = command.to_s
+    toolchain_path.join('bin', command).to_s
   end
 
-  def platform
-    params.fetch(:platform){ DEFAULT_PLATFORM }
+  def home_path
+    @home_path ||= Pathname(
+      params[:ndk_home] ||
+      ENV['ANDROID_NDK_HOME'] ||
+      DEFAULT_NDK_HOMES.find { |path|
+        path.gsub! '%LOCALAPPDATA%', ENV['LOCALAPPDATA'] || '%LOCALAPPDATA%'
+        path.gsub! '\\', '/'
+        path.gsub! '~', Dir.home || '~'
+        File.directory?(path)
+      } || raise(AndroidNDKHomeNotFound)
+    )
   end
 
   def toolchain
-    params.fetch(:toolchain){ DEFAULT_TOOLCHAIN }
+    @toolchain ||= params.fetch(:toolchain){ DEFAULT_TOOLCHAIN }
   end
 
-  def toolchain_version
-    params.fetch(:toolchain_version) do
-      test = case toolchain
+  def toolchain_path
+    @toolchain_path ||= case toolchain
       when :gcc
-        case arch
-        when /armeabi/
-          'arm-linux-androideabi-*'
-        when /arm64/
-          'aarch64-linux-android-*'
-        when /mips64/
-          'mips64el-linux-android-*'
-        when /mips/
-          'mipsel-linux-android-*'
-        when /x86_64/
-          'x86_64-*'
-        when /x86/
-          'x86-*'
-        end
+        gcc_toolchain_path
       when :clang
-        'llvm-*'
+        home_path.join('toolchains', 'llvm' , 'prebuilt', host_platform)
       end
+  end
 
-      Dir[home_path.join('toolchains',test)].map{|t| t.match(/-(\d+\.\d+)$/); $1.to_f }.max
+  def gcc_toolchain_path
+    if @gcc_toolchain_path === nil then
+      prefix = case arch
+        when /armeabi/    then 'arm-linux-androideabi-'
+        when /arm64-v8a/  then 'aarch64-linux-android-'
+        when /x86_64/     then 'x86_64-'
+        when /x86/        then 'x86-'
+        when /mips64/     then 'mips64el-linux-android-'
+        when /mips/       then 'mipsel-linux-android-'
+        end
+
+      test = case arch
+        when /armeabi/    then 'arm-linux-androideabi-*'
+        when /arm64-v8a/  then 'aarch64-linux-android-*'
+        when /x86_64/     then 'x86_64-*'
+        when /x86/        then 'x86-*'
+        when /mips64/     then 'mips64el-linux-android-*'
+        when /mips/       then 'mipsel-linux-android-*'
+        end
+
+      gcc_toolchain_version = Dir[home_path.join('toolchains', test)].map{|t| t.match(/-(\d+\.\d+)$/); $1.to_f }.max
+      @gcc_toolchain_path = home_path.join('toolchains', prefix + gcc_toolchain_version.to_s, 'prebuilt', host_platform)
     end
+    @gcc_toolchain_path
   end
 
-  def toolchain_path
-    prefix = case toolchain
-             when :clang then 'llvm-'
-             when :gcc
-               case arch
-               when /armeabi/ then 'arm-linux-androideabi-'
-               when /arm64/   then 'aarch64-linux-android-'
-               when /x86_64/  then 'x86_64-'
-               when /x86/     then 'x86-'
-               when /mips64/  then 'mips64el-linux-android-'
-               when /mips/    then 'mipsel-linux-android-'
-               end
-             end
-    home_path.join('toolchains', prefix + toolchain_version.to_s, 'prebuilt', host_platform)
+  def host_platform
+    @host_platform ||= case RUBY_PLATFORM
+      when /cygwin|mswin|mingw|bccwin|wince|emx/i
+        path = home_path.join('toolchains', 'llvm' , 'prebuilt', 'windows*')
+        Dir.glob(path.to_s){ |item|
+          next if File.file?(item)
+          path = Pathname(item)
+          break
+        }
+        path.basename
+      when /x86_64-darwin/i
+        'darwin-x86_64'
+      when /darwin/i
+        'darwin-x86'
+      when /x86_64-linux/i
+        'linux-x86_64'
+      when /linux/i
+        'linux-x86'
+      else
+        raise NotImplementedError, "Unknown host platform (#{RUBY_PLATFORM})"
+      end
+  end
+
+  def arch
+    @arch ||= (params[:arch] || ENV['ANDROID_ARCH'] || DEFAULT_ARCH).to_s
   end
 
   def sysroot
-    path = case arch
-           when /armeabi/ then 'arch-arm'
-           when /arm64/   then 'arch-arm64'
-           when /x86_64/  then 'arch-x86_64'
-           when /x86/     then 'arch-x86'
-           when /mips64/  then 'arch-mips64'
-           when /mips/    then 'arch-mips'
-           end
+    @sysroot ||= home_path.join('platforms', platform,
+        case arch
+        when /armeabi/    then 'arch-arm'
+        when /arm64-v8a/  then 'arch-arm64'
+        when /x86_64/     then 'arch-x86_64'
+        when /x86/        then 'arch-x86'
+        when /mips64/     then 'arch-mips64'
+        when /mips/       then 'arch-mips'
+        end
+      ).to_s
+  end
 
-    home_path.join('platforms', platform, path).to_s
+  def platform
+    if @platform === nil then
+      @platform = params[:platform] || ENV['ANDROID_PLATFORM'] || nil
+      if @platform === nil
+        Dir.glob(home_path.join('platforms/android-*').to_s){ |item|
+          next if File.file?(item)
+          if @platform === nil
+            @platform = Integer(item.rpartition('-')[2])
+          else
+            platform = Integer(item.rpartition('-')[2])
+            @platform = platform > @platform ? platform : @platform
+          end
+        }
+        if @platform === nil
+          raise(PlatformDirNotFound)
+        else
+          @platform = "android-#{@platform}"
+        end
+      end
+    end
+    if Integer(@platform.rpartition('-')[2]) < 21
+      case arch
+      when /arm64-v8a/, /x86_64/, /mips64/
+        raise NotImplementedError, "Platform (#{@platform}) has no implementation for architecture (#{arch})"
+      end
+    end
+    @platform
   end
 
-  def bin(command)
-    command = command.to_s
+  def armeabi_v7a_mfpu
+    @armeabi_v7a_mfpu ||= (params[:mfpu] || 'vfpv3-d16').to_s
+  end
 
-    if toolchain == :gcc
-      command = case arch
-                when /armeabi/ then 'arm-linux-androideabi-'
-                when /arm64/   then 'aarch64-linux-android-'
-                when /x86_64/  then 'x86_64-linux-android-'
-                when /x86/     then 'i686-linux-android-'
-                when /mips64/  then 'mips64el-linux-android-'
-                when /mips/    then 'mipsel-linux-android-'
-                end + command
-    end
+  def armeabi_v7a_mfloat_abi
+    @armeabi_v7a_mfloat_abi ||= (params[:mfloat_abi] || 'softfp').to_s
+  end
 
-    toolchain_path.join('bin',command).to_s
+  def no_warn_mismatch
+    if %W(soft softfp).include? armeabi_v7a_mfloat_abi
+      ''
+    else
+      ',--no-warn-mismatch'
+    end
   end
 
   def cc
     case toolchain
-    when :gcc   then bin(:gcc)
-    when :clang then bin(:clang)
+    when :gcc then bin_gcc('gcc')
+    when :clang then bin('clang')
     end
   end
 
-  def cflags
+  def ar
+    case toolchain
+    when :gcc   then bin_gcc('ar')
+    when :clang then bin_gcc('ar')
+    end
+  end
+
+  def ctarget
     flags = []
 
     case toolchain
     when :gcc
-      flags += %W(-ffunction-sections -funwind-tables -no-canonical-prefixes)
-      flags += %W(-D__android__ -mandroid --sysroot="#{sysroot}")
       case arch
-      when /arm64/
-        flags += %W(-fpic -fstack-protector-strong)
-      when 'armeabi-v7a-hard'
-        flags += %W(-fpic -fstack-protector-strong -march=armv7-a -mhard-float -D_NDK_MATH_NO_SOFTFP=1 -mfpu=vfpv3-d16)
-      when 'armeabi-v7a'
-        flags += %W(-fpic -fstack-protector-strong -march=armv7-a -mfloat-abi=softfp -mfpu=vfpv3-d16)
-      when /arm/
-        flags += %W(-fpic -fstack-protector-strong -march=armv5te -mtune=xscale -msoft-float)
-      when /mips/
-        flags += %W(-fpic -fno-strict-aliasing -finline-functions -fmessage-length=0 -fno-inline-functions-called-once -fgcse-after-reload -frerun-cse-after-loop -frename-registers)
-      when /x86/
-        flags += %W(-fstack-protector-strong)
+      when /armeabi-v7a/  then flags += %W(-march=armv7-a)
+      when /armeabi/      then flags += %W(-march=armv5te)
+      when /arm64-v8a/    then flags += %W(-march=armv8-a)
+      when /x86_64/       then flags += %W(-march=x86-64)
+      when /x86/          then flags += %W(-march=i686)
+      when /mips64/       then flags += %W(-march=mips64r6)
+      when /mips/         then flags += %W(-march=mips32)
       end
     when :clang
+      case arch
+      when /armeabi-v7a/  then flags += %W(-target armv7-none-linux-androideabi)
+      when /armeabi/      then flags += %W(-target armv5te-none-linux-androideabi)
+      when /arm64-v8a/    then flags += %W(-target aarch64-none-linux-android)
+      when /x86_64/       then flags += %W(-target x86_64-none-linux-android)
+      when /x86/          then flags += %W(-target i686-none-linux-android)
+      when /mips64/       then flags += %W(-target mips64el-none-linux-android)
+      when /mips/         then flags += %W(-target mipsel-none-linux-android)
+      end
     end
 
-    flags
-  end
+    case arch
+    when /armeabi-v7a/  then flags += %W(-mfpu=#{armeabi_v7a_mfpu} -mfloat-abi=#{armeabi_v7a_mfloat_abi})
+    when /armeabi/      then flags += %W(-mtune=xscale -msoft-float)
+    when /arm64-v8a/    then flags += %W()
+    when /x86_64/       then flags += %W()
+    when /x86/          then flags += %W()
+    when /mips64/       then flags += %W(-fmessage-length=0)
+    when /mips/         then flags += %W(-fmessage-length=0)
+    end
 
-  def ld
-    cc
+    flags
   end
 
-  def ldflags
+  def cflags
     flags = []
+
+    flags += %W(-MMD -MP -D__android__ -DANDROID --sysroot="#{sysroot}")
+    flags += ctarget
     case toolchain
     when :gcc
-      flags += %W(-no-canonical-prefixes)
-      flags += %W(-D__android__ -mandroid --sysroot="#{sysroot}")
-      case arch
-      when 'armeabi-v7a-hard'
-        flags += %W(-march=armv7-a -Wl,--fix-cortex-a8 -Wl,--no-warn-mismatch -lm_hard)
-      when 'armeabi-v7a'
-        flags += %W(-march=armv7-a -Wl,--fix-cortex-a8)
-      end
+    when :clang
+      flags += %W(-gcc-toolchain "#{gcc_toolchain_path}" -Wno-invalid-command-line-argument -Wno-unused-command-line-argument)
     end
-    
+    flags += %W(-fpic -ffunction-sections -funwind-tables -fstack-protector-strong -no-canonical-prefixes)
+
     flags
   end
 
-  def ar
-    case toolchain
-    when :gcc   then bin(:ar)
-    when :clang then bin('llvm-ar')
-    end
+  def ldflags
+    flags = []
+
+    flags += %W(--sysroot="#{sysroot}")
+
+    flags
   end
 
-  def host_platform
-    case RUBY_PLATFORM
-    when /cygwin|mswin|mingw|bccwin|wince|emx/i
-      'windows'
-    when /x86_64-darwin/i
-      'darwin-x86_64'
-    when /darwin/i
-      'darwin-x86'
-    when /x86_64-linux/i
-      'linux-x86_64'
-    when /linux/i
-      'linux-x86'
-    else
-      raise NotImplementedError, "Unknown host platform (#{RUBY_PLATFORM})"
+  def ldflags_before_libraries
+    flags = []
+
+    case toolchain
+    when :gcc
+      case arch
+      when /armeabi-v7a/  then flags += %W(-Wl#{no_warn_mismatch})
+      end
+    when :clang
+      flags += %W(-gcc-toolchain "#{gcc_toolchain_path.to_s}")
+      case arch
+      when /armeabi-v7a/  then flags += %W(-target armv7-none-linux-androideabi -Wl,--fix-cortex-a8#{no_warn_mismatch})
+      when /armeabi/      then flags += %W(-target armv5te-none-linux-androideabi)
+      when /arm64-v8a/    then flags += %W(-target aarch64-none-linux-android)
+      when /x86_64/       then flags += %W(-target x86_64-none-linux-android)
+      when /x86/          then flags += %W(-target i686-none-linux-android)
+      when /mips64/       then flags += %W(-target mips64el-none-linux-android)
+      when /mips/         then flags += %W(-target mipsel-none-linux-android)
+      end
     end
+    flags += %W(-no-canonical-prefixes)
+
+    flags
   end
 end
 
 MRuby::Toolchain.new(:android) do |conf, params|
-  ndk = MRuby::Toolchain::Android.new(params)
+  android = MRuby::Toolchain::Android.new(params)
 
-  toolchain ndk.toolchain
+  toolchain android.toolchain
 
   [conf.cc, conf.cxx, conf.objc, conf.asm].each do |cc|
-    cc.command = ndk.cc
-    cc.flags = ndk.cflags
+    cc.command = android.cc
+    cc.flags = android.cflags
   end
-  conf.linker.command = ndk.ld
-  conf.linker.flags = ndk.ldflags
-  conf.archiver.command = ndk.ar
-end
 
+  conf.archiver.command = android.ar
+  conf.linker.command = android.cc
+  conf.linker.flags = android.ldflags
+  conf.linker.flags_before_libraries = android.ldflags_before_libraries
+end
index ea92b51..c75fa03 100644 (file)
@@ -1,4 +1,4 @@
-MRuby::Toolchain.new(:clang) do |conf|
+MRuby::Toolchain.new(:clang) do |conf, _params|
   toolchain :gcc
 
   [conf.cc, conf.objc, conf.asm].each do |cc|
index fb14558..f370c0a 100644 (file)
@@ -1,4 +1,4 @@
-MRuby::Toolchain.new(:gcc) do |conf|
+MRuby::Toolchain.new(:gcc) do |conf, _params|
   [conf.cc, conf.objc, conf.asm].each do |cc|
     cc.command = ENV['CC'] || 'gcc'
     cc.flags = [ENV['CFLAGS'] || %w(-g -std=gnu99 -O3 -Wall -Werror-implicit-function-declaration -Wdeclaration-after-statement -Wwrite-strings)]
@@ -6,6 +6,8 @@ MRuby::Toolchain.new(:gcc) do |conf|
     cc.option_include_path = '-I%s'
     cc.option_define = '-D%s'
     cc.compile_options = '%{flags} -MMD -o %{outfile} -c %{infile}'
+    cc.cxx_compile_flag = '-x c++ -std=c++03'
+    cc.cxx_exception_flag = '-fexceptions'
   end
 
   [conf.cxx].each do |cxx|
@@ -15,6 +17,8 @@ MRuby::Toolchain.new(:gcc) do |conf|
     cxx.option_include_path = '-I%s'
     cxx.option_define = '-D%s'
     cxx.compile_options = '%{flags} -MMD -o %{outfile} -c %{infile}'
+    cxx.cxx_compile_flag = '-x c++ -std=c++03'
+    cxx.cxx_exception_flag = '-fexceptions'
   end
 
   conf.linker do |linker|
diff --git a/third-party/mruby/tasks/toolchains/openwrt.rake b/third-party/mruby/tasks/toolchains/openwrt.rake
new file mode 100644 (file)
index 0000000..1637f6d
--- /dev/null
@@ -0,0 +1,38 @@
+# usage of environmental variables to set the
+# cross compiling toolchain proper
+MRuby::Toolchain.new(:openwrt) do |conf|
+  [conf.cc, conf.objc, conf.asm].each do |cc|
+    cc.command = ENV['TARGET_CC']
+    cc.flags = ENV['TARGET_CFLAGS']
+    cc.include_paths = ["#{MRUBY_ROOT}/include"]
+    cc.defines = %w(DISABLE_GEMS)
+    cc.option_include_path = '-I%s'
+    cc.option_define = '-D%s'
+    cc.compile_options = '%{flags} -MMD -o %{outfile} -c %{infile}'
+  end
+
+  [conf.cxx].each do |cxx|
+    cxx.command = ENV['TARGET_CXX']
+    cxx.flags = ENV['TARGET_CXXFLAGS']
+    cxx.include_paths = ["#{MRUBY_ROOT}/include"]
+    cxx.defines = %w(DISABLE_GEMS)
+    cxx.option_include_path = '-I%s'
+    cxx.option_define = '-D%s'
+    cxx.compile_options = '%{flags} -MMD -o %{outfile} -c %{infile}'
+   end
+
+  conf.linker do |linker|
+    linker.command = ENV['TARGET_CC']
+    linker.flags = ENV['TARGET_LDFLAGS']
+    linker.libraries = %w(m)
+    linker.library_paths = []
+    linker.option_library = '-l%s'
+    linker.option_library_path = '-L%s'
+    linker.link_options = '%{flags} -o %{outfile} %{objs} %{flags_before_libraries} %{libs} %{flags_after_libraries}'
+  end
+
+  conf.archiver do |archiver|
+    archiver.command = ENV['TARGET_AR']
+    archiver.archive_options = 'rs %{outfile} %{objs}'
+  end
+end
index eaf0130..5bc24a7 100644 (file)
@@ -1,5 +1,5 @@
-MRuby::Toolchain.new(:visualcpp) do |conf|
-  [conf.cc].each do |cc|
+MRuby::Toolchain.new(:visualcpp) do |conf, _params|
+  conf.cc do |cc|
     cc.command = ENV['CC'] || 'cl.exe'
     # C4013: implicit function declaration
     cc.flags = [ENV['CFLAGS'] || %w(/c /nologo /W3 /we4013 /Zi /MD /O2 /D_CRT_SECURE_NO_WARNINGS)]
@@ -7,15 +7,19 @@ MRuby::Toolchain.new(:visualcpp) do |conf|
     cc.option_include_path = '/I%s'
     cc.option_define = '/D%s'
     cc.compile_options = "%{flags} /Fo%{outfile} %{infile}"
+    cc.cxx_compile_flag = '/TP'
+    cc.cxx_exception_flag = '/EHs'
   end
 
-  [conf.cxx].each do |cxx|
+  conf.cxx do |cxx|
     cxx.command = ENV['CXX'] || 'cl.exe'
     cxx.flags = [ENV['CXXFLAGS'] || ENV['CFLAGS'] || %w(/c /nologo /W3 /Zi /MD /O2 /EHs /D_CRT_SECURE_NO_WARNINGS)]
     cxx.defines = %w(DISABLE_GEMS MRB_STACK_EXTEND_DOUBLING)
     cxx.option_include_path = '/I%s'
     cxx.option_define = '/D%s'
     cxx.compile_options = "%{flags} /Fo%{outfile} %{infile}"
+    cxx.cxx_compile_flag = '/TP'
+    cxx.cxx_exception_flag = '/EHs'
   end
 
   conf.linker do |linker|
@@ -50,4 +54,15 @@ MRuby::Toolchain.new(:visualcpp) do |conf|
   end
 
   conf.file_separator = '\\'
+
+  if require 'open3'
+    Open3.popen3 conf.cc.command do |_, _, e, _|
+      if /Version (\d{2})\.\d{2}\.\d{5}/ =~ e.gets && $1.to_i <= 17
+        m = "# VS2010/2012 support will be dropped after the next release! #"
+        h = "#" * m.length
+        puts h, m, h
+      end
+    end
+  end
+
 end
index 6fad58f..8ab9db2 100644 (file)
@@ -10,11 +10,7 @@ def t_print(*args)
   len = args.size
   while i < len
     str = args[i].to_s
-    begin
-      __printstr__ str
-    rescue NoMethodError
-      __t_printstr__ str rescue print str
-    end
+    __t_printstr__ str rescue print str
     i += 1
   end
 end
@@ -27,8 +23,8 @@ def assertion_string(err, str, iso=nil, e=nil, bt=nil)
   msg += " => #{e.message}" if e
   msg += " (mrbgems: #{GEMNAME})" if Object.const_defined?(:GEMNAME)
   if $mrbtest_assert && $mrbtest_assert.size > 0
-    $mrbtest_assert.each do |idx, str, diff|
-      msg += "\n - Assertion[#{idx}] Failed: #{str}\n#{diff}"
+    $mrbtest_assert.each do |idx, assert_msg, diff|
+      msg += "\n - Assertion[#{idx}] Failed: #{assert_msg}\n#{diff}"
     end
   end
   msg += "\nbacktrace:\n\t#{bt.join("\n\t")}" if bt
@@ -48,7 +44,8 @@ def assert(str = 'Assertion failed', iso = '')
   begin
     $mrbtest_assert = []
     $mrbtest_assert_idx = 0
-    if(!yield || $mrbtest_assert.size > 0)
+    yield
+    if($mrbtest_assert.size > 0)
       $asserts.push(assertion_string('Fail: ', str, iso, nil))
       $ko_test += 1
       t_print('F')
@@ -65,7 +62,7 @@ def assert(str = 'Assertion failed', iso = '')
       $asserts.push(assertion_string("#{e.class}: ", str, iso, e, bt))
       $kill_test += 1
       t_print('X')
-  end
+    end
   ensure
     $mrbtest_assert = nil
   end
@@ -160,7 +157,7 @@ def assert_raise(*exp)
       msg = "#{msg}#{exp.inspect} exception expected, not"
       diff = "      Class: <#{e.class}>\n" +
              "    Message: #{e.message}"
-      if not exp.any?{|ex| ex.instance_of?(Module) ? e.kind_of?(ex) : ex == e.class }
+      unless exp.any?{|ex| ex.instance_of?(Module) ? e.kind_of?(ex) : ex == e.class }
         $mrbtest_assert.push([$mrbtest_assert_idx, msg, diff])
         ret = false
       end
@@ -217,7 +214,7 @@ def report()
   t_print("\n")
 
   $asserts.each do |msg|
-    puts msg
+    t_print "#{msg}\n"
   end
 
   $total_test = $ok_test+$ko_test+$kill_test
@@ -236,7 +233,7 @@ end
 ##
 # Performs fuzzy check for equality on methods returning floats
 def check_float(a, b)
-  tolerance = 1e-12
+  tolerance = Mrbtest::FLOAT_TOLERANCE
   a = a.to_f
   b = b.to_f
   if a.finite? and b.finite?
index 0ef0b41..12971a9 100644 (file)
@@ -3,7 +3,7 @@ require 'test/assert.rb'
 
 def cmd(s)
   case RbConfig::CONFIG['host_os']
-  when /mswin(?!ce)|mingw|cygwin|bccwin/
+  when /mswin(?!ce)|mingw|bccwin/
     "bin\\#{s}.exe"
   else
     "bin/#{s}"
@@ -12,7 +12,7 @@ end
 
 def shellquote(s)
   case RbConfig::CONFIG['host_os']
-  when /mswin(?!ce)|mingw|cygwin|bccwin/
+  when /mswin(?!ce)|mingw|bccwin/
     "\"#{s}\""
   else
     "'#{s}'"
@@ -20,6 +20,11 @@ def shellquote(s)
 end
 
 ARGV.each do |gem|
+  case RbConfig::CONFIG['host_os']
+  when /mswin(?!ce)|mingw|bccwin/
+    gem = gem.gsub('\\', '/')
+  end
+
   Dir["#{gem}/bintest/**/*.rb"].each do |file|
     load file
   end
index 538ea0c..7c11265 100644 (file)
@@ -13,6 +13,14 @@ assert('Array.[]', '15.2.12.4.1') do
   assert_equal([1, 2, 3], Array.[](1,2,3))
 end
 
+class SubArray < Array
+end
+
+assert('SubArray.[]') do
+  a = SubArray[1, 2, 3]
+  assert_equal(SubArray, a.class)
+end
+
 assert('Array#+', '15.2.12.5.1') do
   assert_equal([1, 1], [1].+([1]))
 end
@@ -82,6 +90,14 @@ assert('Array#[]=', '15.2.12.5.5') do
   a = [1,2,3,4,5]
   a[2...4] = 6
   assert_equal([1,2,6,5], a)
+
+  # passing self (#3274)
+  a = [1,2,3]
+  a[1,0] = a
+  assert_equal([1,1,2,3,2,3], a)
+  a = [1,2,3]
+  a[-1,0] = a
+  assert_equal([1,2,1,2,3,3], a)
 end
 
 assert('Array#clear', '15.2.12.5.6') do
@@ -98,6 +114,11 @@ end
 
 assert('Array#concat', '15.2.12.5.8') do
   assert_equal([1,2,3,4], [1, 2].concat([3, 4]))
+
+  # passing self (#3302)
+  a = [1,2,3]
+  a.concat(a)
+  assert_equal([1,2,3,1,2,3], a)
 end
 
 assert('Array#delete_at', '15.2.12.5.9') do
@@ -216,6 +237,8 @@ assert('Array#pop', '15.2.12.5.21') do
   assert_nil([].pop)
   assert_equal([1,2], a)
   assert_equal(3, b)
+
+  assert_raise(RuntimeError) { [].freeze.pop }
 end
 
 assert('Array#push', '15.2.12.5.22') do
@@ -263,6 +286,8 @@ assert('Array#shift', '15.2.12.5.27') do
   assert_nil([].shift)
   assert_equal([2,3], a)
   assert_equal(1, b)
+
+  assert_raise(RuntimeError) { [].freeze.shift }
 end
 
 assert('Array#size', '15.2.12.5.28') do
@@ -318,7 +343,8 @@ end
 assert('Array#hash', '15.2.12.5.35') do
   a = [ 1, 2, 3 ]
 
-  assert_true(a.hash.is_a? Integer)
+  #assert_true(a.hash.is_a? Integer)
+  assert_true(a.hash.is_a? Integral)  # mruby special
   assert_equal([1,2].hash, [1,2].hash)
 end
 
@@ -347,3 +373,22 @@ assert("Array (Longish inline array)") do
   ary.each {|p| h[p.class] += 1}
   assert_equal({Array=>200}, h)
 end
+
+assert("Array#rindex") do
+  class Sneaky
+    def ==(*)
+      $a.clear
+      $a.replace([1])
+      false
+    end
+  end
+  $a = [2, 3, 4, 5, 6, 7, 8, 9, 10, Sneaky.new]
+  assert_equal 0, $a.rindex(1)
+end
+
+assert('Array#freeze') do
+  a = [].freeze
+  assert_raise(RuntimeError) do
+    a[0] = 1
+  end
+end
index 720fd37..eb077fc 100644 (file)
@@ -384,7 +384,68 @@ assert('class variable and class << self style class method') do
   assert_equal("value", ClassVariableTest.class_variable)
 end
 
+assert('class variable definition in singleton_class') do
+  class ClassVariableDefinitionInSingletonTest
+    class << self
+      @@class_variable = "value"
+    end
+    def class_variable
+      @@class_variable
+    end
+  end
+
+  assert_equal("value", ClassVariableDefinitionInSingletonTest.new.class_variable)
+end
+
+assert('class variable in module and class << self style class method') do
+  module ClassVariableInModuleTest
+    @@class_variable = "value"
+    class << self
+      def class_variable
+        @@class_variable
+      end
+    end
+  end
+
+  assert_equal("value", ClassVariableInModuleTest.class_variable)
+end
+
+assert('child class/module defined in singleton class get parent constant') do
+  actual = module GetParentConstantTest
+            EXPECT = "value"
+            class << self
+              class CHILD
+                class << self
+                    EXPECT
+                end
+              end
+            end
+          end
+  assert_equal("value", actual)
+end
+
+assert('overriding class variable with a module (#3235)') do
+  module ModuleWithCVar
+    @@class_variable = 1
+  end
+  class CVarOverrideTest
+    @@class_variable = 2
+    include ModuleWithCVar
+
+    assert_equal(1, @@class_variable)
+  end
+end
+
 assert('class with non-class/module outer raises TypeError') do
   assert_raise(TypeError) { class 0::C1; end }
   assert_raise(TypeError) { class []::C2; end }
 end
+
+assert("remove_method doesn't segfault if the passed in argument isn't a symbol") do
+  klass = Class.new
+  assert_raise(TypeError) { klass.remove_method nil }
+  assert_raise(TypeError) { klass.remove_method 123 }
+  assert_raise(TypeError) { klass.remove_method 1.23 }
+  assert_raise(NameError) { klass.remove_method "hello" }
+  assert_raise(TypeError) { klass.remove_method Class.new }
+end
diff --git a/third-party/mruby/test/t/codegen.rb b/third-party/mruby/test/t/codegen.rb
new file mode 100644 (file)
index 0000000..29f5ad5
--- /dev/null
@@ -0,0 +1,75 @@
+##
+# Codegen tests
+
+assert('peephole optimization does not eliminate move whose result is reused') do
+  assert_raise LocalJumpError do
+    def method
+      yield
+    end
+    method(&a &&= 0)
+  end
+end
+
+assert('empty condition in ternary expression parses correctly') do
+  assert_equal(() ? 1 : 2, 2)
+end
+
+assert('method call with exactly 127 arguments') do
+  def args_to_ary(*args)
+    args
+  end
+
+  assert_equal [0]*127, args_to_ary(
+    0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, \
+    0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, \
+    0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, \
+    0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, \
+    0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, \
+    0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
+  )
+end
+
+assert('nested empty heredoc') do
+  _, a = nil, <<B
+#{<<A}
+A
+B
+  assert_equal "\n", a
+end
+
+assert('splat in case splat') do
+  a = *case
+    when 0
+      * = 1
+  end
+
+  assert_equal [1], a
+end
+
+assert('undef with 127 or more arguments') do
+  assert_raise NameError do
+    undef
+      a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a,
+      a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a,
+      a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a,
+      a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a
+  end
+end
+
+assert('next in normal loop with 127 arguments') do
+  assert_raise NameError do
+    while true
+      next A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A
+    end
+  end
+end
+
+assert('negate literal register alignment') do
+  a = *case
+  when 0
+    -0.0
+    2
+  end
+
+  assert_equal [2], a
+end
index d278130..ce7b584 100644 (file)
@@ -338,10 +338,13 @@ assert('Exception 19') do
       begin
         1 * "b"
       ensure
-        @e = self.z
+        @e = self.zz
       end
     end
 
+    def zz
+      true
+    end
     def z
       true
     end
@@ -350,7 +353,7 @@ assert('Exception 19') do
 end
 
 assert('Exception#inspect without message') do
-  assert_equal "Exception: Exception", Exception.new.inspect
+  assert_equal "Exception", Exception.new.inspect
 end
 
 assert('Exception#backtrace') do
@@ -373,12 +376,47 @@ assert('Raise in ensure') do
   end
 end
 
-assert('Raise in rescue') do
-  assert_raise(ArgumentError) do
-    begin
-      raise "" # RuntimeError
-    rescue
-      raise ArgumentError
+def backtrace_available?
+  begin
+    raise "XXX"
+  rescue => exception
+    not exception.backtrace.empty?
+  end
+end
+
+assert('GC in rescue') do
+  skip "backtrace isn't available" unless backtrace_available?
+
+  line = nil
+  begin
+    [1].each do
+      [2].each do
+        [3].each do
+          line = __LINE__; raise "XXX"
+        end
+      end
+    end
+  rescue => exception
+    GC.start
+    assert_equal("#{__FILE__}:#{line}:in call",
+                 exception.backtrace.first)
+  end
+end
+
+assert('Method call in rescue') do
+  skip "backtrace isn't available" unless backtrace_available?
+
+  line = nil
+  begin
+    [1].each do
+      [2].each do
+        line = __LINE__; raise "XXX"
+      end
+    end
+  rescue => exception
+    [3].each do
     end
+    assert_equal("#{__FILE__}:#{line}:in call",
+                 exception.backtrace.first)
   end
 end
index 1805c6e..7e8c989 100644 (file)
@@ -148,6 +148,9 @@ end
 
 assert('Float#to_i', '15.2.9.3.14') do
   assert_equal(3, 3.123456789.to_i)
+  assert_raise(FloatDomainError) { Float::INFINITY.to_i }
+  assert_raise(FloatDomainError) { (-Float::INFINITY).to_i }
+  assert_raise(FloatDomainError) { Float::NAN.to_i }
 end
 
 assert('Float#truncate', '15.2.9.3.15') do
index 3196cc9..c63b8c0 100644 (file)
@@ -16,6 +16,13 @@ assert('Hash#[]', '15.2.13.4.2') do
   a = { 'abc' => 'abc' }
 
   assert_equal 'abc', a['abc']
+
+  # Hash#[] should call #default (#3272)
+  hash = {}
+  def hash.default(k); self[k] = 1; end
+  hash[:foo] += 1
+
+  assert_equal 2, hash[:foo]
 end
 
 assert('Hash#[]=', '15.2.13.4.3') do
@@ -37,6 +44,10 @@ assert('Hash#dup') do
   b = a.dup
   a['a'] = 2
   assert_equal({'a' => 1}, b)
+
+  c = Hash.new { |h, k| h[k] = k.upcase }
+  d = c.dup
+  assert_equal("FOO", d["foo"])
 end
 
 assert('Hash#default', '15.2.13.4.5') do
@@ -239,6 +250,10 @@ assert('Hash#replace', '15.2.13.4.23') do
   a = Hash.new{|h,x| x}
   b.replace(a)
   assert_equal(127, b[127])
+
+   assert_raise(TypeError) do
+    { 'abc_key' => 'abc_value' }.replace "a"
+  end
 end
 
 assert('Hash#shift', '15.2.13.4.24') do
@@ -351,3 +366,10 @@ assert('Hash#rehash') do
   h.rehash
   assert_equal("b", h[[:b]])
 end
+
+assert('Hash#freeze') do
+  h = {}.freeze
+  assert_raise(RuntimeError) do
+    h[:a] = 'b'
+  end
+end
index be3c13d..d4ef8b5 100644 (file)
@@ -147,6 +147,12 @@ assert('Integer#<<', '15.2.8.3.12') do
 
   # Left Shift by a negative is Right Shift
   assert_equal 23, 46 << -1
+
+  # Left Shift by 31 is bitShift overflow to SignedInt
+  assert_equal 2147483648, 1 << 31
+
+  # -3 Left Shift by 30 is bitShift overflow to SignedInt
+  assert_equal(-3221225472, -3 << 30)
 end
 
 assert('Integer#>>', '15.2.8.3.13') do
diff --git a/third-party/mruby/test/t/iterations.rb b/third-party/mruby/test/t/iterations.rb
new file mode 100644 (file)
index 0000000..f227a60
--- /dev/null
@@ -0,0 +1,61 @@
+assert('while expression', '11.5.2.3.2') do
+  idx = 10
+  all = []
+  res = while idx > 0
+    all << idx
+    idx -= 1
+  end
+
+  assert_equal nil, res
+  assert_equal [10,9,8,7,6,5,4,3,2,1], all
+end
+
+assert('until expression', '11.5.2.3.3') do
+  idx = 10
+  all = []
+  res = until idx == 0
+    all << idx
+    idx -= 1
+  end
+
+  assert_equal nil, res
+  assert_equal [10,9,8,7,6,5,4,3,2,1], all
+end
+
+assert('break expression', '11.5.2.4.3') do
+  assert_equal :result do
+    while true
+      break :result
+    end
+  end
+
+  assert_equal :result do
+    until false
+      break :result
+    end
+  end
+end
+
+assert('next expression', '11.5.2.4.4') do
+  assert_equal [8,6,4,2,0] do
+    all = []
+    idx = 10
+    while idx > 0
+      idx -= 1
+      next if (idx % 2) == 1
+      all << idx
+    end
+    all
+  end
+
+  assert_equal [8,6,4,2,0] do
+    all = []
+    idx = 10
+    until idx == 0
+      idx -= 1
+      next if (idx % 2) == 1
+      all << idx
+    end
+    all
+  end
+end
\ No newline at end of file
index 6df2294..40a3482 100644 (file)
@@ -221,6 +221,14 @@ assert('Kernel#dup', '15.3.1.3.9') do
   assert_false c.respond_to?(:test)
 end
 
+assert('Kernel#dup class') do
+  assert_nothing_raised do
+    Array.dup.new(200)
+    Range.dup.new(2, 3)
+    String.dup.new("a"*50)
+  end
+end
+
 # Kernel#eval is provided by mruby-eval mrbgem '15.3.1.3.12'
 
 assert('Kernel#extend', '15.3.1.3.13') do
@@ -249,6 +257,13 @@ assert('Kernel#extend works on toplevel', '15.3.1.3.13') do
   assert_true respond_to?(:test_method)
 end
 
+assert('Kernel#freeze') do
+  obj = Object.new
+  assert_equal obj, obj.freeze
+  assert_equal 0, 0.freeze
+  assert_equal :a, :a.freeze
+end
+
 assert('Kernel#global_variables', '15.3.1.3.14') do
   assert_equal Array, global_variables.class
 end
@@ -372,7 +387,7 @@ assert('Kernel#method_missing', '15.3.1.3.30') do
   begin
     c.no_method_named_this
   rescue NoMethodError => e
-    assert_equal "undefined method 'no_method_named_this' for #{c.to_s}", e.message
+    assert_equal "undefined method 'no_method_named_this' for #{c}", e.message
   end
 
   class NoInspectClass
@@ -382,7 +397,7 @@ assert('Kernel#method_missing', '15.3.1.3.30') do
   begin
     d.no_method_named_this
   rescue NoMethodError => e
-    assert_equal "undefined method 'no_method_named_this' for #{d.to_s}", e.message
+    assert_equal "undefined method 'no_method_named_this' for #{d}", e.message
   end
 end
 
@@ -423,6 +438,11 @@ end
 
 assert('Kernel#public_methods', '15.3.1.3.38') do
   assert_equal Array, public_methods.class
+  class Foo
+    def foo
+    end
+  end
+  assert_equal [:foo], Foo.new.public_methods(false)
 end
 
 # Kernel#puts is defined in mruby-print mrbgem. '15.3.1.3.39'
@@ -507,6 +527,21 @@ assert('Kernel#to_s', '15.3.1.3.46') do
   assert_equal to_s.class, String
 end
 
+assert('Kernel#to_s on primitives') do
+  begin
+    Fixnum.alias_method :to_s_, :to_s
+    Fixnum.remove_method :to_s
+
+    assert_nothing_raised do
+      # segfaults if mrb_cptr is used
+      1.to_s
+    end
+  ensure
+    Fixnum.alias_method :to_s, :to_s_
+    Fixnum.remove_method :to_s_
+  end
+end
+
 assert('Kernel.local_variables', '15.3.1.2.7') do
   a, b = 0, 1
   a += b
@@ -514,11 +549,10 @@ assert('Kernel.local_variables', '15.3.1.2.7') do
   vars = Kernel.local_variables.sort
   assert_equal [:a, :b, :vars], vars
 
-  Proc.new {
+  assert_equal [:a, :b, :c, :vars], Proc.new { |a, b|
     c = 2
-    vars = Kernel.local_variables.sort
-    assert_equal [:a, :b, :c, :vars], vars
-  }.call
+    Kernel.local_variables.sort
+  }.call(-1, -2)
 end
 
 assert('Kernel#!=') do
@@ -587,4 +621,3 @@ assert('stack extend') do
 
   assert_equal 6, recurse(0, 5)
 end
-
diff --git a/third-party/mruby/test/t/lang.rb b/third-party/mruby/test/t/lang.rb
new file mode 100755 (executable)
index 0000000..0d70bb3
--- /dev/null
@@ -0,0 +1,74 @@
+# The aim of these tests is  to detect pitfall for optimized VM.
+
+# Test for or/and
+#
+# You may think instruction fusion(OP_EQ and OP_JMPIF) for avoiding
+# generate intermediate boolean value.
+# But and/or is pitfall for this fusioning.
+#
+# For example, the following mruby code:
+#
+#   if i > 0 and i < 10 then
+#
+# compiles to the following byte code:
+#
+#    1 000 OP_LOADI      R1      0               ; R1:i
+#    2 001 OP_MOVE       R2      R1              ; R1:i
+#    2 002 OP_LOADI      R3      0
+#    2 003 OP_GT R2      :>      1
+#    2 004 OP_JMPNOT     R2      008
+#    2 005 OP_MOVE       R2      R1              ; R1:i
+#    2 006 OP_LOADI      R3      10
+#    2 007 OP_LT R2      :<      1
+#    2 008 OP_JMPNOT     R2      (The address of end of then part)
+#
+# When the instruction fusion the OP_GT and OP_JMPNOT you fell into the pitfalls.
+# The deleted intermediate boolean value is used in OP_JMPNOT (address 008).
+
+assert('and', '11.2.3') do
+  a = 1
+  if a > 0 and a < 10 then
+    b = 1
+  else
+    b = 0
+  end
+  assert_equal 1, b
+
+  if a < 0 and a < 10 then
+    b = 1
+  else
+    b = 0
+  end
+  assert_equal 0, b
+
+  if a < 0 and a > 10 then
+    b = 1
+  else
+    b = 0
+  end
+  assert_equal 0, b
+end
+
+assert('or','11.2.4') do
+  a = 1
+  if a > 0 or a < 10 then
+    b = 1
+  else
+    b = 0
+  end
+  assert_equal 1, b
+
+  if a < 0 or a < 10 then
+    b = 1
+  else
+    b = 0
+  end
+  assert_equal 1, b
+
+  if a < 0 or a > 10 then
+    b = 1
+  else
+    b = 0
+  end
+  assert_equal 0, b
+end
index 5bf6d6e..51a37c3 100644 (file)
@@ -26,7 +26,7 @@ assert('Literals Numerical', '8.7.6.2') do
   assert_equal 10000000, 10_000_000
   assert_equal       10, 1_0
   # integer with exponent
-  assert_equal 10.0, 1e1,
+  assert_equal 10.0, 1e1
   assert_equal(0.1, 1e-1)
   assert_equal 10.0, 1e+1
   # float with exponent
index 4bde20f..419b0bf 100644 (file)
@@ -494,6 +494,18 @@ end
 
 # Not ISO specified
 
+assert('Module#define_method') do
+  c = Class.new {
+    define_method(:m1) { :ok }
+    define_method(:m2, Proc.new { :ok })
+  }
+  assert_equal c.new.m1, :ok
+  assert_equal c.new.m2, :ok
+  assert_raise(TypeError) do
+    Class.new { define_method(:n1, nil) }
+  end
+end
+
 # @!group prepend
   assert('Module#prepend') do
     module M0
@@ -775,10 +787,28 @@ end
 # @!endgroup prepend
 
 assert('Module#to_s') do
-  module Test4to_sModules
+  module Outer
+    class Inner; end
+    const_set :SetInner, Class.new
   end
 
-  assert_equal 'Test4to_sModules', Test4to_sModules.to_s
+  assert_equal 'Outer', Outer.to_s
+  assert_equal 'Outer::Inner', Outer::Inner.to_s
+  assert_equal 'Outer::SetInner', Outer::SetInner.to_s
+
+  outer = Module.new do
+    const_set :SetInner, Class.new
+  end
+  Object.const_set :SetOuter, outer
+
+  assert_equal 'SetOuter', SetOuter.to_s
+  assert_equal 'SetOuter::SetInner', SetOuter::SetInner.to_s
+
+  mod = Module.new
+  cls = Class.new
+
+  assert_equal "#<Module:0x", mod.to_s[0,11]
+  assert_equal "#<Class:0x", cls.to_s[0,10]
 end
 
 assert('Module#inspect') do
@@ -837,3 +867,14 @@ assert('module with non-class/module outer raises TypeError') do
   assert_raise(TypeError) { module 0::M1 end }
   assert_raise(TypeError) { module []::M2 end }
 end
+
+assert('get constant of parent module in singleton class; issue #3568') do
+  actual = module GetConstantInSingletonTest
+    EXPECTED = "value"
+    class << self
+      EXPECTED
+    end
+  end
+
+  assert_equal("value", actual)
+end
index 53b922f..b49878f 100644 (file)
@@ -32,3 +32,8 @@ end
 assert('NilClass#to_s', '15.2.4.3.5') do
   assert_equal '', nil.to_s
 end
+
+assert('safe navigation') do
+  assert_nil nil&.size
+  assert_equal 0, []&.size
+end
index 888b7d5..ef4566e 100644 (file)
@@ -136,6 +136,18 @@ assert('Proc#return_does_not_break_self') do
   assert_equal c, c.block.call
 end
 
+assert('call Proc#initialize if defined') do
+  a = []
+  c = Class.new(Proc) do
+    define_method(:initialize) do
+      a << :ok
+    end
+  end
+
+  assert_kind_of c, c.new{}
+  assert_equal [:ok], a
+end
+
 assert('&obj call to_proc if defined') do
   pr = Proc.new{}
   def mock(&b)
index 278b269..5391369 100644 (file)
@@ -43,10 +43,11 @@ assert('Range#first', '15.2.14.4.7') do
 end
 
 assert('Range#include?', '15.2.14.4.8') do
-  a = (1..10)
+  assert_true (1..10).include?(10)
+  assert_false (1..10).include?(11)
 
-  assert_true a.include?(5)
-  assert_false a.include?(20)
+  assert_true (1...10).include?(9)
+  assert_false (1...10).include?(10)
 end
 
 assert('Range#initialize', '15.2.14.4.9') do
@@ -57,6 +58,8 @@ assert('Range#initialize', '15.2.14.4.9') do
   assert_true a.exclude_end?
   assert_equal (1..10), b
   assert_false b.exclude_end?
+
+  assert_raise(NameError) { (0..1).send(:initialize, 1, 3) }
 end
 
 assert('Range#last', '15.2.14.4.10') do
index fbaada4..a413962 100644 (file)
@@ -251,6 +251,19 @@ assert('String#chomp!', '15.2.10.5.10') do
   assert_equal 'abc', e
 end
 
+assert('String#chomp! uses the correct length') do
+  class A
+    def to_str
+      $s.replace("AA")
+      "A"
+    end
+  end
+
+  $s = "AAA"
+  $s.chomp!(A.new)
+  assert_equal $s, "A"
+end
+
 assert('String#chop', '15.2.10.5.11') do
   a = ''.chop
   b = 'abc'.chop
@@ -328,6 +341,12 @@ assert('String#each_line', '15.2.10.5.15') do
   end
 
   assert_equal list, n_list
+
+  n_list.clear
+  a.each_line("li") do |line|
+    n_list << line
+  end
+  assert_equal ["first li", "ne\nsecond li", "ne\nthird li", "ne"], n_list
 end
 
 assert('String#empty?', '15.2.10.5.16') do
@@ -352,6 +371,13 @@ assert('String#gsub', '15.2.10.5.18') do
   assert_equal('$$a$$',  '##a##'.gsub('##'){|w| '$$' }, 'mruby/mruby#847 another case with block')
   assert_equal('A',      'a'.gsub('a', 'A'))
   assert_equal('A',      'a'.gsub('a'){|w| w.capitalize })
+  assert_equal("<a><><>", 'a'.gsub('a', '<\0><\1><\2>'))
+  assert_equal(".h.e.l.l.o.", "hello".gsub("", "."))
+  a = []
+  assert_equal(".h.e.l.l.o.", "hello".gsub("") { |i| a << i; "." })
+  assert_equal(["", "", "", "", "", ""], a)
+  assert_raise(ArgumentError) { "".gsub }
+  assert_raise(ArgumentError) { "".gsub("", "", "") }
 end
 
 assert('String#gsub with backslash') do
@@ -381,8 +407,6 @@ assert('String#hash', '15.2.10.5.20') do
 end
 
 assert('String#include?', '15.2.10.5.21') do
-  assert_true 'abc'.include?(97)
-  assert_false 'abc'.include?(100)
   assert_true 'abc'.include?('a')
   assert_false 'abc'.include?('d')
 end
@@ -391,6 +415,8 @@ assert('String#index', '15.2.10.5.22') do
   assert_equal 0, 'abc'.index('a')
   assert_nil 'abc'.index('d')
   assert_equal 3, 'abcabc'.index('a', 1)
+  assert_equal 5, "hello".index("", 5)
+  assert_equal nil, "hello".index("", 6)
 end
 
 assert('String#initialize', '15.2.10.5.23') do
@@ -559,6 +585,16 @@ assert('String#sub', '15.2.10.5.36') do
   assert_equal 'aBcabc', 'abcabc'.sub('b', 'B')
   assert_equal 'aBcabc', 'abcabc'.sub('b') { |w| w.capitalize }
   assert_equal 'aa$', 'aa#'.sub('#', '$')
+  assert_equal '.abc', "abc".sub("", ".")
+
+  str = "abc"
+  miss = str.sub("X", "Z")
+  assert_equal str, miss
+  assert_not_equal str.object_id, miss.object_id
+
+  a = []
+  assert_equal '.abc', "abc".sub("") { |i| a << i; "." }
+  assert_equal [""], a
 end
 
 assert('String#sub with backslash') do
@@ -585,10 +621,14 @@ assert('String#to_f', '15.2.10.5.38') do
   a = ''.to_f
   b = '123456789'.to_f
   c = '12345.6789'.to_f
+  d = '1e-2147483648'.to_f
+  e = '1e2147483648'.to_f
 
   assert_float(0.0, a)
   assert_float(123456789.0, b)
   assert_float(12345.6789, c)
+  assert_float(0, d)
+  assert_float(Float::INFINITY, e)
 end
 
 assert('String#to_i', '15.2.10.5.39') do
@@ -685,4 +725,3 @@ assert('String#freeze') do
 
   assert_raise(RuntimeError) { str.upcase! }
 end
-
index 9fd8830..10b6438 100644 (file)
@@ -23,7 +23,8 @@
   [:Exception, :Object, '15.2.22.2'],
   [:StandardError, :Exception, '15.2.23.2'],
   [:ArgumentError, :StandardError, '15.2.24.2'],
-  [:LocalJumpError, :StandardError, '15.2.25.2'],
+  # [:LocalJumpError, :StandardError, '15.2.25.2'],
+  [:LocalJumpError, :ScriptError, '15.2.25.2'], # mruby specific
   [:RangeError, :StandardError, '12.2.26.2'],
   [:RegexpError, :StandardError, '12.2.27.2'],
   [:RuntimeError, :StandardError, '12.2.28.2'],
index b025284..9059f45 100644 (file)
@@ -1,6 +1,13 @@
 ##
 # Symbol ISO Test
 
+assert('Symbol') do
+  assert_equal :"a", :a
+  assert_equal :"a#{1}", :a1
+  assert_equal :'a', :a
+  assert_equal :'a#{1}', :"a\#{1}"
+end
+
 assert('Symbol', '15.2.11') do
   assert_equal Class, Symbol.class
 end
index 4ba171f..2993945 100644 (file)
@@ -38,6 +38,26 @@ assert('yield', '11.3.5') do
   assert_raise LocalJumpError do
     yield
   end
+  assert_raise LocalJumpError do
+    o = Object.new
+    def o.foo
+      yield
+    end
+    o.foo
+  end
+end
+
+assert('redo in a for loop (#3275)') do
+  sum = 0
+  for i in 1..10
+    sum += i
+    i -= 1
+    if i > 0
+      redo
+    end
+  end
+
+  assert_equal 220, sum
 end
 
 assert('Abbreviated variable assignment', '11.4.2.3.2') do
@@ -248,6 +268,13 @@ assert('multiple assignment (nosplat array rhs)') do
   assert_equal 2, g
 end
 
+assert('multiple assignment (empty array rhs #3236, #3239)') do
+  a,b,*c = []; assert_equal [nil, nil, []], [a, b, c]
+  a,b,*c = [1]; assert_equal [1, nil, []], [a, b, c]
+  a,b,*c = [nil]; assert_equal [nil,nil, []], [a, b, c]
+  a,b,*c = [[]]; assert_equal [[], nil, []], [a, b, c]
+end
+
 assert('Return values of case statements') do
   a = [] << case 1
   when 3 then 2
@@ -300,6 +327,36 @@ assert('Return values of no expression case statement') do
   assert_equal 1, when_value
 end
 
+assert('splat object in assignment') do
+  o = Object.new
+  def o.to_a
+    nil
+  end
+  assert_equal [o], (a = *o)
+
+  def o.to_a
+    1
+  end
+  assert_raise(TypeError) { a = *o }
+
+  def o.to_a
+    [2]
+  end
+  assert_equal [2], (a = *o)
+end
+
+assert('splat object in case statement') do
+  o = Object.new
+  def o.to_a
+    nil
+  end
+  a = case o
+  when *o
+    1
+  end
+  assert_equal 1, a
+end
+
 assert('splat in case statement') do
   values = [3,5,1,7,8]
   testa = [1,2,7]
@@ -324,7 +381,7 @@ assert('splat in case statement') do
 end
 
 assert('External command execution.') do
-  class << Kernel
+  module Kernel
     sym = '`'.to_sym
     alias_method :old_cmd, sym
 
index 7edd65e..8622ae0 100644 (file)
@@ -2,34 +2,38 @@
 
 assert('bare \u notation test') do
   # Mininum and maximum one byte characters
-  assert_equal("\u0000", "\x00")
-  assert_equal("\u007F", "\x7F")
+  assert_equal("\x00", "\u0000")
+  assert_equal("\x7F", "\u007F")
 
   # Mininum and maximum two byte characters
-  assert_equal("\u0080", "\xC2\x80")
-  assert_equal("\u07FF", "\xDF\xBF")
+  assert_equal("\xC2\x80", "\u0080")
+  assert_equal("\xDF\xBF", "\u07FF")
 
   # Mininum and maximum three byte characters
-  assert_equal("\u0800", "\xE0\xA0\x80")
-  assert_equal("\uFFFF", "\xEF\xBF\xBF")
+  assert_equal("\xE0\xA0\x80", "\u0800")
+  assert_equal("\xEF\xBF\xBF", "\uFFFF")
 
   # Four byte characters require the \U notation
 end
 
 assert('braced \u notation test') do
   # Mininum and maximum one byte characters
-  assert_equal("\u{0000}", "\x00")
-  assert_equal("\u{007F}", "\x7F")
+  assert_equal("\x00", "\u{0000}")
+  assert_equal("\x7F", "\u{007F}")
 
   # Mininum and maximum two byte characters
-  assert_equal("\u{0080}", "\xC2\x80")
-  assert_equal("\u{07FF}", "\xDF\xBF")
+  assert_equal("\xC2\x80", "\u{0080}")
+  assert_equal("\xDF\xBF", "\u{07FF}")
 
   # Mininum and maximum three byte characters
-  assert_equal("\u{0800}", "\xE0\xA0\x80")
-  assert_equal("\u{FFFF}", "\xEF\xBF\xBF")
+  assert_equal("\xE0\xA0\x80", "\u{0800}")
+  assert_equal("\xEF\xBF\xBF", "\u{FFFF}")
 
   # Mininum and maximum four byte characters
-  assert_equal("\u{10000}",  "\xF0\x90\x80\x80")
-  assert_equal("\u{10FFFF}", "\xF4\x8F\xBF\xBF")
+  assert_equal("\xF0\x90\x80\x80", "\u{10000}")
+  assert_equal("\xF4\x8F\xBF\xBF", "\u{10FFFF}")
+end
+
+assert('braced multiple \u notation test') do
+  assert_equal("ABC", "\u{41 42 43}")
 end
index c1d065b..29b35a9 100644 (file)
 #endif
 #include "neverbleed.h"
 
-#define OPENSSL_1_1_API (!defined(LIBRESSL_VERSION_NUMBER) && OPENSSL_VERSION_NUMBER >= 0x1010000fL)
+#if (!defined(LIBRESSL_VERSION_NUMBER) && OPENSSL_VERSION_NUMBER >= 0x1010000fL)
+#define OPENSSL_1_1_API 1
+#else
+#define OPENSSL_1_1_API 0
+#endif
 
 enum neverbleed_type { NEVERBLEED_TYPE_ERROR, NEVERBLEED_TYPE_RSA, NEVERBLEED_TYPE_ECDSA };