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
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
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
es
fangdingjun
kumagi
+lstefani
makovich
mod-h2-dev
moparisthebest
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}")
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})
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()
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?
-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
#
# $ 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 \
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 \
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 \
--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` \
--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
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
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
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" \
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"
./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
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.
-# 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,
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@
! -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
@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
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*) \
*.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
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.
* 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)
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:
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
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::
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)
------------------------------------
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
--------------------------
* 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:
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
---------------------------------
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.
``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``.
.. 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.
.. 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)
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:
-# 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,
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,
[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
])
# 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,
# 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,
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,
# 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,
# 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,
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,
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,
# 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,
# 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,
# 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,
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,
# 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,
[
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])
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,
# 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,
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,
_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,
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,
# 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,
-
-/* 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@
/* 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
#! /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
# 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
# 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>.
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
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."
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 ;;
*:Sortix:*:*)
echo ${UNAME_MACHINE}-unknown-sortix
exit ;;
+ *:Redox:*:*)
+ echo ${UNAME_MACHINE}-unknown-redox
+ exit ;;
alpha:OSF1:*:*)
case $UNAME_RELEASE in
*4.0)
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 ;;
#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);
*: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
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`
{ 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:*:*)
*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:*:*)
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
*: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 ;;
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 ;;
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}
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
*: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 ;;
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
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: "'"
/* 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
#! /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
# 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
# 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
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
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."
-ptx*)
basic_machine=`echo $1 | sed -e 's/86-.*/86-sequent/'`
;;
- -windowsnt*)
- os=`echo $os | sed -e 's/windowsnt/winnt/'`
- ;;
-psos*)
os=-psos
;;
| 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 \
| ubicom32 \
| v850 | v850e | v850e1 | v850e2 | v850es | v850e2v3 \
| visium \
- | we32k \
+ | wasm32 \
| x86 | xc16x | xstormy16 | xtensa \
| z8k | z80)
basic_machine=$basic_machine-unknown
| 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-* \
| v850-* | v850e-* | v850e1-* | v850es-* | v850e2-* | v850e2v3-* \
| vax-* \
| visium-* \
+ | wasm32-* \
| we32k-* \
| x86-* | x86_64-* | xc16x-* | xps100-* \
| xstormy16-* | xtensa*-* \
basic_machine=rs6000-bull
os=-bosx
;;
- dpx2* | dpx2*-bull)
+ dpx2*)
basic_machine=m68k-bull
os=-sysv3
;;
basic_machine=v70-nec
os=-sysv
;;
- next | m*-next )
+ next | m*-next)
basic_machine=m68k-next
case $os in
-nextstep* )
nsr-tandem)
basic_machine=nsr-tandem
;;
+ nsx-tandem)
+ basic_machine=nsx-tandem
+ ;;
op50n-* | op60c-*)
basic_machine=hppa1.1-oki
os=-proelf
basic_machine=a29k-wrs
os=-vxworks
;;
+ wasm32)
+ basic_machine=wasm32-unknown
+ ;;
w65*)
basic_machine=w65-wdc
os=-none
basic_machine=hppa1.1-winbond
os=-proelf
;;
+ x64)
+ basic_machine=x86_64-pc
+ ;;
xbox)
basic_machine=i686-pc
os=-mingw32
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
-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]*\
| -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* \
| -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*)
-nova*)
os=-rtmk-nova
;;
- -ns2 )
+ -ns2)
os=-nextstep2
;;
-nsk*)
-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)
sparc-* | *-sun)
os=-sunos4.1.1
;;
+ pru-*)
+ os=-elf
+ ;;
*-be)
os=-beos
;;
m88k-omron*)
os=-luna
;;
- *-next )
+ *-next)
os=-nextstep
;;
*-sequent)
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: "'"
#! /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>.
#
# 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=''
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
enable_lib_only
with_libxml2
with_jemalloc
-with_spdylay
with_systemd
with_mruby
with_neverbleed
SYSTEMD_CFLAGS
SYSTEMD_LIBS
LIBXML2_CFLAGS
-LIBXML2_LIBS
-LIBSPDYLAY_CFLAGS
-LIBSPDYLAY_LIBS'
+LIBXML2_LIBS'
# Initialize some variables set by options.
# 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]...
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
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]
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.
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.
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 $@
# 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
$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
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
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
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
_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
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
memchr \
memmove \
memset \
+ mkostemp \
socket \
sqrt \
strchr \
:
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'
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
# 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
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\\"
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}')
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}')
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])
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"`
[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]])],
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
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.])
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
memchr \
memmove \
memset \
+ mkostemp \
socket \
sqrt \
strchr \
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
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}')
-# 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,
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@
#! /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
# 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:
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_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 \
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 \
$(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)/*
-# 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,
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@
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 \
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 \
$(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)/*
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
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)
_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
_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
_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
# 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 = {
(``-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``)
.\" 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
.
\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>
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
.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
.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
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
.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
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.
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
DESCRIPTION
-----------
-benchmarking tool for HTTP/2 and SPDY server
+benchmarking tool for HTTP/2 server
.. describe:: <URI>
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``
.. 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``
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``
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
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.
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
--------
.\" 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
.
.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
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.
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.
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.
<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.
--- /dev/null
+
+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.
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.
--- /dev/null
+
+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.
--- /dev/null
+
+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()`.
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
.\" 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
.
.\" 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
.
\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>
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
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
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.
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"
"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.
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.
.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
.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
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
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
.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
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
.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.
\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
.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
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
.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
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
.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
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
.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
.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
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
.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
.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>
.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
.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
.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
.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
.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
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\&.
DESCRIPTION
-----------
-A reverse proxy for HTTP/2, HTTP/1 and SPDY.
+A reverse proxy for HTTP/2, and HTTP/1.
.. describe:: <PRIVATE_KEY>
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
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
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.
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"
"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.
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.
.. 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``
.. 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``
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>
.. 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>
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
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
: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>
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.
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>
.. 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``
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
.. 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
* $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
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
Set the maximum size of request body for API request.
- Default: ``16K``
+ Default: ``32M``
DNS
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
~~~~~~~~~
.. 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``
<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>
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
----------------------
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.
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
.. 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
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
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`.
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
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
=======================
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.
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.
.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.
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
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
---------------
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
===============================
: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
------------
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
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
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 a 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
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 a 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
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
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
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:
.. 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`.
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
-----------------
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);
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
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)
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.
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}
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
-# 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,
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@
@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
* 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();
* |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();
}
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)) {
}
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) {
* 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;
* 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"
* 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);
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;
/* 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 &&
/* 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 &&
/* 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 &&
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);
}
/* 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);
}
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) {
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);
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);
}
}
-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;
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) {
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) {
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) {
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) {
}
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) {
/* 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;
}
/* 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);
}
/* 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);
GO_FILES = \
nghttpx_http1_test.go \
nghttpx_http2_test.go \
- nghttpx_spdy_test.go \
server_tester.go
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:
-# 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,
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@
GO_FILES = \
nghttpx_http1_test.go \
nghttpx_http2_test.go \
- nghttpx_spdy_test.go \
server_tester.go
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:
"golang.org/x/net/websocket"
"io"
"net/http"
+ "regexp"
"syscall"
"testing"
"time"
// }
// }
+// 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)
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)
}
}
}
}
+// 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) {
}
}
+// 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()
// 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()
// 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()
}
}
+// 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)
}
}
+// 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) {
}
}
+// 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) {
+++ /dev/null
-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)
- }
-}
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 {
acceptProxyProtocol = true
case "--redirect-if-not-tls":
redirectIfNotTLS = true
+ case "--affinity-cookie":
+ affinityCookie = true
+ case "--alpn-h1":
+ alpnH1 = true
default:
args = append(args, k)
}
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 {
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 = ""
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)
done := make(chan struct{})
go func() {
st.cmd.Wait()
- done <- struct{}{}
+ close(done)
}()
st.cmd.Process.Signal(syscall.SIGQUIT)
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
}
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)
-# 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,
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@
-#\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
-# 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,
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@
*/
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
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.
* 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
* 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,
* 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.
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;
/**
*
* 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|
* <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);
/**
* @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.
*/
* 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
*
* 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.
*
* 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.
* @macro
* Version number of the nghttp2 library release
*/
-#define NGHTTP2_VERSION "1.20.0"
+#define NGHTTP2_VERSION "1.31.1"
/**
* @macro
* 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 */
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);
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;
+}
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 */
#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 */
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) {
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) {
}
}
-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,
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
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,
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,
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);
}
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;
}
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);
}
}
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,
}
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);
}
}
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;
}
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));
}
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;
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;
}
}
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;
}
nghttp2_buf *buf;
nghttp2_ext_altsvc *altsvc;
+ /* This is required with --disable-assert. */
+ (void)rv;
+
altsvc = frame->payload;
buf = &bufs->head->buf;
#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);
* 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
* 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
* 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
* 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
* 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
* 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|.
*/
void nghttp2_frame_unpack_goaway_payload(nghttp2_goaway *frame,
const uint8_t *payload,
- size_t payloadlen,
uint8_t *var_gift_payload,
size_t var_gift_payloadlen);
* Unpacks 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
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;
}
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
#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;
*
* 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|.
*
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:
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;
}
*/
#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);
}
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
/* 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. */
/*
* 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
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);
nghttp2_vec res = {rcbuf->base, rcbuf->len};
return res;
}
+
+int nghttp2_rcbuf_is_static(const nghttp2_rcbuf *rcbuf) {
+ return rcbuf->ref == -1;
+}
}
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;
}
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);
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 {
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 {
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;
}
/*
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;
}
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;
}
/*
/* 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;
} 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) {
}
}
+ 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);
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;
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) {
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);
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 */
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) {
* 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);
}
}
+/*
+ * 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,
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);
}
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);
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) {
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(
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);
}
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);
}
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(
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);
}
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);
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);
}
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);
}
}
+ 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);
}
return rv;
}
+ if (!nghttp2_session_want_read(session)) {
+ return (ssize_t)inlen;
+ }
+
for (;;) {
switch (iframe->state) {
case NGHTTP2_IB_READ_CLIENT_MAGIC:
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;
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;
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;
/* 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;
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;
}
* 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,
}
assert(0);
+ abort(); /* if NDEBUG is set */
}
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,
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;
+}
/* 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
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
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
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
}
}
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);
}
}
} 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);
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) {
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;
nghttp2_frame *frame;
nghttp2_priority_spec copy_pri_spec;
nghttp2_mem *mem;
+ (void)flags;
mem = &session->mem;
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;
}
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;
}
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) {
int32_t promised_stream_id;
int rv;
nghttp2_mem *mem;
+ (void)flags;
mem = &session->mem;
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;
}
}
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;
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) {
nghttp2_frame *frame;
nghttp2_ext_altsvc *altsvc;
int rv;
+ (void)flags;
mem = &session->mem;
-#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
-# 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,
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@
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
-# 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,
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@
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}
set(NGHTTP_SOURCES
${HELPER_OBJECTS}
nghttp.cc
- ssl.cc
+ tls.cc
)
if(HAVE_LIBXML2)
list(APPEND NGHTTP_SOURCES HtmlParser.cc)
set(NGHTTPD_SOURCES
${HELPER_OBJECTS}
nghttpd.cc
- ssl.cc
+ tls.cc
HttpServer.cc
)
util.cc
http2.cc h2load.cc
timegm.c
- ssl.cc
+ tls.cc
h2load_http2_session.cc
h2load_http1_session.cc
)
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
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
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
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
${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}
#include "app_helper.h"
#include "http2.h"
#include "util.h"
-#include "ssl.h"
+#include "tls.h"
#include "template.h"
#ifndef O_BINARY
}
}
- if (ssl_ && !nghttp2::ssl::check_http2_requirement(ssl_)) {
+ if (ssl_ && !nghttp2::tls::check_http2_requirement(ssl_)) {
terminate_session(NGHTTP2_INADEQUATE_SECURITY);
}
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(
struct Worker {
std::unique_ptr<Sessions> sessions;
ev_async w;
- // protectes q
+ // protects q
std::mutex m;
std::deque<ClientInfo> q;
};
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;
}
}
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
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);
}
-I$(top_srcdir)/lib \
-I$(top_srcdir)/src/includes \
-I$(top_srcdir)/third-party \
- @LIBSPDYLAY_CFLAGS@ \
@LIBXML2_CFLAGS@ \
@LIBEV_CFLAGS@ \
@OPENSSL_CFLAGS@ \
LDADD = $(top_builddir)/lib/libnghttp2.la \
$(top_builddir)/third-party/libhttp-parser.la \
@JEMALLOC_LIBS@ \
- @LIBSPDYLAY_LIBS@ \
@LIBXML2_LIBS@ \
@LIBEV_LIBS@ \
@OPENSSL_LIBS@ \
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
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 \
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 \
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 \
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 \
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 \
-# 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,
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 \
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 \
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 \
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) \
@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) \
@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 \
@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 \
@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 \
@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 \
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 \
$(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 \
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) \
@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
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@
-I$(top_srcdir)/lib \
-I$(top_srcdir)/src/includes \
-I$(top_srcdir)/third-party \
- @LIBSPDYLAY_CFLAGS@ \
@LIBXML2_CFLAGS@ \
@LIBEV_CFLAGS@ \
@OPENSSL_CFLAGS@ \
LDADD = $(top_builddir)/lib/libnghttp2.la \
$(top_builddir)/third-party/libhttp-parser.la \
@JEMALLOC_LIBS@ \
- @LIBSPDYLAY_LIBS@ \
@LIBXML2_LIBS@ \
@LIBEV_LIBS@ \
@OPENSSL_LIBS@ \
@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 \
@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 \
@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 \
@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)
@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 \
@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@
@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@
@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@
@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:
@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
@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
@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
@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
@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
#include "nghttp2_config.h"
+#ifndef _WIN32
#include <sys/uio.h>
+#endif // !_WIN32
#include <cassert>
return {dst, size};
}
-} // namespace aria2
+} // namespace nghttp2
#endif // ALLOCATOR_H
print_nv(nva);
}
}
-} // namelen
+} // namespace
void print_timer() {
auto millis = get_timer();
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,
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);
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|.
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
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),
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;
do_write();
do_read();
+ start_ping();
+
auto &connect_cb = on_connect();
if (connect_cb) {
connect_cb(endpoint_it);
}
auto strm = std::move((*it).second);
streams_.erase(it);
+ if (streams_.empty()) {
+ start_ping();
+ }
return strm;
}
strm->stream_id(stream_id);
auto p = streams_.emplace(stream_id, std::move(strm));
assert(p.second);
+ ping_.cancel();
return (*p.first).second.get();
}
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_) {
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));
auto p = streams_.emplace(stream_id, std::move(strm));
assert(p.second);
+ ping_.cancel();
return &(*p.first).second->request();
}
shutdown_socket();
deadline_.cancel();
+ ping_.cancel();
stopped_ = true;
}
} // namespace client
} // namespace asio_http2
-} // nghttp2
+} // namespace nghttp2
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;
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_;
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_;
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;
}
#include <boost/asio/ssl.hpp>
-#include "ssl.h"
+#include "tls.h"
#include "util.h"
namespace nghttp2 {
*/
#include "asio_common.h"
+#include <fcntl.h>
#include <memory>
#include "util.h"
} // namespace server
} // namespace asio_http2
-} // namespace nghttp
+} // namespace nghttp2
#endif // ASIO_SERVER_HTTP2_HANDLER_H
#include "asio_server.h"
#include "util.h"
-#include "ssl.h"
+#include "tls.h"
#include "template.h"
namespace nghttp2 {
#include <boost/asio/ssl.hpp>
-#include "ssl.h"
+#include "tls.h"
#include "util.h"
namespace nghttp2 {
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);
#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"
} // namespace
Config::Config()
- : ciphers(ssl::DEFAULT_CIPHER_LIST),
+ : ciphers(tls::DEFAULT_CIPHER_LIST),
data_length(-1),
addrs(nullptr),
nreqs(1),
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),
}
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;
} // 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();
rv = client->connect();
if (rv != 0) {
client->fail();
+ client->worker->free_client(client);
delete client;
return;
}
}
if (rv != 0) {
client->fail();
+ client->worker->free_client(client);
delete client;
}
}
if (client->try_again_or_fail() == 0) {
return;
}
+ client->worker->free_client(client);
delete client;
return;
}
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
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;
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();
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);
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;
}
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);
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) {
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) {
}
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) {
}
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;
}
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;
if (req_inflight == 0) {
terminate_session();
}
+ std::cout << "Process Request Failure:" << worker->stats.req_failed
+ << std::endl;
}
namespace {
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);
}
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;
}
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;
}
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) {
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();
+ }
}
}
} 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.
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);
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();
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;
}
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;
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;
}
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;
<< 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
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 {
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.
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>
<< 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>
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
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.
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;
{"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},
{"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;
}
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);
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;
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:
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);
}
// 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);
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 "
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);
}
config.h1reqs.reserve(reqlines.size());
config.nva.reserve(reqlines.size());
- config.nv.reserve(reqlines.size());
for (auto &req : reqlines) {
// For HTTP/1.1
}
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!
// 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;
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.;
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
<< "%), " << 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)
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;
// 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
~Config();
bool is_rate_mode() const;
+ bool is_timing_based_mode() const;
bool has_base_uri() const;
};
// 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;
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
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;
};
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);
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 {
#include <cassert>
#include <cerrno>
+#include <iostream>
#include "h2load.h"
#include "util.h"
}
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
void Http2Session::on_connect() {
int rv;
+ // This is required with --disable-assert.
+ (void)rv;
+
nghttp2_session_callbacks *callbacks;
nghttp2_session_callbacks_new(&callbacks);
+++ /dev/null
-/*
- * 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
+++ /dev/null
-/*
- * 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
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:
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:
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:
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");
}
}
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;
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
//
// 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,
{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]);
}
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]);
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"
"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) {
-# 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,
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@
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 {
// 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
// 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
// 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_;
namespace nghttp2 {
-typedef struct { int dump_header_table; } inflate_config;
+typedef struct {
+ int dump_header_table;
+} inflate_config;
static inflate_config config;
#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>
#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;
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;
}
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;
};
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);
#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
sockaddr sa;
sockaddr_in6 in6;
sockaddr_in in;
+#ifndef _WIN32
sockaddr_un un;
+#endif // !_WIN32
};
struct Address {
#include "HtmlParser.h"
#include "util.h"
#include "base64.h"
-#include "ssl.h"
+#include "tls.h"
#include "template.h"
#ifndef O_BINARY
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
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);
void Request::init_inflater() {
int rv;
+ // This is required with --disable-assert.
+ (void)rv;
rv = nghttp2_gzip_inflate_new(&inflater);
assert(rv == 0);
}
} // 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;
}
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;
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;
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());
}
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;
}
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;
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(
(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.
} // namespace
int main(int argc, char **argv) {
- ssl::libssl_init();
+ tls::libssl_init();
bool color = false;
while (1) {
{"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},
{"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;
}
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);
bool hexdump;
bool no_push;
bool expect_continue;
+ bool verify_peer;
};
enum class RequestState { INITIAL, ON_REQUEST, ON_RESPONSE, ON_COMPLETE };
switch (rv) {
case Z_STREAM_END:
inflater->finished = 1;
+ /* FALL THROUGH */
case Z_OK:
case Z_BUF_ERROR:
return 0;
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);
#include "app_helper.h"
#include "HttpServer.h"
#include "util.h"
-#include "ssl.h"
+#include "tls.h"
namespace nghttp2 {
--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.
} // 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;
#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"
#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"
CU_pSuite pSuite = NULL;
unsigned int num_tests_failed;
- nghttp2::ssl::libssl_init();
+ nghttp2::tls::libssl_init();
shrpx::create_config();
}
// 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",
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",
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) ||
#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"
#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"
};
namespace {
-std::random_device rd;
-} // namespace
-
-namespace {
void signal_cb(struct ev_loop *loop, ev_signal *w, int revents);
} // namespace
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;
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);
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;
}
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;
}
}
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;
}
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();
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
auto loop = ev_default_loop(config->ev_loop_flags);
- int ipc_fd;
+ int ipc_fd = 0;
auto pid = fork_worker_process(ipc_fd, {});
} // 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
}
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");
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;
{
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;
}
auto &apiconf = config->api;
- apiconf.max_request_body = 16_k;
+ apiconf.max_request_body = 32_m;
auto &dnsconf = config->dns;
{
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
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
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
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.
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"
"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.
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.
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
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>
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>
--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
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
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
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.
--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
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>
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
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
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>
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
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
* $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
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.
--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
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>
--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>
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
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 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;
}
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;
}
}
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
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;
}
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 "
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;
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};
// 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().
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);
{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},
{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;
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,
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;
}
util::make_socket_closeonexec(cfd);
#endif // !HAVE_ACCEPT4
- util::make_socket_nodelay(cfd);
-
conn_hnr_->handle_connection(cfd, &sockaddr.sa, addrlen, faddr_);
}
*/
#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"
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)) {
switch (path[25]) {
case 'g':
if (util::streq_l("/api/v1beta1/backendconfi", std::begin(path), 25)) {
- return &apis[0];
+ return &apis()[0];
}
break;
}
switch (path[26]) {
case 'n':
if (util::streq_l("/api/v1beta1/configrevisio", std::begin(path), 26)) {
- return &apis[1];
+ return &apis()[1];
}
break;
}
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;
}
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
}
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;
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) {
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;
}
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_;
};
#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;
delete handler;
return;
}
- if (handler->do_write() != 0) {
- delete handler;
- return;
- }
}
} // namespace
*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();
}
#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;
}
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");
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));
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;
}
}
} // 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;
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)) {
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);
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_);
}
}
dconn =
- make_unique<HttpDownstreamConnection>(group, -1, conn_.loop, worker_);
+ make_unique<HttpDownstreamConnection>(group, 0, conn_.loop, worker_);
}
dconn->set_client_handler(this);
}
void ClientHandler::write_accesslog(Downstream *downstream) {
- nghttp2::ssl::TLSSessionInfo tls_info;
auto &req = downstream->request();
auto config = get_config();
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,
});
}
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; }
<< " 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();
}
StringRef ClientHandler::get_tls_sni() const { return sni_; }
+StringRef ClientHandler::get_alpn() const { return alpn_; }
+
BlockAllocator &ClientHandler::get_block_allocator() { return balloc_; }
} // namespace shrpx
Worker *get_worker() const;
+ // Initializes forwarded_for_.
+ void init_forwarded_for(int family, const StringRef &ipaddr);
+
using ReadBuf = DefaultMemchunkBuffer;
ReadBuf *get_rb();
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();
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();
// 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:
#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;
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;
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;
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)) {
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;
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;
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;
struct UpstreamParams {
int alt_mode;
bool tls;
+ bool sni_fwd;
bool proxyproto;
};
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)) {
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 {
} 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;
//
// 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.
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;
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)) {
*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] == '*') {
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; });
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;
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;
}
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;
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;
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)) {
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;
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;
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;
}
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;
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;
}
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;
}
// 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;
<< 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);
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);
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) {
}
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) {
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;
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";
}
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;
*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;
}
}
// gcc needs this.
assert(0);
+ abort();
}
namespace {
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
}
}
- if (g.affinity == AFFINITY_IP) {
+ if (g.affinity.type != AFFINITY_NONE) {
size_t idx = 0;
for (auto &addr : g.addrs) {
StringRef key;
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);
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");
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;
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 {
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;
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
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;
ev_tstamp update_interval;
StringRef fetch_ocsp_response_file;
bool disabled;
+ bool startup;
+ bool no_verify;
} ocsp;
// Client verification configurations
// certificate validation
StringRef cacert;
bool enabled;
+ // true if we accept an expired client certificate.
+ bool tolerate_expired;
} client_verify;
// Client (backend connection) TLS configuration.
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;
verbose{false},
daemon{false},
http2_proxy{false},
+ single_process{false},
single_thread{false},
ev_loop_flags{0} {}
~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;
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,
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,
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,
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,
// 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. ":"
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);
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);
bool offline_;
};
-} // namespace
+} // namespace shrpx
#endif // SHRPX_CONNECT_BLOCKER_H
#include <openssl/err.h>
-#include "shrpx_ssl.h"
+#include "shrpx_tls.h"
#include "shrpx_memcached_request.h"
#include "shrpx_log.h"
#include "memchunk.h"
tls.handshake_state = 0;
tls.initial_handshake_done = false;
tls.reneg_started = false;
+ tls.sct_requested = false;
}
if (fd != -1) {
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) {
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;
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;
}
break;
}
+ ERR_clear_error();
+
auto rv = SSL_do_handshake(tls.ssl);
if (rv <= 0) {
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")
!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.";
}
}
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.";
}
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() {
tls.last_write_idle = -1.;
+ ERR_clear_error();
+
auto rv = SSL_write(tls.ssl, data, len);
if (rv <= 0) {
tls.last_readlen = 0;
}
+ ERR_clear_error();
+
auto rv = SSL_read(tls.ssl, data, len);
if (rv <= 0) {
wlimit.drain(nwrite);
+ if (ev_is_active(&wt)) {
+ ev_timer_again(loop, &wt);
+ }
+
return nwrite;
}
wlimit.drain(nwrite);
+ if (ev_is_active(&wt)) {
+ ev_timer_again(loop, &wt);
+ }
+
return nwrite;
}
: 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 {
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)) {
struct MemcachedRequest;
+namespace tls {
+struct TLSSessionCache;
+} // namespace tls
+
enum {
TLS_CONN_NORMAL,
TLS_CONN_WAIT_FOR_SESSION_CACHE,
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
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 {
// 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
#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"
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;
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;
}
}
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);
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>(
#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);
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.
++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
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);
}
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";
acceptors_.push_back(std::move(h));
}
+void ConnectionHandler::delete_acceptor() { acceptors_.clear(); }
+
void ConnectionHandler::enable_acceptor() {
for (auto &a : acceptors_) {
a->enable();
}
void ConnectionHandler::cancel_ocsp_update() {
+ enable_acceptor_on_ocsp_completion_ = false;
+ ev_timer_stop(loop_, &ocsp_timer_);
+
if (ocsp_.proc.pid == 0) {
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));
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;
<< " 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,
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();
// 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.
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
return indexed_ssl_ctx_[idx];
}
+void ConnectionHandler::set_enable_acceptor_on_ocsp_completion(bool f) {
+ enable_acceptor_on_ocsp_completion_ = f;
+}
+
} // namespace shrpx
class MemcachedDispatcher;
struct UpstreamAddr;
-namespace ssl {
+namespace tls {
class CertLookupTree;
-} // namespace ssl
+} // namespace tls
struct OCSPUpdateContext {
// ocsp response buffer
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);
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_;
// 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.
// 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
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
default:
// Unreachable
assert(0);
+ abort();
}
}
return DNS_STATUS_OK;
default:
assert(0);
+ abort();
}
}
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),
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;
upstream_wtimer_.data = this;
downstream_rtimer_.data = this;
downstream_wtimer_.data = this;
+
+ rcbufs_.reserve(32);
}
Downstream::~Downstream() {
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;
}
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;
}
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 {
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
#include <string>
#include <memory>
#include <chrono>
+#include <algorithm>
#include <ev.h>
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
// 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.
// 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;
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,
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.
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.
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
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;
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
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
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);
}
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
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
#include <nghttp2/nghttp2.h>
+#include "shrpx_config.h"
#include "util.h"
#include "allocator.h"
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
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) {
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", "*"));
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);
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);
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);
}
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);
#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 {
return;
}
http2session->connection_alive();
-
- rv = http2session->do_write();
- if (rv != 0) {
- delete http2session;
-
- return;
- }
}
} // namespace
// 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
return 0;
default:
assert(0);
+ abort();
}
}
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};
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);
}
}
- 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();
}
}
// Unreachable
- DIE();
+ assert(0);
+
return 0;
}
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));
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;
}
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);
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();
}
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;
auto nread = conn_.read_clear(buf.data(), buf.size());
if (nread == 0) {
- return 0;
+ return write_clear();
}
if (nread < 0) {
}
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;
auto nread = conn_.read_tls(buf.data(), buf.size());
if (nread == 0) {
- return 0;
+ return write_tls();
}
if (nread < 0) {
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) {
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);
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);
}
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);
}
}
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;
if (rv < NGHTTP2_ERR_FATAL) {
ULOG(FATAL, this) << "nghttp2_submit_rst_stream() failed: "
<< nghttp2_strerror(rv);
- DIE();
+ return -1;
}
return 0;
}
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());
}
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);
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);
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));
}
}
+ 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) {
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);
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()) {
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;
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;
}
#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"
} // 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;
}
}
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) {
} // 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);
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
} // 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,
// 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;
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) {
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};
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);
int HttpDownstreamConnection::push_request_headers() {
if (downstream_->get_request_header_sent()) {
+ signal_write();
return 0;
}
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()) {
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);
buf->append("\r\n");
}
- auto &xffconf = httpconf.xff;
-
auto xff = xffconf.strip_incoming ? nullptr
: req.fs.header(http2::HD_X_FORWARDED_FOR);
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) {
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");
}
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);
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";
}
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)) {
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;
}
}
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;
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) {
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);
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_;
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"),
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) {
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
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
} 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);
} 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;
}
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
}
}
- if (http2_proxy) {
- req.path = path;
- } else {
- req.path = http2::rewrite_clean_path(balloc, path);
- }
+ req.path = http2::rewrite_clean_path(balloc, path);
}
} // namespace
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);
}
}
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();
}
}
// 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;
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;
}
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;
}
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;
}
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;
error_reply(status_code);
- handler_->signal_write_no_wait();
+ handler_->signal_write();
return 0;
}
}
end:
- handler_->signal_write_no_wait();
+ handler_->signal_write();
return 0;
}
// drop connection.
return -1;
end:
- handler_->signal_write_no_wait();
+ handler_->signal_write();
return 0;
}
downstream->pop_downstream_connection();
- handler_->signal_write_no_wait();
+ handler_->signal_write();
return 0;
}
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();
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)) {
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
}
}
+ 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) {
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");
}
}
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;
}
// 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;
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;
#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 {
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) {
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);
}
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;
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) {
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) {
}
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)) {
} // 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
? 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;
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) {
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;
}
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);
#include "shrpx_config.h"
#include "shrpx_log_config.h"
-#include "ssl.h"
+#include "tls.h"
#include "template.h"
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) \
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,
};
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;
#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"
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);
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);
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);
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;
}
}
#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"
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_;
#include "shrpx_mruby.h"
#include "shrpx_mruby_module.h"
#include "shrpx_log.h"
+#include "shrpx_tls.h"
namespace shrpx {
}
} // 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);
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
continue;
}
if (i != p) {
- headers[p++] = std::move(kv);
+ headers[p] = std::move(kv);
}
+ ++p;
}
headers.resize(p);
}
continue;
}
if (i != p) {
- headers[p++] = std::move(kv);
+ headers[p] = std::move(kv);
}
+ ++p;
}
headers.resize(p);
}
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_{} {}
} // 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;
// 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) {
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;
}
}
} // 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;
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);
if (node->len == n) {
// Complete match with this node
if (node->index != -1) {
+ *pattern_is_wildcard = false;
return node;
}
// 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);
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 {
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;
// 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 {
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
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;
struct Pattern {
StringRef pattern;
size_t idx;
+ bool wildcard;
};
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},
namespace shrpx {
void test_shrpx_router_match(void);
+void test_shrpx_router_match_wildcard(void);
void test_shrpx_router_match_prefix(void);
} // namespace shrpx
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);
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
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
// -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
+++ /dev/null
-/*
- * nghttp2 - HTTP/2 C Library
- *
- * Copyright (c) 2012 Tatsuhiro Tsujikawa
- *
- * Permission is hereby granted, free of charge, to any person obtaining
- * a copy of this software and associated documentation files (the
- * "Software"), to deal in the Software without restriction, including
- * without limitation the rights to use, copy, modify, merge, publish,
- * distribute, sublicense, and/or sell copies of the Software, and to
- * permit persons to whom the Software is furnished to do so, subject to
- * the following conditions:
- *
- * The above copyright notice and this permission notice shall be
- * included in all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
- * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
- * OF 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
+++ /dev/null
-/*
- * nghttp2 - HTTP/2 C Library
- *
- * Copyright (c) 2012 Tatsuhiro Tsujikawa
- *
- * Permission is hereby granted, free of charge, to any person obtaining
- * a copy of this software and associated documentation files (the
- * "Software"), to deal in the Software without restriction, including
- * without limitation the rights to use, copy, modify, merge, publish,
- * distribute, sublicense, and/or sell copies of the Software, and to
- * permit persons to whom the Software is furnished to do so, subject to
- * the following conditions:
- *
- * The above copyright notice and this permission notice shall be
- * included in all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
- * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
- * OF 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
* OF 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 {
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;
}
} // 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);
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;
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);
#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;
}
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);
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;
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="
#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));
} // 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
,
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);
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();
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);
}
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);
#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
#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
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();
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) {
}
}
+ 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) {
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";
#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) {
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});
}
}
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});
}
,
neverbleed_t *nb
#endif // HAVE_NEVERBLEED
- ) {
+) {
auto config = get_config();
if (!upstream_tls_enabled(config->conn)) {
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();
}
#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
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>();
}
} // 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) {
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
* OF 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"
struct DownstreamAddr;
struct UpstreamAddr;
-namespace ssl {
+namespace tls {
struct TLSSessionCache {
// ASN1 representation of SSL_SESSION object. See
,
neverbleed_t *nb
#endif // HAVE_NEVERBLEED
- );
+);
// Create client side SSL_CTX. This does not configure ALPN settings.
// |next_proto_select_cb| is for NPN.
,
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);
// 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.
// 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
* OF 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"
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
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}));
}
};
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);
}
// -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"));
* OF 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
#include <memory>
-#include "shrpx_ssl.h"
+#include "shrpx_tls.h"
#include "shrpx_log.h"
#include "shrpx_client_handler.h"
#include "shrpx_http2_session.h"
}
} // 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),
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;
}
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];
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;
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();
// 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;
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;
}
}
}
}
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)) {
}
}
-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
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;
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);
#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"
} // namespace mruby
#endif // HAVE_MRUBY
-namespace ssl {
+namespace tls {
class CertLookupTree;
-} // namespace ssl
+} // namespace tls
struct DownstreamAddr {
Address addr;
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.
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
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;
// 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
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;
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);
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.
// 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
#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"
#ifdef HAVE_NEVERBLEED
neverbleed_t *nb
#endif // HAVE_NEVERBLEED
- ) {
+) {
std::array<char, STRERROR_BUFSIZE> errbuf;
auto config = get_config();
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();
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;
} // 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;
auto loop = EV_DEFAULT;
- auto gen = std::mt19937(rd());
+ auto gen = util::make_mt19937();
ConnectionHandler conn_handler(loop, gen);
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;
#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();
}
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);
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));
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
#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
* OF 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 {
} // 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
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) {
}
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
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
* OF 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
//
#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
// 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
#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
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) {
if (c <= 'z') {
return c - 'a' + 10;
}
- return c;
+ return 256;
}
StringRef quote_string(BlockAllocator &balloc, const StringRef &target) {
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) {
}
}
-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);
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;
}
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;
}
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)
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;
}
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;
}
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;
}
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 {
}
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,
return StringRef{std::begin(hostport), p};
}
+std::mt19937 make_mt19937() {
+ std::random_device rd;
+ return std::mt19937(rd());
+}
+
} // namespace util
} // namespace nghttp2
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);
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.
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) {
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) {
// NULL-terminated.
StringRef extract_host(const StringRef &hostport);
+// Returns new std::mt19937 object.
+std::mt19937 make_mt19937();
+
} // namespace util
} // namespace nghttp2
if (tz) {
setenv("TZ", tz, 1);
+ free(tz);
} else {
unsetenv("TZ");
}
-# 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,
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@
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;
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 {
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;
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;
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);
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);
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;
} \
} 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);
}
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);
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);
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);
}
}
-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];
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;
} 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[] = {
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;
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;
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 {
}
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 {
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;
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;
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;
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;
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;
}
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;
}
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;
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;
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);
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);
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);
}
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;
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);
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
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);
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
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);
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(
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);
-# 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,
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@
-# 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,
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@
# 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']
Yuichi Osawa
Terence Lee
Zachary Scott
+ Tomasz Dąbrowski
+++ /dev/null
-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:
-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"),
## 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)
$ 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 a mailing list, but you can use [GitHub issues](https://github.com/mruby/mruby).
## How to compile and install (mruby and gems)
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
load "#{MRUBY_ROOT}/tasks/benchmark.rake"
+load "#{MRUBY_ROOT}/tasks/gitlab.rake"
+
##############################
# generic build targets, rules
task :default => :all
--- /dev/null
+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
--- /dev/null
+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
# 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'
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
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
```
### 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
```bash
$ mrdb --version
-mruby 1.2.0 (2014-11-17)
+mruby 1.3.0 (2017-7-4)
```
## 2.2 Basic Operation
-# 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
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.
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
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`:
}
```
-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.
* 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`
# 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
```
--- /dev/null
+# 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.
# 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")) }
#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)
--- /dev/null
+# 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
--- /dev/null
+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
--- /dev/null
+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
--- /dev/null
+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
--- /dev/null
+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
#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)
#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
/*
** 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
mrb_value *stackent;
int nregs;
int ridx;
- int eidx;
+ int epos;
struct REnv *env;
mrb_code *pc; /* return address */
mrb_code *err; /* error position */
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,
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;
};
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 */
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 */
*
* !!!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.
/**
* 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.
/**
* 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.
* @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*);
/**
* 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.
* 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);
* 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.
* 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.
/**
* 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.
*
* 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
*
* 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.
* 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.
* 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.
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.
* 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.
* 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.
/**
* 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);
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);
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)); \
+ 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. */
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);
/*
*
* @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;
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
#ifndef MRUBY_ARRAY_H
#define MRUBY_ARRAY_H
-#include "mruby/common.h"
+#include "common.h"
/*
* Array class
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)
* 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.
* 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);
*/
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)
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 */
#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)
#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 */
# 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;
#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
#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)
#ifndef MRUBY_CLASS_H
#define MRUBY_CLASS_H
-#include "mruby/common.h"
+#include "common.h"
/**
* Class class
}
}
-// 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 {\
/*
-** mruby/common.h - mruby common platform definitions
+**"common.h - mruby common platform definition"
**
** See Copyright Notice in mruby.h
*/
#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
#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;
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);
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;
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 {
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;
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);
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
#ifndef MRUBY_DATA_H
#define MRUBY_DATA_H
-#include "mruby/common.h"
+#include "common.h"
/**
* Custom C wrapped data.
#ifndef MRUBY_DEBUG_H
#define MRUBY_DEBUG_H
-#include "mruby/common.h"
+#include "common.h"
/**
* MRuby Debugging.
#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.
/* 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"
#ifndef MRUBY_ERROR_H
#define MRUBY_ERROR_H
-#include "mruby/common.h"
+#include "common.h"
/**
* MRuby error handling.
/* 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
*
#ifndef MRUBY_GC_H
#define MRUBY_GC_H
-#include "mruby/common.h"
+#include "common.h"
/**
* Uncommon memory management stuffs.
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);
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;
#ifndef MRUBY_HASH_H
#define MRUBY_HASH_H
-#include "mruby/common.h"
+#include "common.h"
+#include <mruby/khash.h>
/**
* Hash class
#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)
#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 */
#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.
struct mrb_locals *lv;
/* debug info */
+ mrb_bool own_filename;
const char *filename;
uint16_t *lines;
struct mrb_irep_debug_info* debug_info;
--- /dev/null
+/*
+** 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 */
#include <string.h>
-#include "mruby.h"
-#include "mruby/common.h"
+#include <mruby.h>
+#include "common.h"
/**
* khash definitions used in mruby's hash table.
#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);
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)
{
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 */
};
#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;
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]) */
#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
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)
#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 {
#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)))
/* 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
#ifndef MRUBY_RANGE_H
#define MRUBY_RANGE_H
-#include "mruby/common.h"
+#include "common.h"
/**
* Range class
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))
/*
*/
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
#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 */
#ifndef MRUBY_STRING_H
#define MRUBY_STRING_H
-#include "mruby/common.h"
+#include "common.h"
/**
* String class
#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)
#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);
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);
/*
* 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);
/*
* 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);
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);
#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; }
#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
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
#ifndef MRUBY_VALUE_H
#define MRUBY_VALUE_H
-#include "mruby/common.h"
+#include "common.h"
/**
* MRuby Value definition functions and macros.
# 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
# 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))
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
{
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;
}
}
#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[];
return _etext < p && p < (char*)&__init_array_start;
}
#endif
+#endif
#else
# define mrb_ro_data_p(p) FALSE
#endif
#ifndef MRUBY_VARIABLE_H
#define MRUBY_VARIABLE_H
-#include "mruby/common.h"
+#include "common.h"
/**
* Functions to access mruby variables.
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);
#ifndef MRUBY_VERSION_H
#define MRUBY_VERSION_H
-#include "mruby/common.h"
+#include "common.h"
/**
* mruby version definition macros
/*
* Minor release version number.
*/
-#define MRUBY_RELEASE_MINOR 2
+#define MRUBY_RELEASE_MINOR 3
/*
* Tiny release version number.
/*
* 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.
# 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 ----------------------------------------------------
# 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
# 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
# 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
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
#
# +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
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
-#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:
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)
);
}
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)
{
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
##
# 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" ]
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
# compile
`./bin/mrbc -g -o "#{bin.path}" "#{script.path}"`
-
+
# add mrdb quit
testcase << {:cmd=>"quit"}
# compile
`./bin/mrbc -g -o "#{bin.path}" "#{script.path}"`
-
+
# add mrdb quit
testcase << {:cmd=>"quit"}
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'}
*/
#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;
}
}
}
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;
}
}
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:
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;
}
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;
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;
}
}
}
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;
}
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;
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;
}
}
}
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);
}
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);
}
}
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 {
}
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;
}
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 {
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;
}
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;
}
}
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;
}
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;
}
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;
}
}
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 ) {
- if( line == 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;
}
}
}
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;
}
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;
}
}
}
-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++;
#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_ */
#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
}
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);
#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);
#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
#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*);
#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"
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++;
{
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;
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);
}
}
{
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;
}
{
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);
}
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;
}
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);
}
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;
}
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 '\"':
*cname = args;
break;
}
- } else {
+ }
+ else {
printf(BREAK_ERR_MSG_INVALIDCLASS, args);
type = MRB_DEBUG_BPTYPE_NONE;
}
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);
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 {
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);
}
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);
}
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;
#include <string.h>
#include "apilist.h"
-#include "mruby/compile.h"
+#include <mruby/compile.h>
typedef struct help_msg {
const char *cmd1;
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) {
#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
**
*/
-#include "mruby/opcode.h"
+#include <mruby/opcode.h>
#include "mrdb.h"
dbgcmd_state
{
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;
}
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;
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;
+}
#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"
{"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}
};
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;
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;
{
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);
}
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;
}
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;
}
switch (dbg->xm) {
case DBG_STEP:
- case DBG_NEXT: // temporary
if (!file || (dbg->prvfile == file && dbg->prvline == line)) {
return;
}
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) {
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);
st = cmd->func(mrb, mrdb);
- if( (st == DBGST_CONTINUE) || (st == DBGST_RESTART) ) break;
+ if ((st == DBGST_CONTINUE) || (st == DBGST_RESTART)) break;
}
return dbg->xm;
}
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 {
}
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;
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;
#ifndef MRDB_H
#define MRDB_H
-#include "mruby.h"
+#include <mruby.h>
#include "mrdbconf.h"
const char *prvfile;
int32_t prvline;
+ mrb_callinfo *prvci;
mrdb_exemode xm;
mrdb_exephase xphase;
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*);
#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
}
struct _args {
+ FILE *rfp;
mrb_bool verbose : 1;
int argc;
char** argv;
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);
}
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;
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) {
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) {
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;
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;
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);
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);
#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"
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
--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
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
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
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
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
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
#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
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);
#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;
--- /dev/null
+MRuby::Gem::Specification.new('mruby-class-ext') do |spec|
+ spec.license = 'MIT'
+ spec.author = 'mruby developers'
+ spec.summary = 'class/module extension'
+end
--- /dev/null
+#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)
+{
+}
--- /dev/null
+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
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
#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;
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);
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);
}
return 0;
}
}
+ if (c0 == OP_LOADNIL) {
+ if (GETARG_B(i) == GETARG_A(i0)) {
+ s->pc--;
+ return 0;
+ }
+ }
break;
case OP_JMPIF:
case OP_JMPNOT:
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);
}
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;
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;
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++;
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));
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) {
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)
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) {
}
}
else {
- codegen(s, t->car->cdr, NOVAL);
- t = t->cdr;
while (t) {
codegen(s, t->car, NOVAL);
t = t->cdr;
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();
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 {
}
}
}
+ if (safe) {
+ dispatch(s, skip);
+ }
if (val) {
push();
}
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);
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);
default:
#ifndef MRB_DISABLE_STDIO
- printf("unknown lhs %d\n", type);
+ fprintf(stderr, "unknown lhs %d\n", type);
#endif
break;
}
n++;
}
}
- push();
+ if (!val) {
+ push();
+ }
}
}
}
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);
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);
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;
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;
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;
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));
break;
case NODE_BLOCK:
- {
+ if (val) {
int idx = lambda_body(s, tree, 1);
genop(s, MKOP_Abc(OP_LAMBDA, cursp(), idx, OP_L_BLOCK));
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));
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 {
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:
{
int n;
- n = gen_values(s, tree, val);
+ n = gen_values(s, tree, val, 0);
if (n >= 0) {
if (val) {
pop_n(n);
break;
case NODE_SPLAT:
- codegen(s, tree, VAL);
+ codegen(s, tree, val);
break;
case NODE_ASGN:
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) {
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;
}
}
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();
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();
case NODE_RETURN:
if (tree) {
- codegen(s, tree, VAL);
- pop();
+ gen_retval(s, tree);
}
else {
genop(s, MKOP_A(OP_LOADNIL, cursp()));
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();
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 {
}
genop(s, MKOP_sBx(OP_JMP, s->loop->pc2 - s->pc));
}
+ if (val) push();
break;
case NODE_RETRY:
genop(s, MKOP_sBx(OP_JMP, lp->pc1 - s->pc));
}
}
+ if (val) push();
}
break;
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();
}
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();
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));
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));
break;
case NODE_INT:
- {
+ if (val) {
char *p = (char*)tree->car;
int base = (intptr_t)tree->cdr->car;
mrb_int i;
break;
default:
- {
+ if (val) {
int sym = new_msym(s, mrb_intern_lit(s->mrb, "-"));
genop(s, MKOP_ABx(OP_LOADI, cursp(), 0));
pop(); pop();
genop(s, MKOP_ABC(OP_SUB, cursp(), sym, 2));
}
+ else {
+ codegen(s, tree, NOVAL);
+ }
break;
}
}
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) {
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;
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;
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;
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++;
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);
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();
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();
default:
break;
}
+ exit:
+ s->rlev = rlev;
}
static void
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;
p->parser = prev->parser;
p->filename_index = prev->filename_index;
+ p->rlev = prev->rlev+1;
+
return p;
}
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;
struct loopinfo *loop;
if (tree) {
- codegen(s, tree, VAL);
- pop();
+ gen_retval(s, tree);
}
loop = s->loop;
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) {
loop->pc3 = tmp;
}
else {
+ if (!tree) {
+ genop(s, MKOP_A(OP_LOADNIL, cursp()));
+ }
genop(s, MKOP_AB(OP_RETURN, cursp(), OP_R_BREAK));
}
}
{
codegen_scope *scope = scope_new(mrb, 0, 0);
struct RProc *proc;
+ struct mrb_jmpbuf *prev_jmp = mrb->jmp;
if (!scope) {
return NULL;
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);
** 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,
NODE_CVDECL,
NODE_OP_ASGN,
NODE_CALL,
+ NODE_SCALL,
NODE_FCALL,
NODE_VCALL,
NODE_SUPER,
NODE_LAST
};
-#endif /* NODE_H */
+#endif /* MRUBY_COMPILER_NODE_H */
#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
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))
#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)
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)
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);
}
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);
}
static node*
new_while(parser_state *p, node *a, node *b)
{
+ void_expr_error(p, a);
return cons((node*)NODE_WHILE, cons(a, 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));
}
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);
}
node *n = list2((node*)NODE_CASE, a);
node *n2 = n;
+ void_expr_error(p, a);
while (n2->cdr) {
n2 = n2->cdr;
}
/* (: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;
}
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)));
}
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));
}
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));
}
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);
}
static node*
new_asgn(parser_state *p, node *a, node *b)
{
+ void_expr_error(p, b);
return cons((node*)NODE_ASGN, cons(a, 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));
}
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);
}
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) */
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) */
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) */
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)
{
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) */
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
{
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;
}
}
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));
}
}
{
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);
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
}
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 ----------------------------- */
%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
%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
%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- */
%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 /* <<, <<- */
}
| 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
{
$$ = 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
;
expr_value : expr
{
if (!$1) $$ = new_nil(p);
- else $$ = $1;
+ else {
+ $$ = $1;
+ }
}
;
;
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
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
{
}
| 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
{
}
| 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
{
}
| primary_value tCOLON2 cname
{
+ void_expr_error(p, $1);
$$ = cons($1, nsym($3));
}
;
| 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);
}
;
-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));
}
}
;
+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;
$$ = cons($1,0);
NODE_LINENO($$, $1);
}
- | args ',' assocs ','
+ | args comma assocs ','
{
$$ = cons(push($1, new_hash(p, $3)), 0);
NODE_LINENO($$, $1);
call_args : command
{
+ void_expr_error(p, $1);
$$ = cons(list1($1), 0);
NODE_LINENO($$, $1);
}
$$ = 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);
}
;
-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;
}
}
;
-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));
}
;
$<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 ')'
{
{
$$ = 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
{
}
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
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
}
| 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
{
}
$$ = $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);
}
;
{
$$ = 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
{
}
| primary_value '[' opt_call_args rbracket
{
- $$ = new_call(p, $1, intern("[]",2), $3);
+ $$ = new_call(p, $1, intern("[]",2), $3, '.');
}
;
| none
;
-exc_list : arg_value
+exc_list : arg
{
$$ = list1($1);
}
heredoc : tHEREDOC_BEG
;
-opt_heredoc_bodies : /* none */
- | heredoc_bodies
- ;
-
heredoc_bodies : heredoc_body
| heredoc_bodies heredoc_body
;
{
$$ = 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));
}
| 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__
{
expr_value term
{
$$ = $3;
- } /*
+ } /*
| error term
{
yyerrok;
}
;
-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);
}
;
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:
}
;
-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
| tCOLON2
;
+call_op : '.'
+ {
+ $$ = '.';
+ }
+ | tANDDOT
+ {
+ $$ = 0;
+ }
+ ;
+
+call_op2 : call_op
+ | tCOLON2
+ {
+ $$ = tCOLON2;
+ }
+ ;
+
opt_terms : /* none */
| terms
;
trailer : /* none */
| nl
- | ','
+ | comma
;
term : ';' {yyerrok;}
| nl
+ | heredoc_body
;
nl : '\n'
p->lineno++;
p->column = 0;
}
- opt_heredoc_bodies
+ ;
terms : term
- | terms ';' {yyerrok;}
+ | terms term
;
none : /* none */
}
;
%%
-#define yylval (*((YYSTYPE*)(p->ylval)))
+#define pylval (*((YYSTYPE*)(p->ylval)))
static void
yyerror(parser_state *p, const char *s)
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);
}
}
+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);
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) {
/* 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;
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;
}
tokadd(parser_state *p, int32_t c)
{
char utf8[4];
- unsigned len;
+ int i, len;
/* mrb_assert(-0x10FFFF <= c && c <= 0xFF); */
if (c >= 0) {
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)
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)
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 */
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)) {
}
}
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) {
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) {
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));
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;
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);
}
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;
}
}
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;
}
p->heredocs_from_nextline = push(p->heredocs_from_nextline, newnode);
p->lstate = EXPR_END;
- yylval.nd = newnode;
+ pylval.nd = newnode;
return tHEREDOC_BEG;
}
case '*':
if ((c = nextc(p)) == '*') {
if ((c = nextc(p)) == '=') {
- yylval.id = intern("**",2);
+ pylval.id = intern("**",2);
p->lstate = EXPR_BEG;
return tOP_ASGN;
}
}
else {
if (c == '=') {
- yylval.id = intern_c('*');
+ pylval.id = intern_c('*');
p->lstate = EXPR_BEG;
return tOP_ASGN;
}
}
if (c == '<') {
if ((c = nextc(p)) == '=') {
- yylval.id = intern("<<",2);
+ pylval.id = intern("<<",2);
p->lstate = EXPR_BEG;
return tOP_ASGN;
}
}
if (c == '>') {
if ((c = nextc(p)) == '=') {
- yylval.id = intern(">>",2);
+ pylval.id = intern(">>",2);
p->lstate = EXPR_BEG;
return tOP_ASGN;
}
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;
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;
}
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;
}
return tOROP;
}
if (c == '=') {
- yylval.id = intern_c('|');
+ pylval.id = intern_c('|');
p->lstate = EXPR_BEG;
return tOP_ASGN;
}
return '+';
}
if (c == '=') {
- yylval.id = intern_c('+');
+ pylval.id = intern_c('+');
p->lstate = EXPR_BEG;
return tOP_ASGN;
}
return '-';
}
if (c == '=') {
- yylval.id = intern_c('-');
+ pylval.id = intern_c('-');
p->lstate = EXPR_BEG;
return tOP_ASGN;
}
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') {
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') {
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 == '_') {
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) {
}
else {
pushback(p, c);
- yylval.nd = new_int(p, "0", 10);
+ pylval.nd = new_int(p, "0", 10);
return tINTEGER;
}
}
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));
}
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;
}
return tREGEXP_BEG;
}
if ((c = nextc(p)) == '=') {
- yylval.id = intern_c('/');
+ pylval.id = intern_c('/');
p->lstate = EXPR_BEG;
return tOP_ASGN;
}
case '^':
if ((c = nextc(p)) == '=') {
- yylval.id = intern_c('^');
+ pylval.id = intern_c('^');
p->lstate = EXPR_BEG;
return tOP_ASGN;
}
}
}
if ((c = nextc(p)) == '=') {
- yylval.id = intern_c('%');
+ pylval.id = intern_c('%');
p->lstate = EXPR_BEG;
return tOP_ASGN;
}
tokadd(p, '$');
tokadd(p, c);
tokfix(p);
- yylval.id = intern_cstr(tok(p));
+ pylval.id = intern_cstr(tok(p));
return tGVAR;
case '-':
pushback(p, c);
gvar:
tokfix(p);
- yylval.id = intern_cstr(tok(p));
+ pylval.id = intern_cstr(tok(p));
return tGVAR;
case '&': /* $&: last match */
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':
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;
c = nextc(p);
}
if (c < 0) {
- if (p->bidx == 1) {
+ if (p->tidx == 1) {
yyerror(p, "incomplete instance variable syntax");
}
else {
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 {
p->lstate = EXPR_BEG;
nextc(p);
tokfix(p);
- yylval.id = intern_cstr(tok(p));
+ pylval.id = intern_cstr(tok(p));
return tLABEL;
}
}
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) {
{
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;
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;
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);
}
if (c && c->dump_result) {
mrb_parser_dump(p->mrb, p->tree, 0);
}
-
}
MRB_CATCH(p->jmp) {
yyerror(p, "memory allocation error");
p->pool = pool;
p->s = p->send = NULL;
-#ifndef MRB_DISBLE_STDIO
+#ifndef MRB_DISABLE_STDIO
p->f = NULL;
#endif
#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;
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);
}
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);
}
{
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;
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;
return mrb_undef_value();
}
if (!p->tree || p->nerr) {
+ c->parser_nerr = p->nerr;
if (p->capture_errors) {
char buf[256];
int n;
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();
}
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) {
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;
}
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
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
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);
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",
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:
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:
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:
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"]) <<
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
ary << val.__svalue
block.call(ary.dup) if ary.size == n
end
+ nil
end
##
end
end
block.call(ary) unless ary.empty?
+ nil
end
##
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
# 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
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
# 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
##
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
["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
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
# = 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':
#
# - 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
}
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)
def reject(&block)
Lazy.new(self){|yielder, val|
- if not block.call(val)
+ unless block.call(val)
yielder << val
end
}
-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?
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)
#
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
#
# 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
##
# just for internal
class Generator
+ include Enumerable
def initialize(&block)
raise TypeError, "wrong argument type #{self.class} (expected Proc)" unless block.kind_of? Proc
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
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
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|
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
-#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)
-#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)
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
-#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)
}
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)) {
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)
{
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)
{
}
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;
}
}
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;
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;
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);
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);
}
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;
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;
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);
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
#include <stdlib.h>
-#include "mruby.h"
+#include <mruby.h>
static mrb_value
f_exit(mrb_state *mrb, mrb_value self)
-#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:
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");
}
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;
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];
/* 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--) {
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;
}
/*
{
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);
}
/*
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);
}
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 */
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);
}
* 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)
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);
}
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
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
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
##
# 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
#
#
def invert
- h = Hash.new
+ h = self.class.new
self.each {|k, v| h[v] = k }
h
end
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
** 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:
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++) {
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' }
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 })
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
--- /dev/null
+MRuby::Gem::Specification.new('mruby-inline-struct') do |spec|
+ spec.license = 'MIT'
+ spec.author = 'mruby developers'
+ spec.summary = 'inline structure'
+end
--- /dev/null
+#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));
+}
--- /dev/null
+##
+# 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
-#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:
* 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
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));
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)
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
** See Copyright Notice in mruby.h
*/
-#include "mruby.h"
-#include "mruby/array.h"
+#include <mruby.h>
+#include <mruby/array.h>
#include <math.h>
}
/* 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>
def div(other)
self.divmod(other)[0]
end
+
+ def zero?
+ self == 0
+ end
+
+ def nonzero?
+ if self == 0
+ nil
+ else
+ self
+ end
+ end
end
#include <limits.h>
-#include "mruby.h"
+#include <mruby.h>
static mrb_value
mrb_int_chr(mrb_state *mrb, mrb_value x)
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
-#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:
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;
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
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
], 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
-#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;
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;
else {
obj_count->counts[obj->tt]++;
}
+ return MRB_EACH_OBJ_OK;
}
/*
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) {
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;
}
/*
-#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;
-#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)
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, "-");
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);
}
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;
-#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)
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)
/*
** 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 */
/*
** 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
** 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>
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));
}
mrb_value seed;
mt_state *t;
+ seed = get_opt(mrb);
+
/* avoid memory leaks */
t = (mt_state*)DATA_PTR(self);
if (t) {
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;
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;
}
}
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) {
}
mrb_random_rand_seed(mrb, random);
mt_rand(random);
+ len = RARRAY_LEN(ary);
if (!given) { /* pick one element */
switch (len) {
case 0:
** 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);
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
--- /dev/null
+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
-#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)
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);
/*
* 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
*
{
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;
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);
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);
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();
}
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());
}
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
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
** 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 */
** 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);
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;
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");
}
#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);*/\
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) \
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
mrb_int n;
mrb_int width;
mrb_int prec;
- int flags = FNONE;
int nextarg = 1;
int posarg = 0;
mrb_value nextvalue;
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 */
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");
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;
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)) {
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;
}
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;
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;
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;
len = (mrb_int)size;
}
- if (dots) {
- prec -= 2;
- width -= 2;
- }
-
if (*p == 'X') {
char *pp = s;
int c;
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);
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;
if (!isfinite(fval)) {
const char *expr;
const int elen = 3;
+ char sign = '\0';
if (isnan(fval)) {
expr = "NaN";
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;
}
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;
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
spec.license = 'MIT'
spec.author = 'mruby developers'
spec.summary = 'String class extension'
+ spec.add_test_dependency 'mruby-enumerator', core: 'mruby-enumerator'
end
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
#
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
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
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
# "hello".lstrip! #=> nil
#
def lstrip!
+ raise RuntimeError, "can't modify frozen String" if frozen?
s = self.lstrip
(s == self) ? nil : self.replace(s)
end
# "hello".rstrip! #=> nil
#
def rstrip!
+ raise RuntimeError, "can't modify frozen String" if frozen?
s = self.rstrip
(s == self) ? nil : self.replace(s)
end
# <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
# 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?
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]
# "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
##
# "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
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
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
#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)
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)
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();
}
return str;
}
+static mrb_value mrb_fixnum_chr(mrb_state *mrb, mrb_value num);
+
/*
* call-seq:
* str << integer -> str
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;
}
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;
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++;
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;
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) {
if (*e == 0xff) {
mrb_str_cat_lit(mrb, result, "\x01");
(*e) = 0;
- } else
+ }
+ else
(*e)++;
break;
}
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;
}
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] =
{
{
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)
{
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());
}
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]
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))
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
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
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
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
#
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
*/
#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)
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;
}
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:
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)
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
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);
}
}
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);
}
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;
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);
}
mrb_struct_init_copy(mrb_state *mrb, mrb_value copy)
{
mrb_value s;
- mrb_int i, len;
mrb_get_args(mrb, "o", &s);
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 */
}
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;
}
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 */
}
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;
}
"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;
}
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) {
{
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 */
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
# 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
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
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
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)
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
-#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;
#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 *);
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());
}
#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);
}
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
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")
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
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)
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|
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
*/
#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 */
{ "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",
};
static const char wday_names[7][4] = {
"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat",
};
+#endif
struct mrb_time {
time_t sec;
/** 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;
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
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;
}
}
#endif
tm->timezone = MRB_TIMEZONE_LOCAL;
- mrb_time_update_datetime(tm);
+ time_update_datetime(mrb, tm);
return tm;
}
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);
}
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)
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;
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) {
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);
}
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)
{
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);
}
{
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);
}
{
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);
}
{
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,
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);
}
{
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);
}
{
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);
}
{
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);
}
{
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);
}
{
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);
}
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);
}
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;
}
{
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;
}
{
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);
}
{
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);
}
{
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);
}
{
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);
}
{
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);
}
{
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);
}
{
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);
}
{
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;
}
{
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);
}
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
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
--- /dev/null
+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
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
+# coding: utf-8
##
# 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
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
+++ /dev/null
-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
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
#
# 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
##
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
#
# 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|
}.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
-#include "mruby.h"
-#include "mruby/irep.h"
+#include <mruby.h>
+#include <mruby/irep.h>
extern const uint8_t mrblib_irep[];
# 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)
#
# 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
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
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
# 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
m
when "'"
post
+ when "1", "2", "3", "4", "5", "6", "7", "8", "9"
+ ""
else
self[j, 2]
end
#
# 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
##
#
# 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)
#
# 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
##
#
# 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)
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
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
** 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;
}
{
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);
}
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);
}
}
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);
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);
}
}
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;
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);
}
}
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);
}
}
}
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
{
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;
}
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);
}
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
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", ×);
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);
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;
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);
}
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);
}
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++);
}
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
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;
}
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;
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]);
}
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);
/* 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;
}
{
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);
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;
}
}
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 {
}
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);
}
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;
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);
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
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();
}
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
{
struct RArray *a = mrb_ary_ptr(self);
- return mrb_fixnum_value(a->len);
+ return mrb_fixnum_value(ARY_LEN(a));
}
MRB_API mrb_value
{
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;
}
{
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
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;
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));
}
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 */
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));
** 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;
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;
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
#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));
}
*/
#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)
}
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);
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));
}
}
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));
}
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*
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);
}
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);
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)
{
}
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));
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);
}
}
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;
}
}
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;
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 '?':
p = va_arg(ap, mrb_value*);
if (i < argc) {
- *p = *sp++;
+ *p = ARGV[arg_i++];
i++;
}
}
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++;
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++;
}
}
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++;
}
}
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++;
}
}
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++;
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++;
}
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++;
}
}
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++;
}
}
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;
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++;
}
}
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++;
}
if (i < argc) {
mrb_value ss;
- ss = *sp++;
+ ss = ARGV[arg_i++];
*symp = to_sym(mrb, ss);
i++;
}
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;
}
}
{
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;
break;
}
}
+
+#undef ARGV
+
if (!c && argc > i) {
mrb_raise(mrb, E_ARGUMENT_ERROR, "wrong number of arguments");
}
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*
ic->super = super;
if (m->tt == MRB_TT_ICLASS) {
ic->c = m->c;
- } else {
+ }
+ else {
ic->c = m;
}
return ic;
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;
}
}
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;
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:
{
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);
}
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);
}
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);
}
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;
}
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;
}
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
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)) {
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 *
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, ">");
}
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();
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");
}
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;
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;
}
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;
}
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)
{
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 */
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 */
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 */
-#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
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;
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:
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;
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));
** See Copyright Notice in mruby.h
*/
-#include "mruby.h"
+#include <mruby.h>
void
mrb_init_comparable(mrb_state *mrb)
#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)
if (!(pc < (*it)->start_pos)) {
ret = it + 1;
count -= step + 1;
- } else { count = step; }
+ }
+ else { count = step; }
}
--ret;
if (!(pc < it->start_pos)) {
ret = it + 1;
count -= step + 1;
- } else { count = step; }
+ }
+ else { count = step; }
}
--ret;
#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
{
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 */
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);
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"
"__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;
** See Copyright Notice in mruby.h
*/
-#include "mruby.h"
+#include <mruby.h>
void
mrb_init_enumerable(mrb_state *mrb)
#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)
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;
* 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>.
*
*/
* 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
{
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"));
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)
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;
}
}
+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();
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
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++;
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());
}
}
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
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;
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;
}
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)
{
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);
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
}
/* this header file is to be removed soon.
added for compatibility purpose (1.0.0) */
-#include "mruby/error.h"
+#include <mruby/error.h>
** 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)
case MRB_TT_EXCEPTION:
case MRB_TT_FILE:
case MRB_TT_DATA:
+ case MRB_TT_ISTRUCT:
default:
return MakeID(mrb_ptr(obj));
}
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
#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;
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++='.';
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);
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--;
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) {
return fmt_fp(f, flo, 0, p, 0, *fmt);
default:
return -1;
- }
+ }
}
mrb_value
#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
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.
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;
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 {\
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 {
#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)
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);
}
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) {
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.
*/
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) {
}
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*
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
{
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);
}
}
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;
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;
}
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:
#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;
{
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;
{
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:
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;
}
/* 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);
}
}
children += i;
/* mark ensure stack */
- children += (c->ci) ? c->ci->eidx : 0;
+ children += c->eidx;
/* mark closure */
if (c->cibase) {
case MRB_TT_ARRAY:
{
struct RArray *a = (struct RArray*)obj;
- children += a->len;
+ children += ARY_LEN(a);
}
break;
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;
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++;
}
{
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;
{
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;
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).
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);
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;
}
}
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
** 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);
}
}
-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);
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;
}
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;
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);
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
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++) {
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
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);
}
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;
}
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:
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 */
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;
}
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;
}
mrb_value key;
mrb_get_args(mrb, "o", &key);
+ mrb_hash_modify(mrb, self);
return mrb_hash_delete_key(mrb, self, key);
}
}
}
- 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 */
{
khash_t(ht) *h = RHASH_TBL(hash);
+ mrb_hash_modify(mrb, hash);
if (h) kh_clear(ht, mrb, h);
return 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;
*
*/
-static mrb_value
+MRB_API mrb_value
mrb_hash_values(mrb_state *mrb, mrb_value hash)
{
khash_t(ht) *h = RHASH_TBL(hash);
** See Copyright Notice in mruby.h
*/
-#include "mruby.h"
+#include <mruby.h>
void mrb_init_symtbl(mrb_state*);
void mrb_init_class(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*);
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;
** 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,
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:
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:
* <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));
{
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 */
/* 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) {
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
case MRB_TT_EXCEPTION:
mrb_iv_copy(mrb, dest, obj);
break;
+ case MRB_TT_ISTRUCT:
+ mrb_istruct_copy(dest, obj);
+ break;
default:
break;
}
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);
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:
}
-/* 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)
{
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 */
/*
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;
}
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)
{
}
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;
}
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);
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;
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());
}
}
}
}
}
- 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)
{
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 */
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 */
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 */
#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
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) {
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;
return irep;
}
-MRB_API mrb_irep*
+mrb_irep*
mrb_read_irep(mrb_state *mrb, const uint8_t *bin)
{
#ifdef MRB_USE_ETEXT_EDATA
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
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
#ifndef MRB_DISABLE_STDIO
-MRB_API mrb_irep*
+mrb_irep*
mrb_read_irep_file(mrb_state *mrb, FILE* fp)
{
mrb_irep *irep = NULL;
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;
}
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
#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)
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);
}
mrb_float mod;
if (y == 0.0) {
- div = INFINITY;
+ if (x == 0.0) {
+ div = NAN;
+ }
+ else {
+ div = INFINITY;
+ }
mod = NAN;
}
else {
}
}
+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:
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;
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:
{
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);
{
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);
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;
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);
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)
{
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));
}
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));
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);
}
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);
}
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 */
/*
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 */
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 */
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)
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);
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:
{
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);
}
{
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);
}
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");
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);
}
/* 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));
** 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)
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 */
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 */
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 */
{
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;
/* {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
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;
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;
case MRB_TT_MODULE:
case MRB_TT_CLASS:
case MRB_TT_ICLASS:
+ case MRB_TT_SCLASS:
break;
default:
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);
if (base != 0) {
tmp = mrb_check_string_type(mrb, val);
if (!mrb_nil_p(tmp)) {
+ val = tmp;
goto string_conv;
}
arg_error:
/* this header file is to be removed soon. */
-#include "mruby/opcode.h"
+#include <mruby/opcode.h>
#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
** 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
** 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),
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;
e = mrb->c->ci->env;
}
p->env = e;
+ mrb_field_write_barrier(mrb, (struct RBasic *)p, (struct RBasic *)p->env);
}
struct RProc *
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;
}
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
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;
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);
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());
** 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)
{
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;
}
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;
}
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);
}
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;
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;
}
{
struct RRange *rr;
struct RRange *ro;
- mrb_value obj;
+ mrb_value obj, v1, v2;
mrb_get_args(mrb, "o", &obj);
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();
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;
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;
}
*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) */
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);
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);
}
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)) {
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;
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));
#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*);
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]));
}
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);
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)
{
irep = (mrb_irep *)mrb_malloc(mrb, sizeof(mrb_irep));
*irep = mrb_irep_zero;
irep->refcnt = 1;
+ irep->own_filename = FALSE;
return irep;
}
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
** 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;
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");
}
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);
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;
}
}
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);
}
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;
}
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
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);
}
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 {
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;
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");
}
}
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);
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';
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);
}
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)
{
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);
}
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);
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)
{
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);
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));
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') {
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 */
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 */
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;
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)) {
if (pos == -1) return mrb_nil_value();
pos = bytes2chars(RSTRING_PTR(str), pos);
+ BYTES_ALIGN_CHECK(pos);
return mrb_fixnum_value(pos);
}
{
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;
}
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;
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) {
sub = mrb_nil_value();
}
pos = chars2bytes(str, 0, pos);
- len = chars2bytes(str, pos, len);
mrb_regexp_check(mrb, sub);
switch (mrb_type(sub)) {
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;
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);
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));
}
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;
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;
break;
default:
base = 8;
+ break;
}
}
else if (base < -1) {
}
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:
}
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)
{
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);
}
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 */
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:
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++;
}
#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;
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 */
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;
}
#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 {
if (*++m == '*') ++m;
break;
case '!':
- if (*++m == '=') ++m;
+ switch (*++m) {
+ case '=': case '~': ++m;
+ }
break;
case '+': case '-':
if (*++m == '@') ++m;
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 */
#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)
** 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;
{
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;
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) {
return;
}
- seg = mrb_malloc(mrb, sizeof(segment));
+ seg = (segment*)mrb_malloc(mrb, sizeof(segment));
if (!seg) return;
seg->next = NULL;
seg->key[0] = sym;
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) {
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) {
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 */
return size;
}
seg = seg->next;
- size += MRB_SEGMENT_SIZE;
+ size += MRB_IV_SEGMENT_SIZE;
}
/* empty iv_tbl */
return 0;
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];
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)
{
{
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);
}
}
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",
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
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);
-#include "mruby.h"
-#include "mruby/variable.h"
+#include <mruby.h>
+#include <mruby/variable.h>
void
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));
#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
#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
# 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)
}
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;
}
}
-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;
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*
{
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;
#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) {
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;
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
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;
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;
}
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;
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 */
/*
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) {
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
{
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;
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;
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;
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;
}
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,
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
{
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;
#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
#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;
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 */
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;
}
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;
}
/* 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;
}
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;
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;
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;
}
/* 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;
}
/* 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;
}
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);
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 */
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;
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;
}
CASE(OP_FSEND) {
/* A B C R(A) := fcall(R(A),Syms(B),R(A+1),... ,R(A+C-1)) */
+ /* not implemented yet */
NEXT;
}
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;
}
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);
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;
}
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));
}
}
}
+ 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 */
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;
}
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) {
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;
}
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)) {
}
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) {
if (argv0 != argv) {
value_move(®s[1], argv, argc-mlen); /* m1 + o */
}
+ if (argc < m1) {
+ stack_clear(®s[argc+1], m1-argc);
+ }
if (mlen) {
value_move(®s[len-m2+1], &argv[argc-mlen], mlen);
}
+ if (mlen < m2) {
+ stack_clear(®s[len-m2+mlen+1], m2-mlen);
+ }
if (r) {
regs[m1+o+1] = mrb_ary_new_capa(mrb, 0);
}
}
pc += o + 1;
}
+ mrb->c->ci->argc = len;
+ /* clear local (but non-argument) variables */
+ if (irep->nlocals-len-2 > 0) {
+ stack_clear(®s[len+2], irep->nlocals-len-2);
+ }
JUMP;
}
/* 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;
}
/* 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;
+ }
}
}
}
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);
}
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;
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;
}
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);
}
value_move(mrb->c->stack, ®s[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;
}
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;
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;
}
default:
goto L_SEND;
}
- ARENA_RESTORE(mrb, ai);
+ mrb_gc_arena_restore(mrb, ai);
NEXT;
}
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):
{
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):
{
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);
}
#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;
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), ®s[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, ®s[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;
}
mrb_value v = regs[a];
int pre = GETARG_B(i);
int post = GETARG_C(i);
-
struct RArray *ary;
int len, idx;
v = mrb_ary_new_from_values(mrb, 1, ®s[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;
}
b+=2;
}
regs[GETARG_A(i)] = hash;
- ARENA_RESTORE(mrb, ai);
+ mrb_gc_arena_restore(mrb, ai);
NEXT;
}
}
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;
}
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)];
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;
}
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;
/* 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) {
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;
}
/* 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);
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;
}
/* 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);
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) {
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
--- /dev/null
+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
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"
require 'pathname'
require 'forwardable'
require 'tsort'
+require 'shellwords'
module MRuby
module Gem
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
@name = name
@initializer = block
@version = "0.0.0"
+ @mrblib_dir = "mrblib"
+ @objs_dir = "src"
MRuby::Gem.current = self
end
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"))
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
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}]
"#{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
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
@ary = tsort_dependencies gem_table.keys, gem_table, true
+ each(&:setup_compilers)
+
each do |g|
import_include_paths(g)
end
# 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
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[]
@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 = []
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
#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
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)
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
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
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
-MRuby::Toolchain.new(:clang) do |conf|
+MRuby::Toolchain.new(:clang) do |conf, _params|
toolchain :gcc
[conf.cc, conf.objc, conf.asm].each do |cc|
-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)]
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|
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|
--- /dev/null
+# 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
-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)]
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|
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
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
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
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')
$asserts.push(assertion_string("#{e.class}: ", str, iso, e, bt))
$kill_test += 1
t_print('X')
- end
+ end
ensure
$mrbtest_assert = nil
end
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
t_print("\n")
$asserts.each do |msg|
- puts msg
+ t_print "#{msg}\n"
end
$total_test = $ok_test+$ko_test+$kill_test
##
# 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?
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}"
def shellquote(s)
case RbConfig::CONFIG['host_os']
- when /mswin(?!ce)|mingw|cygwin|bccwin/
+ when /mswin(?!ce)|mingw|bccwin/
"\"#{s}\""
else
"'#{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
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
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
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
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
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
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
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
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
--- /dev/null
+##
+# 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
begin
1 * "b"
ensure
- @e = self.z
+ @e = self.zz
end
end
+ def zz
+ true
+ end
def z
true
end
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
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
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
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
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
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
h.rehash
assert_equal("b", h[[:b]])
end
+
+assert('Hash#freeze') do
+ h = {}.freeze
+ assert_raise(RuntimeError) do
+ h[:a] = 'b'
+ end
+end
# 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
--- /dev/null
+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
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
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
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
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
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'
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
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
assert_equal 6, recurse(0, 5)
end
-
--- /dev/null
+# 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
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
# 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
# @!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
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
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
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)
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
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
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
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
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
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
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
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
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
assert_raise(RuntimeError) { str.upcase! }
end
-
[: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'],
##
# 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
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
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
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]
end
assert('External command execution.') do
- class << Kernel
+ module Kernel
sym = '`'.to_sym
alias_method :old_cmd, sym
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
#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 };