diff --git a/.github/actions/launchable/setup/action.yml b/.github/actions/launchable/setup/action.yml
index 155604983ebd23..6f81531ddcbdb2 100644
--- a/.github/actions/launchable/setup/action.yml
+++ b/.github/actions/launchable/setup/action.yml
@@ -38,6 +38,19 @@ inputs:
Directory to (re-)checkout source codes. Launchable retrives the commit information
from the directory.
+ launchable-workspace:
+ required: true
+ default: ${{ github.event.repository.name }}
+ description: >-
+ A workspace name in Launchable
+
+ test-task:
+ required: true
+ default: ${{ matrix.test_task }}
+ description: >-
+ A test task that determine which tests are executed.
+ This value is used in the Launchable flavor.
+
runs:
using: composite
@@ -77,18 +90,25 @@ runs:
: # The following envs are necessary in Launchable tokenless authentication.
: # https://github.com/launchableinc/cli/blob/v1.80.1/launchable/utils/authentication.py#L20
echo "LAUNCHABLE_ORGANIZATION=${{ github.repository_owner }}" >> $GITHUB_ENV
- echo "LAUNCHABLE_WORKSPACE=${{ github.event.repository.name }}" >> $GITHUB_ENV
+ echo "LAUNCHABLE_WORKSPACE=${{ inputs.launchable-workspace }}" >> $GITHUB_ENV
: # https://github.com/launchableinc/cli/blob/v1.80.1/launchable/utils/authentication.py#L71
echo "GITHUB_PR_HEAD_SHA=${{ github.event.pull_request.head.sha || github.sha }}" >> $GITHUB_ENV
echo "LAUNCHABLE_TOKEN=${{ inputs.launchable-token }}" >> $GITHUB_ENV
if: steps.enable-launchable.outputs.enable-launchable
+ - name: Set up path
+ shell: bash
+ working-directory: ${{ inputs.srcdir }}
+ # Since updated PATH variable will be available in only subsequent actions, we need to add the path beforehand.
+ # https://docs.github.com/en/actions/using-workflows/workflow-commands-for-github-actions#adding-a-system-path
+ run: echo "$(python -msite --user-base)/bin" >> $GITHUB_PATH
+ if: steps.enable-launchable.outputs.enable-launchable && startsWith(inputs.os, 'macos')
+
- name: Set up Launchable
shell: bash
working-directory: ${{ inputs.srcdir }}
run: |
set -x
- echo "$(python -msite --user-base)/bin" >> $GITHUB_PATH
pip install --user launchable
launchable verify || true
: # The build name cannot include a slash, so we replace the string here.
@@ -135,7 +155,7 @@ runs:
working-directory: ${{ inputs.srcdir }}
post: |
: # record
- launchable record tests --flavor os=${{ inputs.os }} --flavor test_task=${{ matrix.test_task }} --flavor test_opts=${test_opts} raw ${report_path}
+ launchable record tests --flavor os=${{ inputs.os }} --flavor test_task=${{ inputs.test-task }} --flavor test_opts=${test_opts} raw ${report_path}
rm -f ${report_path}
if: ${{ always() && steps.enable-launchable.outputs.enable-launchable }}
env:
diff --git a/.github/workflows/annocheck.yml b/.github/workflows/annocheck.yml
index 6b0d49d0eb51f4..bbffefbbf070c3 100644
--- a/.github/workflows/annocheck.yml
+++ b/.github/workflows/annocheck.yml
@@ -74,7 +74,7 @@ jobs:
builddir: build
makeup: true
- - uses: ruby/setup-ruby@1d0e911f615a112e322369596f10ee0b95b010ae # v1.183.0
+ - uses: ruby/setup-ruby@161cd54b698f1fb3ea539faab2e036d409550e3c # v1.187.0
with:
ruby-version: '3.0'
bundler: none
diff --git a/.github/workflows/baseruby.yml b/.github/workflows/baseruby.yml
index b3df961bd85da5..00ad9f98cd6403 100644
--- a/.github/workflows/baseruby.yml
+++ b/.github/workflows/baseruby.yml
@@ -51,7 +51,7 @@ jobs:
- ruby-3.3
steps:
- - uses: ruby/setup-ruby@1d0e911f615a112e322369596f10ee0b95b010ae # v1.183.0
+ - uses: ruby/setup-ruby@161cd54b698f1fb3ea539faab2e036d409550e3c # v1.187.0
with:
ruby-version: ${{ matrix.ruby }}
bundler: none
diff --git a/.github/workflows/check_dependencies.yml b/.github/workflows/check_dependencies.yml
index a5b81d4896f251..cc2061272e9f98 100644
--- a/.github/workflows/check_dependencies.yml
+++ b/.github/workflows/check_dependencies.yml
@@ -55,7 +55,7 @@ jobs:
- uses: ./.github/actions/setup/directories
- - uses: ruby/setup-ruby@1d0e911f615a112e322369596f10ee0b95b010ae # v1.183.0
+ - uses: ruby/setup-ruby@161cd54b698f1fb3ea539faab2e036d409550e3c # v1.187.0
with:
ruby-version: '3.0'
bundler: none
diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml
index 1480031bfa8925..745f8fd6e5e995 100644
--- a/.github/workflows/codeql-analysis.yml
+++ b/.github/workflows/codeql-analysis.yml
@@ -32,7 +32,7 @@ permissions: # added using https://github.com/step-security/secure-workflows
jobs:
analyze:
name: Analyze
- runs-on: ${{ matrix.os }}
+ runs-on: ubuntu-latest
permissions:
actions: read # for github/codeql-action/init to get workflow details
contents: read # for actions/checkout to fetch code
@@ -56,10 +56,7 @@ jobs:
matrix:
include:
- language: cpp
- os: ubuntu-latest
- # ruby analysis used large memory. We need to use a larger runner.
- language: ruby
- os: ${{ github.repository == 'ruby/ruby' && 'macos-arm-oss' || 'ubuntu-latest' }}
steps:
- name: Checkout repository
@@ -80,15 +77,15 @@ jobs:
run: sudo rm /usr/lib/ruby/vendor_ruby/rubygems/defaults/operating_system.rb
- name: Initialize CodeQL
- uses: github/codeql-action/init@b611370bb5703a7efb587f9d136a52ea24c5c38c # v3.25.11
+ uses: github/codeql-action/init@4fa2a7953630fd2f3fb380f21be14ede0169dd4f # v3.25.12
with:
languages: ${{ matrix.language }}
- name: Autobuild
- uses: github/codeql-action/autobuild@b611370bb5703a7efb587f9d136a52ea24c5c38c # v3.25.11
+ uses: github/codeql-action/autobuild@4fa2a7953630fd2f3fb380f21be14ede0169dd4f # v3.25.12
- name: Perform CodeQL Analysis
- uses: github/codeql-action/analyze@b611370bb5703a7efb587f9d136a52ea24c5c38c # v3.25.11
+ uses: github/codeql-action/analyze@4fa2a7953630fd2f3fb380f21be14ede0169dd4f # v3.25.12
with:
category: '/language:${{ matrix.language }}'
upload: False
@@ -118,7 +115,7 @@ jobs:
continue-on-error: true
- name: Upload SARIF
- uses: github/codeql-action/upload-sarif@b611370bb5703a7efb587f9d136a52ea24c5c38c # v3.25.11
+ uses: github/codeql-action/upload-sarif@4fa2a7953630fd2f3fb380f21be14ede0169dd4f # v3.25.12
with:
sarif_file: sarif-results/${{ matrix.language }}.sarif
continue-on-error: true
diff --git a/.github/workflows/dependabot_automerge.yml b/.github/workflows/dependabot_automerge.yml
index 80112a0af3500b..6b5f865f6253d9 100644
--- a/.github/workflows/dependabot_automerge.yml
+++ b/.github/workflows/dependabot_automerge.yml
@@ -11,7 +11,7 @@ jobs:
steps:
- name: Dependabot metadata
- uses: dependabot/fetch-metadata@5e5f99653a5b510e8555840e80cbf1514ad4af38 # v2.1.0
+ uses: dependabot/fetch-metadata@dbb049abf0d677abbd7f7eee0375145b417fdd34 # v2.2.0
id: metadata
- name: Wait for status checks
diff --git a/.github/workflows/macos.yml b/.github/workflows/macos.yml
index 3c312829a0b901..df74904a542790 100644
--- a/.github/workflows/macos.yml
+++ b/.github/workflows/macos.yml
@@ -26,10 +26,14 @@ jobs:
matrix:
include:
- test_task: check
+ os: macos-14
- test_task: test-all
test_opts: --repeat-count=2
+ os: macos-14
- test_task: test-bundler-parallel
+ os: macos-14
- test_task: test-bundled-gems
+ os: macos-14
- test_task: check
os: macos-12
- test_task: check
@@ -39,7 +43,7 @@ jobs:
env:
GITPULLOPTIONS: --no-tags origin ${{ github.ref }}
- runs-on: ${{ matrix.os || (github.repository == 'ruby/ruby' && 'macos-arm-oss' || 'macos-14') }}
+ runs-on: ${{ matrix.os }}
if: >-
${{!(false
@@ -94,7 +98,7 @@ jobs:
- name: Set up Launchable
uses: ./.github/actions/launchable/setup
with:
- os: ${{ matrix.os || (github.repository == 'ruby/ruby' && 'macos-arm-oss' || 'macos-14') }}
+ os: ${{ matrix.os }}
test-opts: ${{ matrix.test_opts }}
launchable-token: ${{ secrets.LAUNCHABLE_TOKEN }}
builddir: build
@@ -129,7 +133,7 @@ jobs:
- uses: ./.github/actions/slack
with:
- label: ${{ matrix.os || (github.repository == 'ruby/ruby' && 'macos-arm-oss' || 'macos-14') }} / ${{ matrix.test_task }}
+ label: ${{ matrix.os }} / ${{ matrix.test_task }}
SLACK_WEBHOOK_URL: ${{ secrets.SIMPLER_ALERTS_URL }} # ruby-lang slack: ruby/simpler-alerts-bot
if: ${{ failure() }}
diff --git a/.github/workflows/mingw.yml b/.github/workflows/mingw.yml
index ad96de69398771..c0be674d4b3eed 100644
--- a/.github/workflows/mingw.yml
+++ b/.github/workflows/mingw.yml
@@ -66,7 +66,7 @@ jobs:
steps:
- name: Set up Ruby & MSYS2
- uses: ruby/setup-ruby@1d0e911f615a112e322369596f10ee0b95b010ae # v1.183.0
+ uses: ruby/setup-ruby@161cd54b698f1fb3ea539faab2e036d409550e3c # v1.187.0
with:
ruby-version: ${{ matrix.baseruby }}
diff --git a/.github/workflows/rjit-bindgen.yml b/.github/workflows/rjit-bindgen.yml
index 25a3814c2346e4..fd1b638d9e0f6c 100644
--- a/.github/workflows/rjit-bindgen.yml
+++ b/.github/workflows/rjit-bindgen.yml
@@ -47,7 +47,7 @@ jobs:
steps:
- name: Set up Ruby
- uses: ruby/setup-ruby@1d0e911f615a112e322369596f10ee0b95b010ae # v1.183.0
+ uses: ruby/setup-ruby@161cd54b698f1fb3ea539faab2e036d409550e3c # v1.187.0
with:
ruby-version: '3.1'
diff --git a/.github/workflows/rjit.yml b/.github/workflows/rjit.yml
index 75c8152ce565d0..0d8b5f24ddc279 100644
--- a/.github/workflows/rjit.yml
+++ b/.github/workflows/rjit.yml
@@ -67,6 +67,8 @@ jobs:
srcdir: src
builddir: build
makeup: true
+ # Set fetch-depth: 10 so that Launchable can receive commits information.
+ fetch-depth: 10
- name: Run configure
env:
@@ -77,19 +79,33 @@ jobs:
- run: $SETARCH make
+ - name: Set up Launchable
+ uses: ./.github/actions/launchable/setup
+ with:
+ os: ubuntu-22.04
+ test-task: test
+ launchable-token: ${{ secrets.LAUNCHABLE_TOKEN }}
+ builddir: build
+ srcdir: src
+ launchable-workspace: ruby-make-btest
+ test-opts: ${{ matrix.run_opts }}
+ continue-on-error: true
+
- name: make test
run: |
$SETARCH make -s test RUN_OPTS="$RUN_OPTS"
timeout-minutes: 30
env:
GNUMAKEFLAGS: ''
- RUBY_TESTOPTS: '--tty=no'
+ RUBY_TESTOPTS: >-
+ ${{ env.TESTS }}
+ --tty=no
RUN_OPTS: ${{ matrix.run_opts }}
- name: make test-all
run: |
$SETARCH make -s test-all RUN_OPTS="$RUN_OPTS"
- timeout-minutes: 40
+ timeout-minutes: 60
env:
GNUMAKEFLAGS: ''
RUBY_TESTOPTS: '-q --tty=no'
diff --git a/.github/workflows/scorecards.yml b/.github/workflows/scorecards.yml
index e0104bfd123099..85dd475fc6c63d 100644
--- a/.github/workflows/scorecards.yml
+++ b/.github/workflows/scorecards.yml
@@ -67,6 +67,6 @@ jobs:
# Upload the results to GitHub's code scanning dashboard.
- name: 'Upload to code-scanning'
- uses: github/codeql-action/upload-sarif@b611370bb5703a7efb587f9d136a52ea24c5c38c # v2.1.27
+ uses: github/codeql-action/upload-sarif@4fa2a7953630fd2f3fb380f21be14ede0169dd4f # v2.1.27
with:
sarif_file: results.sarif
diff --git a/.github/workflows/spec_guards.yml b/.github/workflows/spec_guards.yml
index df95ffcf2e9da7..c6f0983eeba9d4 100644
--- a/.github/workflows/spec_guards.yml
+++ b/.github/workflows/spec_guards.yml
@@ -47,7 +47,7 @@ jobs:
steps:
- uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
- - uses: ruby/setup-ruby@1d0e911f615a112e322369596f10ee0b95b010ae # v1.183.0
+ - uses: ruby/setup-ruby@161cd54b698f1fb3ea539faab2e036d409550e3c # v1.187.0
with:
ruby-version: ${{ matrix.ruby }}
bundler: none
diff --git a/.github/workflows/ubuntu.yml b/.github/workflows/ubuntu.yml
index 7f65be0339421a..cd94d62b5f361b 100644
--- a/.github/workflows/ubuntu.yml
+++ b/.github/workflows/ubuntu.yml
@@ -34,7 +34,9 @@ jobs:
- test_task: check
configure: '--enable-shared --enable-load-relative'
- test_task: check
- configure: '--with-shared-gc'
+ shared_gc: true
+ shared_gc_dir: '/home/runner/ruby_gc'
+ configure: '--with-shared-gc=/home/runner/ruby_gc'
- test_task: test-bundler-parallel
- test_task: test-bundled-gems
- test_task: check
@@ -67,7 +69,7 @@ jobs:
with:
arch: ${{ matrix.arch }}
- - uses: ruby/setup-ruby@1d0e911f615a112e322369596f10ee0b95b010ae # v1.183.0
+ - uses: ruby/setup-ruby@161cd54b698f1fb3ea539faab2e036d409550e3c # v1.187.0
with:
ruby-version: '3.0'
bundler: none
@@ -93,6 +95,13 @@ jobs:
- run: $SETARCH make prepare-gems
if: ${{ matrix.test_task == 'test-bundled-gems' }}
+ - name: Build shared GC
+ run: >
+ echo "RUBY_GC_LIBRARY=librubygc.default.so" >> $GITHUB_ENV &&
+ mkdir ${{ matrix.shared_gc_dir }} &&
+ make shared-gc SHARED_GC=default
+ if: ${{ matrix.shared_gc }}
+
- run: $SETARCH make
- name: Set test options for skipped tests
diff --git a/.github/workflows/wasm.yml b/.github/workflows/wasm.yml
index 579cae55fb6389..c43641b93658e5 100644
--- a/.github/workflows/wasm.yml
+++ b/.github/workflows/wasm.yml
@@ -100,7 +100,7 @@ jobs:
run: |
echo "WASI_SDK_PATH=/opt/wasi-sdk" >> $GITHUB_ENV
- - uses: ruby/setup-ruby@1d0e911f615a112e322369596f10ee0b95b010ae # v1.183.0
+ - uses: ruby/setup-ruby@161cd54b698f1fb3ea539faab2e036d409550e3c # v1.187.0
with:
ruby-version: '3.0'
bundler: none
@@ -136,7 +136,7 @@ jobs:
- run: tar cfz ../install.tar.gz -C ../install .
- name: Upload artifacts
- uses: actions/upload-artifact@65462800fd760344b1a7b4382951275a0abb4808 # v4.3.3
+ uses: actions/upload-artifact@0b2256b8c012f0828dc542b3febcab082c67f72b # v4.3.4
with:
name: ruby-wasm-install
path: ${{ github.workspace }}/install.tar.gz
@@ -164,7 +164,7 @@ jobs:
- name: Save Pull Request number
if: ${{ github.event_name == 'pull_request' }}
run: echo "${{ github.event.pull_request.number }}" >> ${{ github.workspace }}/github-pr-info.txt
- - uses: actions/upload-artifact@65462800fd760344b1a7b4382951275a0abb4808 # v4.3.3
+ - uses: actions/upload-artifact@0b2256b8c012f0828dc542b3febcab082c67f72b # v4.3.4
if: ${{ github.event_name == 'pull_request' }}
with:
name: github-pr-info
diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml
index b05c6d59d99365..cb492aebc335a0 100644
--- a/.github/workflows/windows.yml
+++ b/.github/workflows/windows.yml
@@ -54,6 +54,7 @@ jobs:
env:
GITPULLOPTIONS: --no-tags origin ${{ github.ref }}
OS_VER: windows-${{ matrix.vs < 2022 && '2019' || matrix.vs }}
+ VCPKG_DEFAULT_TRIPLET: ${{ matrix.target || 'x64' }}-windows
steps:
- run: md build
@@ -87,7 +88,7 @@ jobs:
${{ steps.find-tools.outputs.needs }}
if: ${{ steps.find-tools.outputs.needs != '' }}
- - uses: ruby/setup-ruby@1d0e911f615a112e322369596f10ee0b95b010ae # v1.183.0
+ - uses: ruby/setup-ruby@161cd54b698f1fb3ea539faab2e036d409550e3c # v1.187.0
with:
ruby-version: '3.0'
bundler: none
@@ -143,25 +144,16 @@ jobs:
- name: Install libraries with vcpkg
run: |
- vcpkg install --triplet x64-windows
+ vcpkg install
working-directory: src
env:
VCPKG_BINARY_SOURCES: "clear;x-gha,readwrite"
- # We use OpenSSL instealled by vcpkg instead
- - name: disable system OpenSSL
- run: |
- for %%I in (libcrypto-1_1-x64 libssl-1_1-x64) do (
- ren c:\Windows\System32\%%I.dll %%I.dll_
- )
- # windows-2019 image doesn't have OpenSSL as of 2023/9/14
- if: ${{ matrix.vs != 2019 }}
-
# TODO: We should use `../src` instead of `D:/a/ruby/ruby/src`
- name: Configure
run: >-
../src/win32/configure.bat --disable-install-doc
- --with-opt-dir=D:/a/ruby/ruby/src/vcpkg_installed/x64-windows
+ --with-opt-dir=D:/a/ruby/ruby/src/vcpkg_installed/%VCPKG_DEFAULT_TRIPLET%
- run: nmake prepare-vcpkg
diff --git a/.github/workflows/yjit-macos.yml b/.github/workflows/yjit-macos.yml
index 6531737120af37..7c8a0c77d0cc07 100644
--- a/.github/workflows/yjit-macos.yml
+++ b/.github/workflows/yjit-macos.yml
@@ -24,7 +24,7 @@ jobs:
cargo:
name: cargo test
- runs-on: ${{ github.repository == 'ruby/ruby' && 'macos-arm-oss' || 'macos-14' }}
+ runs-on: macos-14
if: >-
${{!(false
@@ -68,7 +68,7 @@ jobs:
RUN_OPTS: ${{ matrix.yjit_opts }}
SPECOPTS: ${{ matrix.specopts }}
- runs-on: ${{ github.repository == 'ruby/ruby' && 'macos-arm-oss' || 'macos-14' }}
+ runs-on: macos-14
if: >-
${{!(false
@@ -119,7 +119,7 @@ jobs:
- name: Set up Launchable
uses: ./.github/actions/launchable/setup
with:
- os: ${{ github.repository == 'ruby/ruby' && 'macos-arm-oss' || 'macos-14' }}
+ os: macos-14
test-opts: ${{ matrix.configure }}
launchable-token: ${{ secrets.LAUNCHABLE_TOKEN }}
builddir: build
@@ -158,7 +158,7 @@ jobs:
result:
if: ${{ always() }}
name: ${{ github.workflow }} result
- runs-on: ${{ github.repository == 'ruby/ruby' && 'macos-arm-oss' || 'macos-14' }}
+ runs-on: macos-14
needs: [make]
steps:
- run: exit 1
diff --git a/.github/workflows/yjit-ubuntu.yml b/.github/workflows/yjit-ubuntu.yml
index 452667425269bd..6c220fc5dc0168 100644
--- a/.github/workflows/yjit-ubuntu.yml
+++ b/.github/workflows/yjit-ubuntu.yml
@@ -134,7 +134,7 @@ jobs:
- uses: ./.github/actions/setup/ubuntu
- - uses: ruby/setup-ruby@1d0e911f615a112e322369596f10ee0b95b010ae # v1.183.0
+ - uses: ruby/setup-ruby@161cd54b698f1fb3ea539faab2e036d409550e3c # v1.187.0
with:
ruby-version: '3.0'
bundler: none
diff --git a/NEWS.md b/NEWS.md
index 314b374b72d672..6844332585a102 100644
--- a/NEWS.md
+++ b/NEWS.md
@@ -25,6 +25,12 @@ Note that each entry is kept to a minimum, see links for details.
* Keyword arguments are no longer allowed in index assignment
(e.g. `a[0, kw: 1] = 2`). [[Bug #20218]]
+* `GC.config` added to allow setting configuration variables on the Garbage
+ Collector. [[Feature #20443]]
+
+* GC configuration parameter `rgengc_allow_full_mark` introduced. When `false`
+ GC will only mark young objects. Default is `true`. [[Feature #20443]]
+
## Core classes updates
Note: We're only listing outstanding class updates.
@@ -54,7 +60,7 @@ The following default gems are updated.
* erb 4.0.4
* fiddle 1.1.3
* io-console 0.7.2
-* irb 1.13.2
+* irb 1.14.0
* json 2.7.2
* net-http 0.4.1
* optparse 0.5.0
@@ -70,11 +76,11 @@ The following bundled gems are updated.
* minitest 5.24.1
* rake 13.2.1
* test-unit 3.6.2
-* rexml 3.3.1
+* rexml 3.3.2
* net-ftp 0.3.7
* net-imap 0.4.14
* net-smtp 0.5.0
-* rbs 3.5.1
+* rbs 3.5.2
* typeprof 0.21.11
* debug 1.9.2
* racc 1.8.0
@@ -152,4 +158,5 @@ See GitHub releases like [GitHub Releases of Logger](https://github.com/ruby/log
[Bug #20218]: https://bugs.ruby-lang.org/issues/20218
[Feature #20265]: https://bugs.ruby-lang.org/issues/20265
[Feature #20429]: https://bugs.ruby-lang.org/issues/20429
+[Feature #20443]: https://bugs.ruby-lang.org/issues/20443
[Feature #20497]: https://bugs.ruby-lang.org/issues/20497
diff --git a/array.c b/array.c
index 103b9cf73cbbc4..84d38c3868b174 100644
--- a/array.c
+++ b/array.c
@@ -934,9 +934,6 @@ ary_make_shared(VALUE ary)
return ary;
}
else if (OBJ_FROZEN(ary)) {
- if (!ARY_EMBED_P(ary)) {
- ary_shrink_capa(ary);
- }
return ary;
}
else {
@@ -3638,41 +3635,6 @@ rb_ary_sort_by_bang(VALUE ary)
return ary;
}
-
-/*
- * call-seq:
- * array.map {|element| ... } -> new_array
- * array.map -> new_enumerator
- *
- * Calls the block, if given, with each element of +self+;
- * returns a new +Array+ whose elements are the return values from the block:
- *
- * a = [:foo, 'bar', 2]
- * a1 = a.map {|element| element.class }
- * a1 # => [Symbol, String, Integer]
- *
- * Returns a new Enumerator if no block given:
- * a = [:foo, 'bar', 2]
- * a1 = a.map
- * a1 # => #
Fixnum
s, Symbol
s
- * true
, false
, and nil
) are
- * never returned. In the example below, #each_object returns both
- * the numbers we defined and several constants defined in the Math
- * module.
- *
- * If no block is given, an enumerator is returned instead.
- *
- * a = 102.7
- * b = 95 # Won't be returned
- * c = 12345678987654321
- * count = ObjectSpace.each_object(Numeric) {|x| p x }
- * puts "Total count: #{count}"
- *
- * produces:
- *
- * 12345678987654321
- * 102.7
- * 2.71828182845905
- * 3.14159265358979
- * 2.22044604925031e-16
- * 1.7976931348623157e+308
- * 2.2250738585072e-308
- * Total count: 7
- *
- */
-
-static VALUE
-os_each_obj(int argc, VALUE *argv, VALUE os)
-{
- VALUE of;
-
- of = (!rb_check_arity(argc, 0, 1) ? 0 : argv[0]);
- RETURN_ENUMERATOR(os, 1, &of);
- return os_obj_of(of);
-}
-
-static void
-should_belong_to_current_objspace(VALUE obj)
-{
- if (GET_OBJSPACE_OF_VALUE(obj) != &rb_objspace) {
- rb_raise(rb_eRactorIsolationError, "cannot define or undefine finalizers for objects belonging to another Ractor");
- }
-}
-
-/*
- * call-seq:
- * ObjectSpace.undefine_finalizer(obj)
- *
- * Removes all finalizers for obj.
- *
- */
-
-static VALUE
-undefine_final(VALUE os, VALUE obj)
-{
- return rb_undefine_finalizer(obj);
-}
-
-VALUE
-rb_undefine_finalizer(VALUE obj)
-{
- should_belong_to_current_objspace(obj);
- rb_objspace_t *objspace = &rb_objspace;
- st_data_t data = obj;
- rb_check_frozen(obj);
- st_delete(finalizer_table, &data, 0);
- FL_UNSET(obj, FL_FINALIZE);
- return obj;
-}
-
-static void
-should_be_callable(VALUE block)
-{
- if (!rb_obj_respond_to(block, idCall, TRUE)) {
- rb_raise(rb_eArgError, "wrong type argument %"PRIsVALUE" (should be callable)",
- rb_obj_class(block));
- }
-}
-
-static void
-should_be_finalizable(VALUE obj)
-{
- if (!FL_ABLE(obj)) {
- rb_raise(rb_eArgError, "cannot define finalizer for %s",
- rb_obj_classname(obj));
- }
- rb_check_frozen(obj);
-}
-
-static VALUE
-rb_define_finalizer_no_check(VALUE obj, VALUE block)
-{
- rb_objspace_t *objspace = &rb_objspace;
- VALUE table;
- st_data_t data;
-
- RBASIC(obj)->flags |= FL_FINALIZE;
-
- if (st_lookup(finalizer_table, obj, &data)) {
- table = (VALUE)data;
-
- /* avoid duplicate block, table is usually small */
- {
- long len = RARRAY_LEN(table);
- long i;
-
- for (i = 0; i < len; i++) {
- VALUE recv = RARRAY_AREF(table, i);
- if (rb_equal(recv, block)) {
- block = recv;
- goto end;
- }
- }
- }
-
- rb_ary_push(table, block);
- }
- else {
- table = rb_ary_new3(1, block);
- FL_SET_RAW(table, RUBY_FL_SHAREABLE); //TODO: Protect table from data races
- RBASIC_CLEAR_CLASS(table);
- st_add_direct(finalizer_table, obj, table);
- }
- end:
- block = rb_ary_new3(2, INT2FIX(0), block);
- OBJ_FREEZE(block);
- return block;
-}
-
-/*
- * call-seq:
- * ObjectSpace.define_finalizer(obj, aProc=proc())
- *
- * Adds aProc as a finalizer, to be called after obj
- * was destroyed. The object ID of the obj will be passed
- * as an argument to aProc. If aProc is a lambda or
- * method, make sure it can be called with a single argument.
- *
- * The return value is an array [0, aProc]
.
- *
- * The two recommended patterns are to either create the finaliser proc
- * in a non-instance method where it can safely capture the needed state,
- * or to use a custom callable object that stores the needed state
- * explicitly as instance variables.
- *
- * class Foo
- * def initialize(data_needed_for_finalization)
- * ObjectSpace.define_finalizer(self, self.class.create_finalizer(data_needed_for_finalization))
- * end
- *
- * def self.create_finalizer(data_needed_for_finalization)
- * proc {
- * puts "finalizing #{data_needed_for_finalization}"
- * }
- * end
- * end
- *
- * class Bar
- * class Remover
- * def initialize(data_needed_for_finalization)
- * @data_needed_for_finalization = data_needed_for_finalization
- * end
- *
- * def call(id)
- * puts "finalizing #{@data_needed_for_finalization}"
- * end
- * end
- *
- * def initialize(data_needed_for_finalization)
- * ObjectSpace.define_finalizer(self, Remover.new(data_needed_for_finalization))
- * end
- * end
- *
- * Note that if your finalizer references the object to be
- * finalized it will never be run on GC, although it will still be
- * run at exit. You will get a warning if you capture the object
- * to be finalized as the receiver of the finalizer.
- *
- * class CapturesSelf
- * def initialize(name)
- * ObjectSpace.define_finalizer(self, proc {
- * # this finalizer will only be run on exit
- * puts "finalizing #{name}"
- * })
- * end
- * end
- *
- * Also note that finalization can be unpredictable and is never guaranteed
- * to be run except on exit.
- */
-
-static VALUE
-define_final(int argc, VALUE *argv, VALUE os)
-{
- VALUE obj, block;
-
- rb_scan_args(argc, argv, "11", &obj, &block);
- should_be_finalizable(obj);
- should_belong_to_current_objspace(obj);
- if (argc == 1) {
- block = rb_block_proc();
- }
- else {
- should_be_callable(block);
- }
-
- if (rb_callable_receiver(block) == obj) {
- rb_warn("finalizer references object to be finalized");
- }
-
- return rb_define_finalizer_no_check(obj, block);
-}
-
-VALUE
-rb_define_finalizer(VALUE obj, VALUE block)
-{
- should_be_finalizable(obj);
- should_be_callable(block);
- should_belong_to_current_objspace(obj);
- return rb_define_finalizer_no_check(obj, block);
-}
-
-void
-rb_gc_copy_finalizer(VALUE dest, VALUE obj)
-{
- rb_objspace_t *objspace = &rb_objspace;
- VALUE table;
- st_data_t data;
-
- if (!FL_TEST(obj, FL_FINALIZE)) return;
-
- if (RB_LIKELY(st_lookup(finalizer_table, obj, &data))) {
- table = (VALUE)data;
- WITH_OBJSPACE_OF_VALUE_ENTER(dest, objspace);
- {
- st_insert(finalizer_table, dest, table);
- FL_SET(dest, FL_FINALIZE);
- }
- WITH_OBJSPACE_OF_VALUE_LEAVE(objspace);
- }
- else {
- rb_bug("rb_gc_copy_finalizer: FL_FINALIZE set but not found in finalizer_table: %s", obj_info(obj));
- }
-}
-
-static VALUE
-run_single_final(VALUE cmd, VALUE objid)
-{
- return rb_check_funcall(cmd, idCall, 1, &objid);
-}
-
-static void
-warn_exception_in_finalizer(rb_execution_context_t *ec, VALUE final)
-{
- if (!UNDEF_P(final) && !NIL_P(ruby_verbose)) {
- VALUE errinfo = ec->errinfo;
- rb_warn("Exception in finalizer %+"PRIsVALUE, final);
- rb_ec_error_print(ec, errinfo);
- }
-}
-
-static void
-run_finalizer(rb_objspace_t *objspace, VALUE obj, VALUE table)
-{
- long i;
- enum ruby_tag_type state;
- volatile struct {
- VALUE errinfo;
- VALUE objid;
- VALUE final;
- rb_control_frame_t *cfp;
- VALUE *sp;
- long finished;
- } saved;
-
- rb_execution_context_t * volatile ec = GET_EC();
-#define RESTORE_FINALIZER() (\
- ec->cfp = saved.cfp, \
- ec->cfp->sp = saved.sp, \
- ec->errinfo = saved.errinfo)
-
- saved.errinfo = ec->errinfo;
- saved.objid = rb_obj_id(obj);
- saved.cfp = ec->cfp;
- saved.sp = ec->cfp->sp;
- saved.finished = 0;
- saved.final = Qundef;
-
- EC_PUSH_TAG(ec);
- state = EC_EXEC_TAG();
- if (state != TAG_NONE) {
- ++saved.finished; /* skip failed finalizer */
- warn_exception_in_finalizer(ec, ATOMIC_VALUE_EXCHANGE(saved.final, Qundef));
- }
- for (i = saved.finished;
- RESTORE_FINALIZER(), iFixnum
s, Symbol
s
+ * true
, false
, and nil
) are
+ * never returned. In the example below, #each_object returns both
+ * the numbers we defined and several constants defined in the Math
+ * module.
+ *
+ * If no block is given, an enumerator is returned instead.
+ *
+ * a = 102.7
+ * b = 95 # Won't be returned
+ * c = 12345678987654321
+ * count = ObjectSpace.each_object(Numeric) {|x| p x }
+ * puts "Total count: #{count}"
+ *
+ * produces:
+ *
+ * 12345678987654321
+ * 102.7
+ * 2.71828182845905
+ * 3.14159265358979
+ * 2.22044604925031e-16
+ * 1.7976931348623157e+308
+ * 2.2250738585072e-308
+ * Total count: 7
+ *
+ */
- if (gc_object_moved_p(objspace, (VALUE)key)) {
- return ST_REPLACE;
- }
+static VALUE
+os_each_obj(int argc, VALUE *argv, VALUE os)
+{
+ VALUE of;
- if (gc_object_moved_p(objspace, (VALUE)value)) {
- return ST_REPLACE;
- }
- return ST_CONTINUE;
+ of = (!rb_check_arity(argc, 0, 1) ? 0 : argv[0]);
+ RETURN_ENUMERATOR(os, 1, &of);
+ return os_obj_of(of);
}
-static int
-hash_replace_ref_value(st_data_t *key, st_data_t *value, st_data_t argp, int existing)
+static void
+should_belong_to_current_objspace(VALUE obj)
{
- rb_objspace_t *objspace = (rb_objspace_t *)argp;
-
- if (gc_object_moved_p(objspace, (VALUE)*value)) {
- *value = rb_gc_location((VALUE)*value);
+ if (GET_OBJSPACE_OF_VALUE(obj) != &rb_objspace) {
+ rb_raise(rb_eRactorIsolationError, "cannot define or undefine finalizers for objects belonging to another Ractor");
}
-
- return ST_CONTINUE;
}
-static int
-hash_foreach_replace_value(st_data_t key, st_data_t value, st_data_t argp, int error)
-{
- rb_objspace_t *objspace;
+/*
+ * call-seq:
+ * ObjectSpace.undefine_finalizer(obj)
+ *
+ * Removes all finalizers for obj.
+ *
+ */
- objspace = (rb_objspace_t *)argp;
+static VALUE
+undefine_final(VALUE os, VALUE obj)
+{
+ should_belong_to_current_objspace(obj);
+ return rb_gc_impl_undefine_finalizer(rb_gc_get_objspace(), obj);
+}
- if (gc_object_moved_p(objspace, (VALUE)value)) {
- return ST_REPLACE;
+static void
+should_be_callable(VALUE block)
+{
+ if (!rb_obj_respond_to(block, idCall, TRUE)) {
+ rb_raise(rb_eArgError, "wrong type argument %"PRIsVALUE" (should be callable)",
+ rb_obj_class(block));
}
- return ST_CONTINUE;
}
static void
-gc_ref_update_table_values_only(rb_objspace_t *objspace, st_table *tbl)
+should_be_finalizable(VALUE obj)
{
- if (!tbl || tbl->num_entries == 0) return;
-
- if (st_foreach_with_replace(tbl, hash_foreach_replace_value, hash_replace_ref_value, (st_data_t)objspace)) {
- rb_raise(rb_eRuntimeError, "hash modified during iteration");
+ if (!FL_ABLE(obj)) {
+ rb_raise(rb_eArgError, "cannot define finalizer for %s",
+ rb_obj_classname(obj));
}
+ rb_check_frozen(obj);
}
void
-rb_gc_ref_update_table_values_only(st_table *tbl)
+rb_gc_copy_finalizer(VALUE dest, VALUE obj)
{
- gc_ref_update_table_values_only(&rb_objspace, tbl);
+ rb_gc_impl_copy_finalizer(rb_gc_get_objspace(), dest, obj);
}
-static void
-gc_update_table_refs(rb_objspace_t * objspace, st_table *tbl)
+/*
+ * call-seq:
+ * ObjectSpace.define_finalizer(obj, aProc=proc())
+ *
+ * Adds aProc as a finalizer, to be called after obj
+ * was destroyed. The object ID of the obj will be passed
+ * as an argument to aProc. If aProc is a lambda or
+ * method, make sure it can be called with a single argument.
+ *
+ * The return value is an array [0, aProc]
.
+ *
+ * The two recommended patterns are to either create the finaliser proc
+ * in a non-instance method where it can safely capture the needed state,
+ * or to use a custom callable object that stores the needed state
+ * explicitly as instance variables.
+ *
+ * class Foo
+ * def initialize(data_needed_for_finalization)
+ * ObjectSpace.define_finalizer(self, self.class.create_finalizer(data_needed_for_finalization))
+ * end
+ *
+ * def self.create_finalizer(data_needed_for_finalization)
+ * proc {
+ * puts "finalizing #{data_needed_for_finalization}"
+ * }
+ * end
+ * end
+ *
+ * class Bar
+ * class Remover
+ * def initialize(data_needed_for_finalization)
+ * @data_needed_for_finalization = data_needed_for_finalization
+ * end
+ *
+ * def call(id)
+ * puts "finalizing #{@data_needed_for_finalization}"
+ * end
+ * end
+ *
+ * def initialize(data_needed_for_finalization)
+ * ObjectSpace.define_finalizer(self, Remover.new(data_needed_for_finalization))
+ * end
+ * end
+ *
+ * Note that if your finalizer references the object to be
+ * finalized it will never be run on GC, although it will still be
+ * run at exit. You will get a warning if you capture the object
+ * to be finalized as the receiver of the finalizer.
+ *
+ * class CapturesSelf
+ * def initialize(name)
+ * ObjectSpace.define_finalizer(self, proc {
+ * # this finalizer will only be run on exit
+ * puts "finalizing #{name}"
+ * })
+ * end
+ * end
+ *
+ * Also note that finalization can be unpredictable and is never guaranteed
+ * to be run except on exit.
+ */
+
+static VALUE
+define_final(int argc, VALUE *argv, VALUE os)
{
- if (!tbl || tbl->num_entries == 0) return;
+ VALUE obj, block;
- if (st_foreach_with_replace(tbl, hash_foreach_replace, hash_replace_ref, (st_data_t)objspace)) {
- rb_raise(rb_eRuntimeError, "hash modified during iteration");
+ rb_scan_args(argc, argv, "11", &obj, &block);
+ should_be_finalizable(obj);
+ should_belong_to_current_objspace(obj);
+ if (argc == 1) {
+ block = rb_block_proc();
+ }
+ else {
+ should_be_callable(block);
}
-}
-/* Update MOVED references in a VALUE=>VALUE st_table */
-void
-rb_gc_update_tbl_refs(st_table *ptr)
-{
- rb_objspace_t *objspace = &rb_objspace;
- gc_update_table_refs(objspace, ptr);
+ if (rb_callable_receiver(block) == obj) {
+ rb_warn("finalizer references object to be finalized");
+ }
+
+ return rb_gc_impl_define_finalizer(rb_gc_get_objspace(), obj, block);
}
-static void
-gc_ref_update_hash(rb_objspace_t * objspace, VALUE v)
+VALUE
+rb_define_finalizer(VALUE obj, VALUE block)
{
- rb_hash_stlike_foreach_with_replace(v, hash_foreach_replace, hash_replace_ref, (st_data_t)objspace);
+ should_be_finalizable(obj);
+ should_belong_to_current_objspace(obj);
+ should_be_callable(block);
+ return rb_gc_impl_define_finalizer(rb_gc_get_objspace(), obj, block);
}
-static void
-gc_update_values(rb_objspace_t *objspace, long n, VALUE *values)
+void
+rb_objspace_call_finalizer(rb_objspace_t *objspace)
{
- long i;
-
- for (i=0; iGC.config
will be a +Hash+
+ # containing the most recent full configuration. ie. All keys and values
+ # defined by the specific GC implementation being used. In the case of a
+ # config update, the return value will include the new values being updated.
+ #
+ # This method is only expected to work on CRuby.
+ #
+ # Valid config keys for Ruby's default GC implementation are:
+ #
+ # [rgengc_allow_full_mark]
+ # Control whether the GC is allowed to run a full mark (young & old objects).
+ #
+ # When +true+ GC interleaves major and minor collections. This is default. GC
+ # will function as intended.
+ #
+ # When +false+, the GC will never trigger a full marking cycle unless
+ # explicitly requested by user code. Instead only a minor mark will run -
+ # only young objects will be marked. When the heap space is exhausted, new
+ # pages will be allocated immediately instead of running a full mark.
+ #
+ # A flag will be set to notify that a full mark has been
+ # requested. This flag is accessible using
+ # GC.latest_gc_info(:needs_major_by)
+ #
+ # The user can trigger a major collection at any time using
+ # GC.start(full_mark: true)
+ #
+ # When +false+. Young to Old object promotion is disabled. For performance
+ # reasons it is recommended to warmup an application using +Process.warmup+
+ # before setting this parameter to +false+.
+ def self.config hash = nil
+ return Primitive.gc_config_get unless hash
+
+ Primitive.gc_config_set hash
+ end
+
# call-seq:
# GC.latest_gc_info -> hash
# GC.latest_gc_info(hash) -> hash
@@ -266,26 +322,6 @@ def self.latest_gc_info hash_or_key = nil
Primitive.gc_latest_gc_info hash_or_key
end
- if respond_to?(:compact)
- # call-seq:
- # GC.verify_compaction_references(toward: nil, double_heap: false) -> hash
- #
- # Verify compaction reference consistency.
- #
- # This method is implementation specific. During compaction, objects that
- # were moved are replaced with T_MOVED objects. No object should have a
- # reference to a T_MOVED object after compaction.
- #
- # This function expands the heap to ensure room to move all objects,
- # compacts the heap to make sure everything moves, updates all references,
- # then performs a full \GC. If any object contains a reference to a T_MOVED
- # object, that object should be pushed on the mark stack, and will
- # make a SEGV.
- def self.verify_compaction_references(toward: nil, double_heap: false, expand_heap: false)
- Primitive.gc_verify_compaction_references(double_heap, expand_heap, toward == :empty)
- end
- end
-
# call-seq:
# GC.measure_total_time = true/false
#
@@ -294,8 +330,7 @@ def self.verify_compaction_references(toward: nil, double_heap: false, expand_he
# Note that \GC time measurement can cause some performance overhead.
def self.measure_total_time=(flag)
Primitive.cstmt! %{
- rb_objspace.flags.measure_gc = RTEST(flag) ? TRUE : FALSE;
- return flag;
+ return rb_gc_impl_set_measure_total_time(rb_gc_get_objspace(), flag);
}
end
@@ -306,7 +341,7 @@ def self.measure_total_time=(flag)
# Note that measurement can affect the application performance.
def self.measure_total_time
Primitive.cexpr! %{
- RBOOL(rb_objspace.flags.measure_gc)
+ rb_gc_impl_get_measure_total_time(rb_gc_get_objspace())
}
end
@@ -316,7 +351,7 @@ def self.measure_total_time
# Return measured \GC total time in nano seconds.
def self.total_time
Primitive.cexpr! %{
- ULL2NUM(rb_objspace.profile.marking_time_ns + rb_objspace.profile.sweeping_time_ns)
+ rb_gc_impl_get_profile_total_time(rb_gc_get_objspace())
}
end
end
diff --git a/gc/default.c b/gc/default.c
new file mode 100644
index 00000000000000..9492985d55997c
--- /dev/null
+++ b/gc/default.c
@@ -0,0 +1,10943 @@
+#include "ruby/internal/config.h"
+
+#include ndigits.abs
trailing zeros:
+ * Returns a numeric that is a "ceiling" value for `self`,
+ * as specified by the given `ndigits`,
+ * which must be an
+ * [integer-convertible object](rdoc-ref:implicit_conversion.rdoc@Integer-Convertible+Objects).
*
- * f = 12345.6789
- * f.ceil(0) # => 12346
- * f.ceil(-3) # => 13000
- * f = -12345.6789
- * f.ceil(0) # => -12345
- * f.ceil(-3) # => -12000
+ * When `ndigits` is positive, returns a Float with `ndigits`
+ * decimal digits after the decimal point
+ * (as available, but no fewer than 1):
+ *
+ * ```
+ * f = 12345.6789
+ * f.ceil(1) # => 12345.7
+ * f.ceil(3) # => 12345.679
+ * f.ceil(30) # => 12345.6789
+ * f = -12345.6789
+ * f.ceil(1) # => -12345.6
+ * f.ceil(3) # => -12345.678
+ * f.ceil(30) # => -12345.6789
+ * f = 0.0
+ * f.ceil(1) # => 0.0
+ * f.ceil(100) # => 0.0
+ * ```
+ *
+ * When `ndigits` is non-positive,
+ * returns an Integer based on a computed granularity:
+ *
+ * - The granularity is `10 ** ndigits.abs`.
+ * - The returned value is the largest multiple of the granularity
+ * that is less than or equal to `self`.
+ *
+ * Examples with positive `self`:
+ *
+ * | ndigits | Granularity | 12345.6789.ceil(ndigits) |
+ * |--------:|------------:|-------------------------:|
+ * | 0 | 1 | 12346 |
+ * | -1 | 10 | 12350 |
+ * | -2 | 100 | 12400 |
+ * | -3 | 1000 | 13000 |
+ * | -4 | 10000 | 20000 |
+ * | -5 | 100000 | 100000 |
+ *
+ * Examples with negative `self`:
+ *
+ * | ndigits | Granularity | -12345.6789.ceil(ndigits) |
+ * |--------:|------------:|--------------------------:|
+ * | 0 | 1 | -12345 |
+ * | -1 | 10 | -12340 |
+ * | -2 | 100 | -12300 |
+ * | -3 | 1000 | -12000 |
+ * | -4 | 10000 | -10000 |
+ * | -5 | 100000 | 0 |
+ *
+ * When `self` is zero and `ndigits` is non-positive,
+ * returns Integer zero:
+ *
+ * ```
+ * 0.0.ceil(0) # => 0
+ * 0.0.ceil(-1) # => 0
+ * 0.0.ceil(-2) # => 0
+ * ```
*
* Note that the limited precision of floating-point arithmetic
* may lead to surprising results:
*
- * (2.1 / 0.7).ceil #=> 4 (!)
+ * ```
+ * (2.1 / 0.7).ceil #=> 4 # Not 3 (because 2.1 / 0.7 # => 3.0000000000000004, not 3.0)
+ * ```
*
* Related: Float#floor.
*
@@ -5623,50 +5665,6 @@ int_downto_size(VALUE from, VALUE args, VALUE eobj)
return ruby_num_interval_step_size(from, RARRAY_AREF(args, 0), INT2FIX(-1), FALSE);
}
-/*
- * call-seq:
- * downto(limit) {|i| ... } -> self
- * downto(limit) -> enumerator
- *
- * Calls the given block with each integer value from +self+ down to +limit+;
- * returns +self+:
- *
- * a = []
- * 10.downto(5) {|i| a << i } # => 10
- * a # => [10, 9, 8, 7, 6, 5]
- * a = []
- * 0.downto(-5) {|i| a << i } # => 0
- * a # => [0, -1, -2, -3, -4, -5]
- * 4.downto(5) {|i| fail 'Cannot happen' } # => 4
- *
- * With no block given, returns an Enumerator.
- *
- */
-
-static VALUE
-int_downto(VALUE from, VALUE to)
-{
- RETURN_SIZED_ENUMERATOR(from, 1, &to, int_downto_size);
- if (FIXNUM_P(from) && FIXNUM_P(to)) {
- long i, end;
-
- end = FIX2LONG(to);
- for (i=FIX2LONG(from); i >= end; i--) {
- rb_yield(LONG2FIX(i));
- }
- }
- else {
- VALUE i = from, c;
-
- while (!(c = rb_funcall(i, '<', 1, to))) {
- rb_yield(i);
- i = rb_funcall(i, '-', 1, INT2FIX(1));
- }
- if (NIL_P(c)) rb_cmperr(i, to);
- }
- return from;
-}
-
static VALUE
int_dotimes_size(VALUE num, VALUE args, VALUE eobj)
{
@@ -5739,24 +5737,56 @@ int_round(int argc, VALUE* argv, VALUE num)
}
/*
+ * :markup: markdown
+ *
* call-seq:
* floor(ndigits = 0) -> integer
*
- * Returns the largest number less than or equal to +self+ with
- * a precision of +ndigits+ decimal digits.
+ * Returns an integer that is a "floor" value for `self`,
+ * as specified by the given `ndigits`,
+ * which must be an
+ * [integer-convertible object](rdoc-ref:implicit_conversion.rdoc@Integer-Convertible+Objects).
*
- * When +ndigits+ is negative, the returned value
- * has at least ndigits.abs trailing zeros:
+ * - When `self` is zero, returns zero (regardless of the value of `ndigits`):
*
- * 555.floor(-1) # => 550
- * 555.floor(-2) # => 500
- * -555.floor(-2) # => -600
- * 555.floor(-3) # => 0
+ * ```
+ * 0.floor(2) # => 0
+ * 0.floor(-2) # => 0
+ * ```
*
- * Returns +self+ when +ndigits+ is zero or positive.
+ * - When `self` is non-zero and `ndigits` is non-negative, returns `self`:
+ *
+ * ```
+ * 555.floor # => 555
+ * 555.floor(50) # => 555
+ * ```
*
- * 555.floor # => 555
- * 555.floor(50) # => 555
+ * - When `self` is non-zero and `ndigits` is negative,
+ * returns a value based on a computed granularity:
+ *
+ * - The granularity is `10 ** ndigits.abs`.
+ * - The returned value is the largest multiple of the granularity
+ * that is less than or equal to `self`.
+ *
+ * Examples with positive `self`:
+ *
+ * | ndigits | Granularity | 1234.floor(ndigits) |
+ * |--------:|------------:|--------------------:|
+ * | -1 | 10 | 1230 |
+ * | -2 | 100 | 1200 |
+ * | -3 | 1000 | 1000 |
+ * | -4 | 10000 | 0 |
+ * | -5 | 100000 | 0 |
+ *
+ * Examples with negative `self`:
+ *
+ * | ndigits | Granularity | -1234.floor(ndigits) |
+ * |--------:|------------:|---------------------:|
+ * | -1 | 10 | -1240 |
+ * | -2 | 100 | -1300 |
+ * | -3 | 1000 | -2000 |
+ * | -4 | 10000 | -10000 |
+ * | -5 | 100000 | -100000 |
*
* Related: Integer#ceil.
*
@@ -5803,7 +5833,7 @@ int_floor(int argc, VALUE* argv, VALUE num)
* - When `self` is non-zero and `ndigits` is negative,
* returns a value based on a computed granularity:
*
- * - The granularity is ndigits.abs * 10.
+ * - The granularity is `10 ** ndigits.abs`.
* - The returned value is the smallest multiple of the granularity
* that is greater than or equal to `self`.
*
@@ -5817,8 +5847,6 @@ int_floor(int argc, VALUE* argv, VALUE num)
* | -4 | 10000 | 10000 |
* | -5 | 100000 | 100000 |
*
- * require 'rbconfig/sizeof'
.
+ *
+ * require 'rbconfig/sizeof'
+ * RUBY_PLATFORM # => "x64-mingw-ucrt"
+ * RbConfig::LIMITS.fetch_values('FIXNUM_MAX', 'LONG_MAX')
+ * # => [1073741823, 2147483647]
+ *
+ */
+
void
Init_limits(void)
{
VALUE h = rb_hash_new();
- rb_define_const(rb_define_module("RbConfig"), "LIMITS", h);
+ VALUE mRbConfig = rb_define_module("RbConfig");
+ rb_define_const(mRbConfig, "LIMITS", h);
#ifdef HAVE_LONG_LONG
#ifndef ULLONG_MAX
diff --git a/template/sizes.c.tmpl b/template/sizes.c.tmpl
index 31060f5fea676d..909902fd172ad7 100644
--- a/template/sizes.c.tmpl
+++ b/template/sizes.c.tmpl
@@ -25,12 +25,27 @@ conditions = {
#endif
% end
+
+/*
+ * Document-const: SIZEOF
+ *
+ * A Hash with the byte size of \C types available to the compiler
+ * used to build Ruby. To access this constant, first run
+ * require 'rbconfig/sizeof'
.
+ *
+ * require 'rbconfig/sizeof'
+ * RUBY_PLATFORM # => "x64-mingw-ucrt"
+ * RbConfig::SIZEOF.fetch_values('long', 'void*') # => [4, 8]
+ *
+ */
+
extern void Init_limits(void);
void
Init_sizeof(void)
{
VALUE s = rb_hash_new();
- rb_define_const(rb_define_module("RbConfig"), "SIZEOF", s);
+ VALUE mRbConfig = rb_define_module("RbConfig");
+ rb_define_const(mRbConfig, "SIZEOF", s);
#define DEFINE(type, size) rb_hash_aset(s, rb_str_new_cstr(#type), INT2FIX(SIZEOF_##size))
#define DEFINE_SIZE(type) rb_hash_aset(s, rb_str_new_cstr(#type), INT2FIX(sizeof(type)))
diff --git a/test/-ext-/bug_reporter/test_bug_reporter.rb b/test/-ext-/bug_reporter/test_bug_reporter.rb
index 76f913c27522f7..00743d0e28a30c 100644
--- a/test/-ext-/bug_reporter/test_bug_reporter.rb
+++ b/test/-ext-/bug_reporter/test_bug_reporter.rb
@@ -5,6 +5,8 @@
class TestBugReporter < Test::Unit::TestCase
def test_bug_reporter_add
+ pend "macOS 15 beta is not working with this test" if /darwin/ =~ RUBY_PLATFORM && /15/ =~ `sw_vers -productVersion`
+
omit "flaky with RJIT" if JITSupport.rjit_enabled?
description = RUBY_DESCRIPTION.sub(/\+PRISM /, '')
description = description.sub(/\+RJIT /, '') unless JITSupport.rjit_force_enabled?
diff --git a/test/.excludes-prism/TestSetTraceFunc.rb b/test/.excludes-prism/TestSetTraceFunc.rb
deleted file mode 100644
index 9c79044c775fcc..00000000000000
--- a/test/.excludes-prism/TestSetTraceFunc.rb
+++ /dev/null
@@ -1,2 +0,0 @@
-exclude(:test_return, "[Bug #20457]")
-exclude(:test_return2, "[Bug #20457]")
diff --git a/test/error_highlight/test_error_highlight.rb b/test/error_highlight/test_error_highlight.rb
index 9af530b69f6069..5f4c386990c76d 100644
--- a/test/error_highlight/test_error_highlight.rb
+++ b/test/error_highlight/test_error_highlight.rb
@@ -5,13 +5,6 @@
require "tempfile"
class ErrorHighlightTest < Test::Unit::TestCase
- # We can't revisit instruction sequences to find node ids if the prism
- # compiler was used instead of the parse.y compiler. In that case, we'll omit
- # some tests.
- def self.compiling_with_prism?
- RubyVM::InstructionSequence.compile("").to_a[4][:parser] == :prism
- end
-
class DummyFormatter
def self.message_for(corrections)
""
@@ -876,11 +869,11 @@ def test_COLON2_4
end
end
- if ErrorHighlight.const_get(:Spotter).const_get(:OPT_GETCONSTANT_PATH) && !compiling_with_prism?
+ if ErrorHighlight.const_get(:Spotter).const_get(:OPT_GETCONSTANT_PATH)
def test_COLON2_5
# Unfortunately, we cannot identify which `NotDefined` caused the NameError
assert_error_message(NameError, <<~END) do
-uninitialized constant ErrorHighlightTest::NotDefined
+ uninitialized constant ErrorHighlightTest::NotDefined
END
ErrorHighlightTest::NotDefined::NotDefined
@@ -1342,7 +1335,11 @@ def test_spot_with_backtrace_location
def test_spot_with_node
omit unless RubyVM::AbstractSyntaxTree.respond_to?(:node_id_for_backtrace_location)
- omit if ErrorHighlightTest.compiling_with_prism?
+
+ # We can't revisit instruction sequences to find node ids if the prism
+ # compiler was used instead of the parse.y compiler. In that case, we'll
+ # omit some tests.
+ omit if RubyVM::InstructionSequence.compile("").to_a[4][:parser] == :prism
begin
raise_name_error
diff --git a/test/irb/command/test_cd.rb b/test/irb/command/test_cd.rb
new file mode 100644
index 00000000000000..4537286f73c46c
--- /dev/null
+++ b/test/irb/command/test_cd.rb
@@ -0,0 +1,65 @@
+require "tempfile"
+require_relative "../helper"
+
+module TestIRB
+ class CDTest < IntegrationTestCase
+ def setup
+ super
+
+ write_ruby <<~'RUBY'
+ class Foo
+ class Bar
+ def bar
+ "this is bar"
+ end
+ end
+
+ def foo
+ "this is foo"
+ end
+ end
+
+ binding.irb
+ RUBY
+ end
+
+ def test_cd
+ out = run_ruby_file do
+ type "cd Foo"
+ type "ls"
+ type "cd Bar"
+ type "ls"
+ type "cd .."
+ type "exit"
+ end
+
+ assert_match(/irb\(Foo\):002>/, out)
+ assert_match(/Foo#methods: foo/, out)
+ assert_match(/irb\(Foo::Bar\):004>/, out)
+ assert_match(/Bar#methods: bar/, out)
+ assert_match(/irb\(Foo\):006>/, out)
+ end
+
+ def test_cd_moves_top_level_with_no_args
+ out = run_ruby_file do
+ type "cd Foo"
+ type "cd Bar"
+ type "cd"
+ type "exit"
+ end
+
+ assert_match(/irb\(Foo::Bar\):003>/, out)
+ assert_match(/irb\(main\):004>/, out)
+ end
+
+ def test_cd_with_error
+ out = run_ruby_file do
+ type "cd Baz"
+ type "exit"
+ end
+
+ assert_match(/Error: uninitialized constant Baz/, out)
+ assert_match(/irb\(main\):002>/, out) # the context should not change
+ end
+ end
+end
diff --git a/test/irb/helper.rb b/test/irb/helper.rb
index acaf6277f3879d..ea2c6ef16a417e 100644
--- a/test/irb/helper.rb
+++ b/test/irb/helper.rb
@@ -49,6 +49,19 @@ def ruby_core?
!Pathname(__dir__).join("../../", "irb.gemspec").exist?
end
+ def setup_envs(home:)
+ @backup_home = ENV["HOME"]
+ ENV["HOME"] = home
+ @backup_xdg_config_home = ENV.delete("XDG_CONFIG_HOME")
+ @backup_irbrc = ENV.delete("IRBRC")
+ end
+
+ def teardown_envs
+ ENV["HOME"] = @backup_home
+ ENV["XDG_CONFIG_HOME"] = @backup_xdg_config_home
+ ENV["IRBRC"] = @backup_irbrc
+ end
+
def save_encodings
@default_encoding = [Encoding.default_external, Encoding.default_internal]
@stdio_encodings = [STDIN, STDOUT, STDERR].map {|io| [io.external_encoding, io.internal_encoding] }
diff --git a/test/irb/test_command.rb b/test/irb/test_command.rb
index 30c3f5ca2c452f..567c3216ccec2f 100644
--- a/test/irb/test_command.rb
+++ b/test/irb/test_command.rb
@@ -15,9 +15,7 @@ def setup
Dir.mkdir(@tmpdir)
end
Dir.chdir(@tmpdir)
- @home_backup = ENV["HOME"]
- ENV["HOME"] = @tmpdir
- @xdg_config_home_backup = ENV.delete("XDG_CONFIG_HOME")
+ setup_envs(home: @tmpdir)
save_encodings
IRB.instance_variable_get(:@CONF).clear
IRB.instance_variable_set(:@existing_rc_name_generators, nil)
@@ -25,8 +23,7 @@ def setup
end
def teardown
- ENV["XDG_CONFIG_HOME"] = @xdg_config_home_backup
- ENV["HOME"] = @home_backup
+ teardown_envs
Dir.chdir(@pwd)
FileUtils.rm_rf(@tmpdir)
restore_encodings
diff --git a/test/irb/test_completion.rb b/test/irb/test_completion.rb
index 5fe7952b3d70e6..c9a0eafa3d45bb 100644
--- a/test/irb/test_completion.rb
+++ b/test/irb/test_completion.rb
@@ -16,8 +16,14 @@ def doc_namespace(target, bind)
class CommandCompletionTest < CompletionTest
def test_command_completion
- assert_include(IRB::RegexpCompletor.new.completion_candidates('', 'show_s', '', bind: binding), 'show_source')
- assert_not_include(IRB::RegexpCompletor.new.completion_candidates(';', 'show_s', '', bind: binding), 'show_source')
+ completor = IRB::RegexpCompletor.new
+ binding.eval("some_var = 1")
+ # completion for help command's argument should only include command names
+ assert_include(completor.completion_candidates('help ', 's', '', bind: binding), 'show_source')
+ assert_not_include(completor.completion_candidates('help ', 's', '', bind: binding), 'some_var')
+
+ assert_include(completor.completion_candidates('', 'show_s', '', bind: binding), 'show_source')
+ assert_not_include(completor.completion_candidates(';', 'show_s', '', bind: binding), 'show_source')
end
end
diff --git a/test/irb/test_context.rb b/test/irb/test_context.rb
index cd3f2c8f620a72..7ad8fd2fc4a84a 100644
--- a/test/irb/test_context.rb
+++ b/test/irb/test_context.rb
@@ -28,7 +28,6 @@ def teardown
restore_encodings
end
-
def test_eval_input
verbose, $VERBOSE = $VERBOSE, nil
input = TestInputMethod.new([
diff --git a/test/irb/test_history.rb b/test/irb/test_history.rb
index 63be35fdaa2933..84f043892969d0 100644
--- a/test/irb/test_history.rb
+++ b/test/irb/test_history.rb
@@ -12,19 +12,14 @@ class HistoryTest < TestCase
def setup
@original_verbose, $VERBOSE = $VERBOSE, nil
@tmpdir = Dir.mktmpdir("test_irb_history_")
- @backup_home = ENV["HOME"]
- @backup_xdg_config_home = ENV.delete("XDG_CONFIG_HOME")
- @backup_irbrc = ENV.delete("IRBRC")
+ setup_envs(home: @tmpdir)
@backup_default_external = Encoding.default_external
- ENV["HOME"] = @tmpdir
IRB.instance_variable_set(:@existing_rc_name_generators, nil)
end
def teardown
IRB.instance_variable_set(:@existing_rc_name_generators, nil)
- ENV["HOME"] = @backup_home
- ENV["XDG_CONFIG_HOME"] = @backup_xdg_config_home
- ENV["IRBRC"] = @backup_irbrc
+ teardown_envs
Encoding.default_external = @backup_default_external
$VERBOSE = @original_verbose
FileUtils.rm_rf(@tmpdir)
diff --git a/test/irb/test_init.rb b/test/irb/test_init.rb
index c423fa112e5786..3e8d01c5ce03eb 100644
--- a/test/irb/test_init.rb
+++ b/test/irb/test_init.rb
@@ -270,17 +270,15 @@ def with_argv(argv)
class ConfigValidationTest < TestCase
def setup
- @original_home = ENV["HOME"]
- @original_irbrc = ENV["IRBRC"]
# To prevent the test from using the user's .irbrc file
- ENV["HOME"] = @home = Dir.mktmpdir
+ @home = Dir.mktmpdir
+ setup_envs(home: @home)
super
end
def teardown
super
- ENV["IRBRC"] = @original_irbrc
- ENV["HOME"] = @original_home
+ teardown_envs
File.unlink(@irbrc)
Dir.rmdir(@home)
IRB.instance_variable_set(:@existing_rc_name_generators, nil)
diff --git a/test/irb/test_type_completor.rb b/test/irb/test_type_completor.rb
index 5ed8988b34928e..412d7c696d62d5 100644
--- a/test/irb/test_type_completor.rb
+++ b/test/irb/test_type_completor.rb
@@ -56,6 +56,11 @@ def test_empty_completion
end
def test_command_completion
+ binding.eval("some_var = 1")
+ # completion for help command's argument should only include command names
+ assert_include(@completor.completion_candidates('help ', 's', '', bind: binding), 'show_source')
+ assert_not_include(@completor.completion_candidates('help ', 's', '', bind: binding), 'some_var')
+
assert_include(@completor.completion_candidates('', 'show_s', '', bind: binding), 'show_source')
assert_not_include(@completor.completion_candidates(';', 'show_s', '', bind: binding), 'show_source')
end
diff --git a/test/irb/test_workspace.rb b/test/irb/test_workspace.rb
index 199ce95a37b0fa..ad515f91df3990 100644
--- a/test/irb/test_workspace.rb
+++ b/test/irb/test_workspace.rb
@@ -80,7 +80,6 @@ def test_code_around_binding_on_irb
assert_equal(nil, workspace.code_around_binding)
end
-
def test_toplevel_binding_local_variables
bug17623 = '[ruby-core:102468]'
bundle_exec = ENV.key?('BUNDLE_GEMFILE') ? ['-rbundler/setup'] : []
diff --git a/test/logger/test_logger.rb b/test/logger/test_logger.rb
index 37d0f5862ab1b0..2023553a6c1c3b 100644
--- a/test/logger/test_logger.rb
+++ b/test/logger/test_logger.rb
@@ -113,6 +113,15 @@ def test_string_level
assert_raise(ArgumentError) { @logger.level = 'something_wrong' }
end
+ def test_reraise_write_errors
+ c = Object.new
+ e = Class.new(StandardError)
+ c.define_singleton_method(:write){|*| raise e}
+ c.define_singleton_method(:close){}
+ logger = Logger.new(c, :reraise_write_errors=>[e])
+ assert_raise(e) { logger.warn('foo') }
+ end
+
def test_progname
assert_nil(@logger.progname)
@logger.progname = "name"
diff --git a/test/net/http/test_http.rb b/test/net/http/test_http.rb
index f0f1bc2d8f5a29..a49cc87e8d2454 100644
--- a/test/net/http/test_http.rb
+++ b/test/net/http/test_http.rb
@@ -254,6 +254,18 @@ def host.to_str; raise SocketError, "open failure"; end
end
end
+ def test_default_configuration
+ Net::HTTP.default_configuration = { open_timeout: 5 }
+ http = Net::HTTP.new 'hostname.example'
+ assert_equal 5, http.open_timeout
+ assert_equal 60, http.read_timeout
+
+ http.open_timeout = 10
+ assert_equal 10, http.open_timeout
+ ensure
+ Net::HTTP.default_configuration = nil
+ end
+
end
module TestNetHTTP_version_1_1_methods
@@ -442,7 +454,11 @@ def test_get2
def test_post
start {|http|
_test_post__base http
+ }
+ start {|http|
_test_post__file http
+ }
+ start {|http|
_test_post__no_data http
}
end
@@ -629,10 +645,12 @@ def test_request
# _test_request__range http # WEBrick does not support Range: header.
_test_request__HEAD http
_test_request__POST http
- _test_request__stream_body http
_test_request__uri http
_test_request__uri_host http
}
+ start {|http|
+ _test_request__stream_body http
+ }
end
def _test_request__GET(http)
@@ -843,7 +861,13 @@ def test_set_form
__EOM__
start {|http|
_test_set_form_urlencoded(http, data.reject{|k,v|!v.is_a?(String)})
+ }
+ start {|http|
+ @server.mount('/', lambda {|req, res| res.body = req.body })
_test_set_form_multipart(http, false, data, expected)
+ }
+ start {|http|
+ @server.mount('/', lambda {|req, res| res.body = req.body })
_test_set_form_multipart(http, true, data, expected)
}
}
@@ -887,6 +911,7 @@ def test_set_form_with_file
expected.sub!(/@options
instance
- # variable for use by a subclass.
-
- def initialize(*args)
- if defined?(MOD_RUBY)
- unless ENV.has_key?("GATEWAY_INTERFACE")
- Apache.request.setup_cgi_env
- end
- end
- if %r{HTTP/(\d+\.\d+)} =~ ENV["SERVER_PROTOCOL"]
- httpv = $1
- end
- @config = WEBrick::Config::HTTP.dup.update(
- :ServerSoftware => ENV["SERVER_SOFTWARE"] || "null",
- :HTTPVersion => HTTPVersion.new(httpv || "1.0"),
- :RunOnCGI => true, # to detect if it runs on CGI.
- :NPH => false # set true to run as NPH script.
- )
- if config = args.shift
- @config.update(config)
- end
- @config[:Logger] ||= WEBrick::BasicLog.new($stderr)
- @logger = @config[:Logger]
- @options = args
- end
-
- ##
- # Reads +key+ from the configuration
-
- def [](key)
- @config[key]
- end
-
- ##
- # Starts the CGI process with the given environment +env+ and standard
- # input and output +stdin+ and +stdout+.
-
- def start(env=ENV, stdin=$stdin, stdout=$stdout)
- sock = WEBrick::CGI::Socket.new(@config, env, stdin, stdout)
- req = HTTPRequest.new(@config)
- res = HTTPResponse.new(@config)
- unless @config[:NPH] or defined?(MOD_RUBY)
- def res.setup_header
- unless @header["status"]
- phrase = HTTPStatus::reason_phrase(@status)
- @header["status"] = "#{@status} #{phrase}"
- end
- super
- end
- def res.status_line
- ""
- end
- end
-
- begin
- req.parse(sock)
- req.script_name = (env["SCRIPT_NAME"] || File.expand_path($0)).dup
- req.path_info = (env["PATH_INFO"] || "").dup
- req.query_string = env["QUERY_STRING"]
- req.user = env["REMOTE_USER"]
- res.request_method = req.request_method
- res.request_uri = req.request_uri
- res.request_http_version = req.http_version
- res.keep_alive = req.keep_alive?
- self.service(req, res)
- rescue HTTPStatus::Error => ex
- res.set_error(ex)
- rescue HTTPStatus::Status => ex
- res.status = ex.code
- rescue Exception => ex
- @logger.error(ex)
- res.set_error(ex, true)
- ensure
- req.fixup
- if defined?(MOD_RUBY)
- res.setup_header
- Apache.request.status_line = "#{res.status} #{res.reason_phrase}"
- Apache.request.status = res.status
- table = Apache.request.headers_out
- res.header.each{|key, val|
- case key
- when /^content-encoding$/i
- Apache::request.content_encoding = val
- when /^content-type$/i
- Apache::request.content_type = val
- else
- table[key] = val.to_s
- end
- }
- res.cookies.each{|cookie|
- table.add("Set-Cookie", cookie.to_s)
- }
- Apache.request.send_http_header
- res.send_body(sock)
- else
- res.send_response(sock)
- end
- end
- end
-
- ##
- # Services the request +req+ which will fill in the response +res+. See
- # WEBrick::HTTPServlet::AbstractServlet#service for details.
-
- def service(req, res)
- method_name = "do_" + req.request_method.gsub(/-/, "_")
- if respond_to?(method_name)
- __send__(method_name, req, res)
- else
- raise HTTPStatus::MethodNotAllowed,
- "unsupported method `#{req.request_method}'."
- end
- end
-
- ##
- # Provides HTTP socket emulation from the CGI environment
-
- class Socket # :nodoc:
- include Enumerable
-
- private
-
- def initialize(config, env, stdin, stdout)
- @config = config
- @env = env
- @header_part = StringIO.new
- @body_part = stdin
- @out_port = stdout
- @out_port.binmode
-
- @server_addr = @env["SERVER_ADDR"] || "0.0.0.0"
- @server_name = @env["SERVER_NAME"]
- @server_port = @env["SERVER_PORT"]
- @remote_addr = @env["REMOTE_ADDR"]
- @remote_host = @env["REMOTE_HOST"] || @remote_addr
- @remote_port = @env["REMOTE_PORT"] || 0
-
- begin
- @header_part << request_line << CRLF
- setup_header
- @header_part << CRLF
- @header_part.rewind
- rescue Exception
- raise CGIError, "invalid CGI environment"
- end
- end
-
- def request_line
- meth = @env["REQUEST_METHOD"] || "GET"
- unless url = @env["REQUEST_URI"]
- url = (@env["SCRIPT_NAME"] || File.expand_path($0)).dup
- url << @env["PATH_INFO"].to_s
- url = WEBrick::HTTPUtils.escape_path(url)
- if query_string = @env["QUERY_STRING"]
- unless query_string.empty?
- url << "?" << query_string
- end
- end
- end
- # we cannot get real HTTP version of client ;)
- httpv = @config[:HTTPVersion]
- return "#{meth} #{url} HTTP/#{httpv}"
- end
-
- def setup_header
- @env.each{|key, value|
- case key
- when "CONTENT_TYPE", "CONTENT_LENGTH"
- add_header(key.gsub(/_/, "-"), value)
- when /^HTTP_(.*)/
- add_header($1.gsub(/_/, "-"), value)
- end
- }
- end
-
- def add_header(hdrname, value)
- unless value.empty?
- @header_part << hdrname << ": " << value << CRLF
- end
- end
-
- def input
- @header_part.eof? ? @body_part : @header_part
- end
-
- public
-
- def peeraddr
- [nil, @remote_port, @remote_host, @remote_addr]
- end
-
- def addr
- [nil, @server_port, @server_name, @server_addr]
- end
-
- def gets(eol=LF, size=nil)
- input.gets(eol, size)
- end
-
- def read(size=nil)
- input.read(size)
- end
-
- def each
- input.each{|line| yield(line) }
- end
-
- def eof?
- input.eof?
- end
-
- def <<(data)
- @out_port << data
- end
-
- def write(data)
- @out_port.write(data)
- end
-
- def cert
- return nil unless defined?(OpenSSL)
- if pem = @env["SSL_SERVER_CERT"]
- OpenSSL::X509::Certificate.new(pem) unless pem.empty?
- end
- end
-
- def peer_cert
- return nil unless defined?(OpenSSL)
- if pem = @env["SSL_CLIENT_CERT"]
- OpenSSL::X509::Certificate.new(pem) unless pem.empty?
- end
- end
-
- def peer_cert_chain
- return nil unless defined?(OpenSSL)
- if @env["SSL_CLIENT_CERT_CHAIN_0"]
- keys = @env.keys
- certs = keys.sort.collect{|k|
- if /^SSL_CLIENT_CERT_CHAIN_\d+$/ =~ k
- if pem = @env[k]
- OpenSSL::X509::Certificate.new(pem) unless pem.empty?
- end
- end
- }
- certs.compact
- end
- end
-
- def cipher
- return nil unless defined?(OpenSSL)
- if cipher = @env["SSL_CIPHER"]
- ret = [ cipher ]
- ret << @env["SSL_PROTOCOL"]
- ret << @env["SSL_CIPHER_USEKEYSIZE"]
- ret << @env["SSL_CIPHER_ALGKEYSIZE"]
- ret
- end
- end
- end
- end
-end
diff --git a/tool/lib/webrick/compat.rb b/tool/lib/webrick/compat.rb
deleted file mode 100644
index c497a1933c92af..00000000000000
--- a/tool/lib/webrick/compat.rb
+++ /dev/null
@@ -1,36 +0,0 @@
-# frozen_string_literal: false
-#
-# compat.rb -- cross platform compatibility
-#
-# Author: IPR -- Internet Programming with Ruby -- writers
-# Copyright (c) 2002 GOTOU Yuuzou
-# Copyright (c) 2002 Internet Programming with Ruby writers. All rights
-# reserved.
-#
-# $IPR: compat.rb,v 1.6 2002/10/01 17:16:32 gotoyuzo Exp $
-
-##
-# System call error module used by webrick for cross platform compatibility.
-#
-# EPROTO:: protocol error
-# ECONNRESET:: remote host reset the connection request
-# ECONNABORTED:: Client sent TCP reset (RST) before server has accepted the
-# connection requested by client.
-#
-module Errno
- ##
- # Protocol error.
-
- class EPROTO < SystemCallError; end
-
- ##
- # Remote host reset the connection request.
-
- class ECONNRESET < SystemCallError; end
-
- ##
- # Client sent TCP reset (RST) before server has accepted the connection
- # requested by client.
-
- class ECONNABORTED < SystemCallError; end
-end
diff --git a/tool/lib/webrick/config.rb b/tool/lib/webrick/config.rb
deleted file mode 100644
index 9f2ab44f49d80c..00000000000000
--- a/tool/lib/webrick/config.rb
+++ /dev/null
@@ -1,158 +0,0 @@
-# frozen_string_literal: false
-#
-# config.rb -- Default configurations.
-#
-# Author: IPR -- Internet Programming with Ruby -- writers
-# Copyright (c) 2000, 2001 TAKAHASHI Masayoshi, GOTOU Yuuzou
-# Copyright (c) 2003 Internet Programming with Ruby writers. All rights
-# reserved.
-#
-# $IPR: config.rb,v 1.52 2003/07/22 19:20:42 gotoyuzo Exp $
-
-require_relative 'version'
-require_relative 'httpversion'
-require_relative 'httputils'
-require_relative 'utils'
-require_relative 'log'
-
-module WEBrick
- module Config
- LIBDIR = File::dirname(__FILE__) # :nodoc:
-
- # for GenericServer
- General = Hash.new { |hash, key|
- case key
- when :ServerName
- hash[key] = Utils.getservername
- else
- nil
- end
- }.update(
- :BindAddress => nil, # "0.0.0.0" or "::" or nil
- :Port => nil, # users MUST specify this!!
- :MaxClients => 100, # maximum number of the concurrent connections
- :ServerType => nil, # default: WEBrick::SimpleServer
- :Logger => nil, # default: WEBrick::Log.new
- :ServerSoftware => "WEBrick/#{WEBrick::VERSION} " +
- "(Ruby/#{RUBY_VERSION}/#{RUBY_RELEASE_DATE})",
- :TempDir => ENV['TMPDIR']||ENV['TMP']||ENV['TEMP']||'/tmp',
- :DoNotListen => false,
- :StartCallback => nil,
- :StopCallback => nil,
- :AcceptCallback => nil,
- :DoNotReverseLookup => true,
- :ShutdownSocketWithoutClose => false,
- )
-
- # for HTTPServer, HTTPRequest, HTTPResponse ...
- HTTP = General.dup.update(
- :Port => 80,
- :RequestTimeout => 30,
- :HTTPVersion => HTTPVersion.new("1.1"),
- :AccessLog => nil,
- :MimeTypes => HTTPUtils::DefaultMimeTypes,
- :DirectoryIndex => ["index.html","index.htm","index.cgi","index.rhtml"],
- :DocumentRoot => nil,
- :DocumentRootOptions => { :FancyIndexing => true },
- :RequestCallback => nil,
- :ServerAlias => nil,
- :InputBufferSize => 65536, # input buffer size in reading request body
- :OutputBufferSize => 65536, # output buffer size in sending File or IO
-
- # for HTTPProxyServer
- :ProxyAuthProc => nil,
- :ProxyContentHandler => nil,
- :ProxyVia => true,
- :ProxyTimeout => true,
- :ProxyURI => nil,
-
- :CGIInterpreter => nil,
- :CGIPathEnv => nil,
-
- # workaround: if Request-URIs contain 8bit chars,
- # they should be escaped before calling of URI::parse().
- :Escape8bitURI => false
- )
-
- ##
- # Default configuration for WEBrick::HTTPServlet::FileHandler
- #
- # :AcceptableLanguages::
- # Array of languages allowed for accept-language. There is no default
- # :DirectoryCallback::
- # Allows preprocessing of directory requests. There is no default
- # callback.
- # :FancyIndexing::
- # If true, show an index for directories. The default is true.
- # :FileCallback::
- # Allows preprocessing of file requests. There is no default callback.
- # :HandlerCallback::
- # Allows preprocessing of requests. There is no default callback.
- # :HandlerTable::
- # Maps file suffixes to file handlers. DefaultFileHandler is used by
- # default but any servlet can be used.
- # :NondisclosureName::
- # Do not show files matching this array of globs. .ht* and *~ are
- # excluded by default.
- # :UserDir::
- # Directory inside ~user to serve content from for /~user requests.
- # Only works if mounted on /. Disabled by default.
-
- FileHandler = {
- :NondisclosureName => [".ht*", "*~"],
- :FancyIndexing => false,
- :HandlerTable => {},
- :HandlerCallback => nil,
- :DirectoryCallback => nil,
- :FileCallback => nil,
- :UserDir => nil, # e.g. "public_html"
- :AcceptableLanguages => [] # ["en", "ja", ... ]
- }
-
- ##
- # Default configuration for WEBrick::HTTPAuth::BasicAuth
- #
- # :AutoReloadUserDB:: Reload the user database provided by :UserDB
- # automatically?
-
- BasicAuth = {
- :AutoReloadUserDB => true,
- }
-
- ##
- # Default configuration for WEBrick::HTTPAuth::DigestAuth.
- #
- # :Algorithm:: MD5, MD5-sess (default), SHA1, SHA1-sess
- # :Domain:: An Array of URIs that define the protected space
- # :Qop:: 'auth' for authentication, 'auth-int' for integrity protection or
- # both
- # :UseOpaque:: Should the server send opaque values to the client? This
- # helps prevent replay attacks.
- # :CheckNc:: Should the server check the nonce count? This helps the
- # server detect replay attacks.
- # :UseAuthenticationInfoHeader:: Should the server send an
- # AuthenticationInfo header?
- # :AutoReloadUserDB:: Reload the user database provided by :UserDB
- # automatically?
- # :NonceExpirePeriod:: How long should we store used nonces? Default is
- # 30 minutes.
- # :NonceExpireDelta:: How long is a nonce valid? Default is 1 minute
- # :InternetExplorerHack:: Hack which allows Internet Explorer to work.
- # :OperaHack:: Hack which allows Opera to work.
-
- DigestAuth = {
- :Algorithm => 'MD5-sess', # or 'MD5'
- :Domain => nil, # an array includes domain names.
- :Qop => [ 'auth' ], # 'auth' or 'auth-int' or both.
- :UseOpaque => true,
- :UseNextNonce => false,
- :CheckNc => false,
- :UseAuthenticationInfoHeader => true,
- :AutoReloadUserDB => true,
- :NonceExpirePeriod => 30*60,
- :NonceExpireDelta => 60,
- :InternetExplorerHack => true,
- :OperaHack => true,
- }
- end
-end
diff --git a/tool/lib/webrick/cookie.rb b/tool/lib/webrick/cookie.rb
deleted file mode 100644
index 5fd3bfb22862ee..00000000000000
--- a/tool/lib/webrick/cookie.rb
+++ /dev/null
@@ -1,172 +0,0 @@
-# frozen_string_literal: false
-#
-# cookie.rb -- Cookie class
-#
-# Author: IPR -- Internet Programming with Ruby -- writers
-# Copyright (c) 2000, 2001 TAKAHASHI Masayoshi, GOTOU Yuuzou
-# Copyright (c) 2002 Internet Programming with Ruby writers. All rights
-# reserved.
-#
-# $IPR: cookie.rb,v 1.16 2002/09/21 12:23:35 gotoyuzo Exp $
-
-require 'time'
-require_relative 'httputils'
-
-module WEBrick
-
- ##
- # Processes HTTP cookies
-
- class Cookie
-
- ##
- # The cookie name
-
- attr_reader :name
-
- ##
- # The cookie value
-
- attr_accessor :value
-
- ##
- # The cookie version
-
- attr_accessor :version
-
- ##
- # The cookie domain
- attr_accessor :domain
-
- ##
- # The cookie path
-
- attr_accessor :path
-
- ##
- # Is this a secure cookie?
-
- attr_accessor :secure
-
- ##
- # The cookie comment
-
- attr_accessor :comment
-
- ##
- # The maximum age of the cookie
-
- attr_accessor :max_age
-
- #attr_accessor :comment_url, :discard, :port
-
- ##
- # Creates a new cookie with the given +name+ and +value+
-
- def initialize(name, value)
- @name = name
- @value = value
- @version = 0 # Netscape Cookie
-
- @domain = @path = @secure = @comment = @max_age =
- @expires = @comment_url = @discard = @port = nil
- end
-
- ##
- # Sets the cookie expiration to the time +t+. The expiration time may be
- # a false value to disable expiration or a Time or HTTP format time string
- # to set the expiration date.
-
- def expires=(t)
- @expires = t && (t.is_a?(Time) ? t.httpdate : t.to_s)
- end
-
- ##
- # Retrieves the expiration time as a Time
-
- def expires
- @expires && Time.parse(@expires)
- end
-
- ##
- # The cookie string suitable for use in an HTTP header
-
- def to_s
- ret = ""
- ret << @name << "=" << @value
- ret << "; " << "Version=" << @version.to_s if @version > 0
- ret << "; " << "Domain=" << @domain if @domain
- ret << "; " << "Expires=" << @expires if @expires
- ret << "; " << "Max-Age=" << @max_age.to_s if @max_age
- ret << "; " << "Comment=" << @comment if @comment
- ret << "; " << "Path=" << @path if @path
- ret << "; " << "Secure" if @secure
- ret
- end
-
- ##
- # Parses a Cookie field sent from the user-agent. Returns an array of
- # cookies.
-
- def self.parse(str)
- if str
- ret = []
- cookie = nil
- ver = 0
- str.split(/;\s+/).each{|x|
- key, val = x.split(/=/,2)
- val = val ? HTTPUtils::dequote(val) : ""
- case key
- when "$Version"; ver = val.to_i
- when "$Path"; cookie.path = val
- when "$Domain"; cookie.domain = val
- when "$Port"; cookie.port = val
- else
- ret << cookie if cookie
- cookie = self.new(key, val)
- cookie.version = ver
- end
- }
- ret << cookie if cookie
- ret
- end
- end
-
- ##
- # Parses the cookie in +str+
-
- def self.parse_set_cookie(str)
- cookie_elem = str.split(/;/)
- first_elem = cookie_elem.shift
- first_elem.strip!
- key, value = first_elem.split(/=/, 2)
- cookie = new(key, HTTPUtils.dequote(value))
- cookie_elem.each{|pair|
- pair.strip!
- key, value = pair.split(/=/, 2)
- if value
- value = HTTPUtils.dequote(value.strip)
- end
- case key.downcase
- when "domain" then cookie.domain = value
- when "path" then cookie.path = value
- when "expires" then cookie.expires = value
- when "max-age" then cookie.max_age = Integer(value)
- when "comment" then cookie.comment = value
- when "version" then cookie.version = Integer(value)
- when "secure" then cookie.secure = true
- end
- }
- return cookie
- end
-
- ##
- # Parses the cookies in +str+
-
- def self.parse_set_cookies(str)
- return str.split(/,(?=[^;,]*=)|,$/).collect{|c|
- parse_set_cookie(c)
- }
- end
- end
-end
diff --git a/tool/lib/webrick/htmlutils.rb b/tool/lib/webrick/htmlutils.rb
deleted file mode 100644
index ed9f4ac0d3e97c..00000000000000
--- a/tool/lib/webrick/htmlutils.rb
+++ /dev/null
@@ -1,30 +0,0 @@
-# frozen_string_literal: false
-#--
-# htmlutils.rb -- HTMLUtils Module
-#
-# Author: IPR -- Internet Programming with Ruby -- writers
-# Copyright (c) 2000, 2001 TAKAHASHI Masayoshi, GOTOU Yuuzou
-# Copyright (c) 2002 Internet Programming with Ruby writers. All rights
-# reserved.
-#
-# $IPR: htmlutils.rb,v 1.7 2002/09/21 12:23:35 gotoyuzo Exp $
-
-module WEBrick
- module HTMLUtils
-
- ##
- # Escapes &, ", > and < in +string+
-
- def escape(string)
- return "" unless string
- str = string.b
- str.gsub!(/&/n, '&')
- str.gsub!(/\"/n, '"')
- str.gsub!(/>/n, '>')
- str.gsub!(/header['content-length']
explicitly provided.
- # Example:
- #
- # server.mount_proc '/' do |req, res|
- # res.chunked = true
- # # or
- # # res.header['content-length'] = 10
- # res.body = proc { |out| out.write(Time.now.to_s) }
- # end
-
- attr_accessor :body
-
- ##
- # Request method for this response
-
- attr_accessor :request_method
-
- ##
- # Request URI for this response
-
- attr_accessor :request_uri
-
- ##
- # Request HTTP version for this response
-
- attr_accessor :request_http_version
-
- ##
- # Filename of the static file in this response. Only used by the
- # FileHandler servlet.
-
- attr_accessor :filename
-
- ##
- # Is this a keep-alive response?
-
- attr_accessor :keep_alive
-
- ##
- # Configuration for this response
-
- attr_reader :config
-
- ##
- # Bytes sent in this response
-
- attr_reader :sent_size
-
- ##
- # Creates a new HTTP response object. WEBrick::Config::HTTP is the
- # default configuration.
-
- def initialize(config)
- @config = config
- @buffer_size = config[:OutputBufferSize]
- @logger = config[:Logger]
- @header = Hash.new
- @status = HTTPStatus::RC_OK
- @reason_phrase = nil
- @http_version = HTTPVersion::convert(@config[:HTTPVersion])
- @body = ''
- @keep_alive = true
- @cookies = []
- @request_method = nil
- @request_uri = nil
- @request_http_version = @http_version # temporary
- @chunked = false
- @filename = nil
- @sent_size = 0
- @bodytempfile = nil
- end
-
- ##
- # The response's HTTP status line
-
- def status_line
- "HTTP/#@http_version #@status #@reason_phrase".rstrip << CRLF
- end
-
- ##
- # Sets the response's status to the +status+ code
-
- def status=(status)
- @status = status
- @reason_phrase = HTTPStatus::reason_phrase(status)
- end
-
- ##
- # Retrieves the response header +field+
-
- def [](field)
- @header[field.downcase]
- end
-
- ##
- # Sets the response header +field+ to +value+
-
- def []=(field, value)
- @chunked = value.to_s.downcase == 'chunked' if field.downcase == 'transfer-encoding'
- @header[field.downcase] = value.to_s
- end
-
- ##
- # The content-length header
-
- def content_length
- if len = self['content-length']
- return Integer(len)
- end
- end
-
- ##
- # Sets the content-length header to +len+
-
- def content_length=(len)
- self['content-length'] = len.to_s
- end
-
- ##
- # The content-type header
-
- def content_type
- self['content-type']
- end
-
- ##
- # Sets the content-type header to +type+
-
- def content_type=(type)
- self['content-type'] = type
- end
-
- ##
- # Iterates over each header in the response
-
- def each
- @header.each{|field, value| yield(field, value) }
- end
-
- ##
- # Will this response body be returned using chunked transfer-encoding?
-
- def chunked?
- @chunked
- end
-
- ##
- # Enables chunked transfer encoding.
-
- def chunked=(val)
- @chunked = val ? true : false
- end
-
- ##
- # Will this response's connection be kept alive?
-
- def keep_alive?
- @keep_alive
- end
-
- ##
- # Sends the response on +socket+
-
- def send_response(socket) # :nodoc:
- begin
- setup_header()
- send_header(socket)
- send_body(socket)
- rescue Errno::EPIPE, Errno::ECONNRESET, Errno::ENOTCONN => ex
- @logger.debug(ex)
- @keep_alive = false
- rescue Exception => ex
- @logger.error(ex)
- @keep_alive = false
- end
- end
-
- ##
- # Sets up the headers for sending
-
- def setup_header() # :nodoc:
- @reason_phrase ||= HTTPStatus::reason_phrase(@status)
- @header['server'] ||= @config[:ServerSoftware]
- @header['date'] ||= Time.now.httpdate
-
- # HTTP/0.9 features
- if @request_http_version < "1.0"
- @http_version = HTTPVersion.new("0.9")
- @keep_alive = false
- end
-
- # HTTP/1.0 features
- if @request_http_version < "1.1"
- if chunked?
- @chunked = false
- ver = @request_http_version.to_s
- msg = "chunked is set for an HTTP/#{ver} request. (ignored)"
- @logger.warn(msg)
- end
- end
-
- # Determine the message length (RFC2616 -- 4.4 Message Length)
- if @status == 304 || @status == 204 || HTTPStatus::info?(@status)
- @header.delete('content-length')
- @body = ""
- elsif chunked?
- @header["transfer-encoding"] = "chunked"
- @header.delete('content-length')
- elsif %r{^multipart/byteranges} =~ @header['content-type']
- @header.delete('content-length')
- elsif @header['content-length'].nil?
- if @body.respond_to? :readpartial
- elsif @body.respond_to? :call
- make_body_tempfile
- else
- @header['content-length'] = (@body ? @body.bytesize : 0).to_s
- end
- end
-
- # Keep-Alive connection.
- if @header['connection'] == "close"
- @keep_alive = false
- elsif keep_alive?
- if chunked? || @header['content-length'] || @status == 304 || @status == 204 || HTTPStatus.info?(@status)
- @header['connection'] = "Keep-Alive"
- else
- msg = "Could not determine content-length of response body. Set content-length of the response or set Response#chunked = true"
- @logger.warn(msg)
- @header['connection'] = "close"
- @keep_alive = false
- end
- else
- @header['connection'] = "close"
- end
-
- # Location is a single absoluteURI.
- if location = @header['location']
- if @request_uri
- @header['location'] = @request_uri.merge(location).to_s
- end
- end
- end
-
- def make_body_tempfile # :nodoc:
- return if @bodytempfile
- bodytempfile = Tempfile.create("webrick")
- if @body.nil?
- # nothing
- elsif @body.respond_to? :readpartial
- IO.copy_stream(@body, bodytempfile)
- @body.close
- elsif @body.respond_to? :call
- @body.call(bodytempfile)
- else
- bodytempfile.write @body
- end
- bodytempfile.rewind
- @body = @bodytempfile = bodytempfile
- @header['content-length'] = bodytempfile.stat.size.to_s
- end
-
- def remove_body_tempfile # :nodoc:
- if @bodytempfile
- @bodytempfile.close
- File.unlink @bodytempfile.path
- @bodytempfile = nil
- end
- end
-
-
- ##
- # Sends the headers on +socket+
-
- def send_header(socket) # :nodoc:
- if @http_version.major > 0
- data = status_line()
- @header.each{|key, value|
- tmp = key.gsub(/\bwww|^te$|\b\w/){ $&.upcase }
- data << "#{tmp}: #{check_header(value)}" << CRLF
- }
- @cookies.each{|cookie|
- data << "Set-Cookie: " << check_header(cookie.to_s) << CRLF
- }
- data << CRLF
- socket.write(data)
- end
- rescue InvalidHeader => e
- @header.clear
- @cookies.clear
- set_error e
- retry
- end
-
- ##
- # Sends the body on +socket+
-
- def send_body(socket) # :nodoc:
- if @body.respond_to? :readpartial then
- send_body_io(socket)
- elsif @body.respond_to?(:call) then
- send_body_proc(socket)
- else
- send_body_string(socket)
- end
- end
-
- ##
- # Redirects to +url+ with a WEBrick::HTTPStatus::Redirect +status+.
- #
- # Example:
- #
- # res.set_redirect WEBrick::HTTPStatus::TemporaryRedirect
-
- def set_redirect(status, url)
- url = URI(url).to_s
- @body = "#{url}.\n"
- @header['location'] = url
- raise status
- end
-
- ##
- # Creates an error page for exception +ex+ with an optional +backtrace+
-
- def set_error(ex, backtrace=false)
- case ex
- when HTTPStatus::Status
- @keep_alive = false if HTTPStatus::error?(ex.code)
- self.status = ex.code
- else
- @keep_alive = false
- self.status = HTTPStatus::RC_INTERNAL_SERVER_ERROR
- end
- @header['content-type'] = "text/html; charset=ISO-8859-1"
-
- if respond_to?(:create_error_page)
- create_error_page()
- return
- end
-
- if @request_uri
- host, port = @request_uri.host, @request_uri.port
- else
- host, port = @config[:ServerName], @config[:Port]
- end
-
- error_body(backtrace, ex, host, port)
- end
-
- private
-
- def check_header(header_value)
- header_value = header_value.to_s
- if /[\r\n]/ =~ header_value
- raise InvalidHeader
- else
- header_value
- end
- end
-
- # :stopdoc:
-
- def error_body(backtrace, ex, host, port)
- @body = ''
- @body << <<-_end_of_html_
-
-
- " - ex.backtrace.each{|line| @body << "\t#{line}\n"} - @body << "
Hello, World!" - # - # return 200, "text/html", content - # end - # end - # - # This servlet must be provided two arguments at mount time: - # - # server.mount '/configurable', Configurable, 'red', '2em' - - class AbstractServlet - - ## - # Factory for servlet instances that will handle a request from +server+ - # using +options+ from the mount point. By default a new servlet - # instance is created for every call. - - def self.get_instance(server, *options) - self.new(server, *options) - end - - ## - # Initializes a new servlet for +server+ using +options+ which are - # stored as-is in +@options+. +@logger+ is also provided. - - def initialize(server, *options) - @server = @config = server - @logger = @server[:Logger] - @options = options - end - - ## - # Dispatches to a +do_+ method based on +req+ if such a method is - # available. (+do_GET+ for a GET request). Raises a MethodNotAllowed - # exception if the method is not implemented. - - def service(req, res) - method_name = "do_" + req.request_method.gsub(/-/, "_") - if respond_to?(method_name) - __send__(method_name, req, res) - else - raise HTTPStatus::MethodNotAllowed, - "unsupported method `#{req.request_method}'." - end - end - - ## - # Raises a NotFound exception - - def do_GET(req, res) - raise HTTPStatus::NotFound, "not found." - end - - ## - # Dispatches to do_GET - - def do_HEAD(req, res) - do_GET(req, res) - end - - ## - # Returns the allowed HTTP request methods - - def do_OPTIONS(req, res) - m = self.methods.grep(/\Ado_([A-Z]+)\z/) {$1} - m.sort! - res["allow"] = m.join(",") - end - - private - - ## - # Redirects to a path ending in / - - def redirect_to_directory_uri(req, res) - if req.path[-1] != ?/ - location = WEBrick::HTTPUtils.escape_path(req.path + "/") - if req.query_string && req.query_string.bytesize > 0 - location << "?" << req.query_string - end - res.set_redirect(HTTPStatus::MovedPermanently, location) - end - end - end - - end -end diff --git a/tool/lib/webrick/httpservlet/cgi_runner.rb b/tool/lib/webrick/httpservlet/cgi_runner.rb deleted file mode 100644 index 0398c16749c0c1..00000000000000 --- a/tool/lib/webrick/httpservlet/cgi_runner.rb +++ /dev/null @@ -1,47 +0,0 @@ -# frozen_string_literal: false -# -# cgi_runner.rb -- CGI launcher. -# -# Author: IPR -- Internet Programming with Ruby -- writers -# Copyright (c) 2000 TAKAHASHI Masayoshi, GOTOU YUUZOU -# Copyright (c) 2002 Internet Programming with Ruby writers. All rights -# reserved. -# -# $IPR: cgi_runner.rb,v 1.9 2002/09/25 11:33:15 gotoyuzo Exp $ - -def sysread(io, size) - buf = "" - while size > 0 - tmp = io.sysread(size) - buf << tmp - size -= tmp.bytesize - end - return buf -end - -STDIN.binmode - -len = sysread(STDIN, 8).to_i -out = sysread(STDIN, len) -STDOUT.reopen(File.open(out, "w")) - -len = sysread(STDIN, 8).to_i -err = sysread(STDIN, len) -STDERR.reopen(File.open(err, "w")) - -len = sysread(STDIN, 8).to_i -dump = sysread(STDIN, len) -hash = Marshal.restore(dump) -ENV.keys.each{|name| ENV.delete(name) } -hash.each{|k, v| ENV[k] = v if v } - -dir = File::dirname(ENV["SCRIPT_FILENAME"]) -Dir::chdir dir - -if ARGV[0] - argv = ARGV.dup - argv << ENV["SCRIPT_FILENAME"] - exec(*argv) - # NOTREACHED -end -exec ENV["SCRIPT_FILENAME"] diff --git a/tool/lib/webrick/httpservlet/cgihandler.rb b/tool/lib/webrick/httpservlet/cgihandler.rb deleted file mode 100644 index 4457770b7a006d..00000000000000 --- a/tool/lib/webrick/httpservlet/cgihandler.rb +++ /dev/null @@ -1,126 +0,0 @@ -# frozen_string_literal: false -# -# cgihandler.rb -- CGIHandler Class -# -# Author: IPR -- Internet Programming with Ruby -- writers -# Copyright (c) 2001 TAKAHASHI Masayoshi, GOTOU Yuuzou -# Copyright (c) 2002 Internet Programming with Ruby writers. All rights -# reserved. -# -# $IPR: cgihandler.rb,v 1.27 2003/03/21 19:56:01 gotoyuzo Exp $ - -require 'rbconfig' -require 'tempfile' -require_relative '../config' -require_relative 'abstract' - -module WEBrick - module HTTPServlet - - ## - # Servlet for handling CGI scripts - # - # Example: - # - # server.mount('/cgi/my_script', WEBrick::HTTPServlet::CGIHandler, - # '/path/to/my_script') - - class CGIHandler < AbstractServlet - Ruby = RbConfig.ruby # :nodoc: - CGIRunner = "\"#{Ruby}\" \"#{WEBrick::Config::LIBDIR}/httpservlet/cgi_runner.rb\"" # :nodoc: - CGIRunnerArray = [Ruby, "#{WEBrick::Config::LIBDIR}/httpservlet/cgi_runner.rb".freeze].freeze # :nodoc: - - ## - # Creates a new CGI script servlet for the script at +name+ - - def initialize(server, name) - super(server, name) - @script_filename = name - @tempdir = server[:TempDir] - interpreter = server[:CGIInterpreter] - if interpreter.is_a?(Array) - @cgicmd = CGIRunnerArray + interpreter - else - @cgicmd = "#{CGIRunner} #{interpreter}" - end - end - - # :stopdoc: - - def do_GET(req, res) - cgi_in = IO::popen(@cgicmd, "wb") - cgi_out = Tempfile.new("webrick.cgiout.", @tempdir, mode: IO::BINARY) - cgi_out.set_encoding("ASCII-8BIT") - cgi_err = Tempfile.new("webrick.cgierr.", @tempdir, mode: IO::BINARY) - cgi_err.set_encoding("ASCII-8BIT") - begin - cgi_in.sync = true - meta = req.meta_vars - meta["SCRIPT_FILENAME"] = @script_filename - meta["PATH"] = @config[:CGIPathEnv] - meta.delete("HTTP_PROXY") - if /mswin|bccwin|mingw/ =~ RUBY_PLATFORM - meta["SystemRoot"] = ENV["SystemRoot"] - end - dump = Marshal.dump(meta) - - cgi_in.write("%8d" % cgi_out.path.bytesize) - cgi_in.write(cgi_out.path) - cgi_in.write("%8d" % cgi_err.path.bytesize) - cgi_in.write(cgi_err.path) - cgi_in.write("%8d" % dump.bytesize) - cgi_in.write(dump) - - req.body { |chunk| cgi_in.write(chunk) } - ensure - cgi_in.close - status = $?.exitstatus - sleep 0.1 if /mswin|bccwin|mingw/ =~ RUBY_PLATFORM - data = cgi_out.read - cgi_out.close(true) - if errmsg = cgi_err.read - if errmsg.bytesize > 0 - @logger.error("CGIHandler: #{@script_filename}:\n" + errmsg) - end - end - cgi_err.close(true) - end - - if status != 0 - @logger.error("CGIHandler: #{@script_filename} exit with #{status}") - end - - data = "" unless data - raw_header, body = data.split(/^[\xd\xa]+/, 2) - raise HTTPStatus::InternalServerError, - "Premature end of script headers: #{@script_filename}" if body.nil? - - begin - header = HTTPUtils::parse_header(raw_header) - if /^(\d+)/ =~ header['status'][0] - res.status = $1.to_i - header.delete('status') - end - if header.has_key?('location') - # RFC 3875 6.2.3, 6.2.4 - res.status = 302 unless (300...400) === res.status - end - if header.has_key?('set-cookie') - header['set-cookie'].each{|k| - res.cookies << Cookie.parse_set_cookie(k) - } - header.delete('set-cookie') - end - header.each{|key, val| res[key] = val.join(", ") } - rescue => ex - raise HTTPStatus::InternalServerError, ex.message - end - res.body = body - end - alias do_POST do_GET - - # :startdoc: - end - - end -end diff --git a/tool/lib/webrick/httpservlet/erbhandler.rb b/tool/lib/webrick/httpservlet/erbhandler.rb deleted file mode 100644 index cd09e5f2161540..00000000000000 --- a/tool/lib/webrick/httpservlet/erbhandler.rb +++ /dev/null @@ -1,88 +0,0 @@ -# frozen_string_literal: false -# -# erbhandler.rb -- ERBHandler Class -# -# Author: IPR -- Internet Programming with Ruby -- writers -# Copyright (c) 2001 TAKAHASHI Masayoshi, GOTOU Yuuzou -# Copyright (c) 2002 Internet Programming with Ruby writers. All rights -# reserved. -# -# $IPR: erbhandler.rb,v 1.25 2003/02/24 19:25:31 gotoyuzo Exp $ - -require_relative 'abstract' - -require 'erb' - -module WEBrick - module HTTPServlet - - ## - # ERBHandler evaluates an ERB file and returns the result. This handler - # is automatically used if there are .rhtml files in a directory served by - # the FileHandler. - # - # ERBHandler supports GET and POST methods. - # - # The ERB file is evaluated with the local variables +servlet_request+ and - # +servlet_response+ which are a WEBrick::HTTPRequest and - # WEBrick::HTTPResponse respectively. - # - # Example .rhtml file: - # - # Request to <%= servlet_request.request_uri %> - # - # Query params <%= servlet_request.query.inspect %> - - class ERBHandler < AbstractServlet - - ## - # Creates a new ERBHandler on +server+ that will evaluate and serve the - # ERB file +name+ - - def initialize(server, name) - super(server, name) - @script_filename = name - end - - ## - # Handles GET requests - - def do_GET(req, res) - unless defined?(ERB) - @logger.warn "#{self.class}: ERB not defined." - raise HTTPStatus::Forbidden, "ERBHandler cannot work." - end - begin - data = File.open(@script_filename, &:read) - res.body = evaluate(ERB.new(data), req, res) - res['content-type'] ||= - HTTPUtils::mime_type(@script_filename, @config[:MimeTypes]) - rescue StandardError - raise - rescue Exception => ex - @logger.error(ex) - raise HTTPStatus::InternalServerError, ex.message - end - end - - ## - # Handles POST requests - - alias do_POST do_GET - - private - - ## - # Evaluates +erb+ providing +servlet_request+ and +servlet_response+ as - # local variables. - - def evaluate(erb, servlet_request, servlet_response) - Module.new.module_eval{ - servlet_request.meta_vars - servlet_request.query - erb.result(binding) - } - end - end - end -end diff --git a/tool/lib/webrick/httpservlet/filehandler.rb b/tool/lib/webrick/httpservlet/filehandler.rb deleted file mode 100644 index 010df0e918d22f..00000000000000 --- a/tool/lib/webrick/httpservlet/filehandler.rb +++ /dev/null @@ -1,552 +0,0 @@ -# frozen_string_literal: false -# -# filehandler.rb -- FileHandler Module -# -# Author: IPR -- Internet Programming with Ruby -- writers -# Copyright (c) 2001 TAKAHASHI Masayoshi, GOTOU Yuuzou -# Copyright (c) 2003 Internet Programming with Ruby writers. All rights -# reserved. -# -# $IPR: filehandler.rb,v 1.44 2003/06/07 01:34:51 gotoyuzo Exp $ - -require 'time' - -require_relative '../htmlutils' -require_relative '../httputils' -require_relative '../httpstatus' - -module WEBrick - module HTTPServlet - - ## - # Servlet for serving a single file. You probably want to use the - # FileHandler servlet instead as it handles directories and fancy indexes. - # - # Example: - # - # server.mount('/my_page.txt', WEBrick::HTTPServlet::DefaultFileHandler, - # '/path/to/my_page.txt') - # - # This servlet handles If-Modified-Since and Range requests. - - class DefaultFileHandler < AbstractServlet - - ## - # Creates a DefaultFileHandler instance for the file at +local_path+. - - def initialize(server, local_path) - super(server, local_path) - @local_path = local_path - end - - # :stopdoc: - - def do_GET(req, res) - st = File::stat(@local_path) - mtime = st.mtime - res['etag'] = sprintf("%x-%x-%x", st.ino, st.size, st.mtime.to_i) - - if not_modified?(req, res, mtime, res['etag']) - res.body = '' - raise HTTPStatus::NotModified - elsif req['range'] - make_partial_content(req, res, @local_path, st.size) - raise HTTPStatus::PartialContent - else - mtype = HTTPUtils::mime_type(@local_path, @config[:MimeTypes]) - res['content-type'] = mtype - res['content-length'] = st.size.to_s - res['last-modified'] = mtime.httpdate - res.body = File.open(@local_path, "rb") - end - end - - def not_modified?(req, res, mtime, etag) - if ir = req['if-range'] - begin - if Time.httpdate(ir) >= mtime - return true - end - rescue - if HTTPUtils::split_header_value(ir).member?(res['etag']) - return true - end - end - end - - if (ims = req['if-modified-since']) && Time.parse(ims) >= mtime - return true - end - - if (inm = req['if-none-match']) && - HTTPUtils::split_header_value(inm).member?(res['etag']) - return true - end - - return false - end - - # returns a lambda for webrick/httpresponse.rb send_body_proc - def multipart_body(body, parts, boundary, mtype, filesize) - lambda do |socket| - begin - begin - first = parts.shift - last = parts.shift - socket.write( - "--#{boundary}#{CRLF}" \ - "Content-Type: #{mtype}#{CRLF}" \ - "Content-Range: bytes #{first}-#{last}/#{filesize}#{CRLF}" \ - "#{CRLF}" - ) - - begin - IO.copy_stream(body, socket, last - first + 1, first) - rescue NotImplementedError - body.seek(first, IO::SEEK_SET) - IO.copy_stream(body, socket, last - first + 1) - end - socket.write(CRLF) - end while parts[0] - socket.write("--#{boundary}--#{CRLF}") - ensure - body.close - end - end - end - - def make_partial_content(req, res, filename, filesize) - mtype = HTTPUtils::mime_type(filename, @config[:MimeTypes]) - unless ranges = HTTPUtils::parse_range_header(req['range']) - raise HTTPStatus::BadRequest, - "Unrecognized range-spec: \"#{req['range']}\"" - end - File.open(filename, "rb"){|io| - if ranges.size > 1 - time = Time.now - boundary = "#{time.sec}_#{time.usec}_#{Process::pid}" - parts = [] - ranges.each {|range| - prange = prepare_range(range, filesize) - next if prange[0] < 0 - parts.concat(prange) - } - raise HTTPStatus::RequestRangeNotSatisfiable if parts.empty? - res["content-type"] = "multipart/byteranges; boundary=#{boundary}" - if req.http_version < '1.1' - res['connection'] = 'close' - else - res.chunked = true - end - res.body = multipart_body(io.dup, parts, boundary, mtype, filesize) - elsif range = ranges[0] - first, last = prepare_range(range, filesize) - raise HTTPStatus::RequestRangeNotSatisfiable if first < 0 - res['content-type'] = mtype - res['content-range'] = "bytes #{first}-#{last}/#{filesize}" - res['content-length'] = (last - first + 1).to_s - res.body = io.dup - else - raise HTTPStatus::BadRequest - end - } - end - - def prepare_range(range, filesize) - first = range.first < 0 ? filesize + range.first : range.first - return -1, -1 if first < 0 || first >= filesize - last = range.last < 0 ? filesize + range.last : range.last - last = filesize - 1 if last >= filesize - return first, last - end - - # :startdoc: - end - - ## - # Serves a directory including fancy indexing and a variety of other - # options. - # - # Example: - # - # server.mount('/assets', WEBrick::HTTPServlet::FileHandler, - # '/path/to/assets') - - class FileHandler < AbstractServlet - HandlerTable = Hash.new # :nodoc: - - ## - # Allow custom handling of requests for files with +suffix+ by class - # +handler+ - - def self.add_handler(suffix, handler) - HandlerTable[suffix] = handler - end - - ## - # Remove custom handling of requests for files with +suffix+ - - def self.remove_handler(suffix) - HandlerTable.delete(suffix) - end - - ## - # Creates a FileHandler servlet on +server+ that serves files starting - # at directory +root+ - # - # +options+ may be a Hash containing keys from - # WEBrick::Config::FileHandler or +true+ or +false+. - # - # If +options+ is true or false then +:FancyIndexing+ is enabled or - # disabled respectively. - - def initialize(server, root, options={}, default=Config::FileHandler) - @config = server.config - @logger = @config[:Logger] - @root = File.expand_path(root) - if options == true || options == false - options = { :FancyIndexing => options } - end - @options = default.dup.update(options) - end - - # :stopdoc: - - def set_filesystem_encoding(str) - enc = Encoding.find('filesystem') - if enc == Encoding::US_ASCII - str.b - else - str.dup.force_encoding(enc) - end - end - - def service(req, res) - # if this class is mounted on "/" and /~username is requested. - # we're going to override path information before invoking service. - if defined?(Etc) && @options[:UserDir] && req.script_name.empty? - if %r|^(/~([^/]+))| =~ req.path_info - script_name, user = $1, $2 - path_info = $' - begin - passwd = Etc::getpwnam(user) - @root = File::join(passwd.dir, @options[:UserDir]) - req.script_name = script_name - req.path_info = path_info - rescue - @logger.debug "#{self.class}#do_GET: getpwnam(#{user}) failed" - end - end - end - prevent_directory_traversal(req, res) - super(req, res) - end - - def do_GET(req, res) - unless exec_handler(req, res) - set_dir_list(req, res) - end - end - - def do_POST(req, res) - unless exec_handler(req, res) - raise HTTPStatus::NotFound, "`#{req.path}' not found." - end - end - - def do_OPTIONS(req, res) - unless exec_handler(req, res) - super(req, res) - end - end - - # ToDo - # RFC2518: HTTP Extensions for Distributed Authoring -- WEBDAV - # - # PROPFIND PROPPATCH MKCOL DELETE PUT COPY MOVE - # LOCK UNLOCK - - # RFC3253: Versioning Extensions to WebDAV - # (Web Distributed Authoring and Versioning) - # - # VERSION-CONTROL REPORT CHECKOUT CHECK_IN UNCHECKOUT - # MKWORKSPACE UPDATE LABEL MERGE ACTIVITY - - private - - def trailing_pathsep?(path) - # check for trailing path separator: - # File.dirname("/aaaa/bbbb/") #=> "/aaaa") - # File.dirname("/aaaa/bbbb/x") #=> "/aaaa/bbbb") - # File.dirname("/aaaa/bbbb") #=> "/aaaa") - # File.dirname("/aaaa/bbbbx") #=> "/aaaa") - return File.dirname(path) != File.dirname(path+"x") - end - - def prevent_directory_traversal(req, res) - # Preventing directory traversal on Windows platforms; - # Backslashes (0x5c) in path_info are not interpreted as special - # character in URI notation. So the value of path_info should be - # normalize before accessing to the filesystem. - - # dirty hack for filesystem encoding; in nature, File.expand_path - # should not be used for path normalization. [Bug #3345] - path = req.path_info.dup.force_encoding(Encoding.find("filesystem")) - if trailing_pathsep?(req.path_info) - # File.expand_path removes the trailing path separator. - # Adding a character is a workaround to save it. - # File.expand_path("/aaa/") #=> "/aaa" - # File.expand_path("/aaa/" + "x") #=> "/aaa/x" - expanded = File.expand_path(path + "x") - expanded.chop! # remove trailing "x" - else - expanded = File.expand_path(path) - end - expanded.force_encoding(req.path_info.encoding) - req.path_info = expanded - end - - def exec_handler(req, res) - raise HTTPStatus::NotFound, "`#{req.path}' not found." unless @root - if set_filename(req, res) - handler = get_handler(req, res) - call_callback(:HandlerCallback, req, res) - h = handler.get_instance(@config, res.filename) - h.service(req, res) - return true - end - call_callback(:HandlerCallback, req, res) - return false - end - - def get_handler(req, res) - suffix1 = (/\.(\w+)\z/ =~ res.filename) && $1.downcase - if /\.(\w+)\.([\w\-]+)\z/ =~ res.filename - if @options[:AcceptableLanguages].include?($2.downcase) - suffix2 = $1.downcase - end - end - handler_table = @options[:HandlerTable] - return handler_table[suffix1] || handler_table[suffix2] || - HandlerTable[suffix1] || HandlerTable[suffix2] || - DefaultFileHandler - end - - def set_filename(req, res) - res.filename = @root - path_info = req.path_info.scan(%r|/[^/]*|) - - path_info.unshift("") # dummy for checking @root dir - while base = path_info.first - base = set_filesystem_encoding(base) - break if base == "/" - break unless File.directory?(File.expand_path(res.filename + base)) - shift_path_info(req, res, path_info) - call_callback(:DirectoryCallback, req, res) - end - - if base = path_info.first - base = set_filesystem_encoding(base) - if base == "/" - if file = search_index_file(req, res) - shift_path_info(req, res, path_info, file) - call_callback(:FileCallback, req, res) - return true - end - shift_path_info(req, res, path_info) - elsif file = search_file(req, res, base) - shift_path_info(req, res, path_info, file) - call_callback(:FileCallback, req, res) - return true - else - raise HTTPStatus::NotFound, "`#{req.path}' not found." - end - end - - return false - end - - def check_filename(req, res, name) - if nondisclosure_name?(name) || windows_ambiguous_name?(name) - @logger.warn("the request refers nondisclosure name `#{name}'.") - raise HTTPStatus::NotFound, "`#{req.path}' not found." - end - end - - def shift_path_info(req, res, path_info, base=nil) - tmp = path_info.shift - base = base || set_filesystem_encoding(tmp) - req.path_info = path_info.join - req.script_name << base - res.filename = File.expand_path(res.filename + base) - check_filename(req, res, File.basename(res.filename)) - end - - def search_index_file(req, res) - @config[:DirectoryIndex].each{|index| - if file = search_file(req, res, "/"+index) - return file - end - } - return nil - end - - def search_file(req, res, basename) - langs = @options[:AcceptableLanguages] - path = res.filename + basename - if File.file?(path) - return basename - elsif langs.size > 0 - req.accept_language.each{|lang| - path_with_lang = path + ".#{lang}" - if langs.member?(lang) && File.file?(path_with_lang) - return basename + ".#{lang}" - end - } - (langs - req.accept_language).each{|lang| - path_with_lang = path + ".#{lang}" - if File.file?(path_with_lang) - return basename + ".#{lang}" - end - } - end - return nil - end - - def call_callback(callback_name, req, res) - if cb = @options[callback_name] - cb.call(req, res) - end - end - - def windows_ambiguous_name?(name) - return true if /[. ]+\z/ =~ name - return true if /::\$DATA\z/ =~ name - return false - end - - def nondisclosure_name?(name) - @options[:NondisclosureName].each{|pattern| - if File.fnmatch(pattern, name, File::FNM_CASEFOLD) - return true - end - } - return false - end - - def set_dir_list(req, res) - redirect_to_directory_uri(req, res) - unless @options[:FancyIndexing] - raise HTTPStatus::Forbidden, "no access permission to `#{req.path}'" - end - local_path = res.filename - list = Dir::entries(local_path).collect{|name| - next if name == "." || name == ".." - next if nondisclosure_name?(name) - next if windows_ambiguous_name?(name) - st = (File::stat(File.join(local_path, name)) rescue nil) - if st.nil? - [ name, nil, -1 ] - elsif st.directory? - [ name + "/", st.mtime, -1 ] - else - [ name, st.mtime, st.size ] - end - } - list.compact! - - query = req.query - - d0 = nil - idx = nil - %w[N M S].each_with_index do |q, i| - if d = query.delete(q) - idx ||= i - d0 ||= d - end - end - d0 ||= "A" - idx ||= 0 - d1 = (d0 == "A") ? "D" : "A" - - if d0 == "A" - list.sort!{|a,b| a[idx] <=> b[idx] } - else - list.sort!{|a,b| b[idx] <=> a[idx] } - end - - namewidth = query["NameWidth"] - if namewidth == "*" - namewidth = nil - elsif !namewidth or (namewidth = namewidth.to_i) < 2 - namewidth = 25 - end - query = query.inject('') {|s, (k, v)| s << '&' << HTMLUtils::escape("#{k}=#{v}")} - - type = "text/html" - case enc = Encoding.find('filesystem') - when Encoding::US_ASCII, Encoding::ASCII_8BIT - else - type << "; charset=\"#{enc.name}\"" - end - res['content-type'] = type - - title = "Index of #{HTMLUtils::escape(req.path)}" - res.body = <<-_end_of_html_ - - -
-Name | " - res.body << "Last modified | " - res.body << "Size | \n" - res.body << "
---|---|---|
#{HTMLUtils::escape(dname)} | " - s << "" << (time ? time.strftime("%Y/%m/%d %H:%M") : "") << " | " - s << "" << (size >= 0 ? size.to_s : "-") << " |
:MaxClients
configuration sets this.
-
- attr_reader :tokens
-
- ##
- # Sockets listening for connections.
-
- attr_reader :listeners
-
- ##
- # Creates a new generic server from +config+. The default configuration
- # comes from +default+.
-
- def initialize(config={}, default=Config::General)
- @config = default.dup.update(config)
- @status = :Stop
- @config[:Logger] ||= Log::new
- @logger = @config[:Logger]
-
- @tokens = Thread::SizedQueue.new(@config[:MaxClients])
- @config[:MaxClients].times{ @tokens.push(nil) }
-
- webrickv = WEBrick::VERSION
- rubyv = "#{RUBY_VERSION} (#{RUBY_RELEASE_DATE}) [#{RUBY_PLATFORM}]"
- @logger.info("WEBrick #{webrickv}")
- @logger.info("ruby #{rubyv}")
-
- @listeners = []
- @shutdown_pipe = nil
- unless @config[:DoNotListen]
- raise ArgumentError, "Port must an integer" unless @config[:Port].to_s == @config[:Port].to_i.to_s
-
- @config[:Port] = @config[:Port].to_i
- if @config[:Listen]
- warn(":Listen option is deprecated; use GenericServer#listen", uplevel: 1)
- end
- listen(@config[:BindAddress], @config[:Port])
- if @config[:Port] == 0
- @config[:Port] = @listeners[0].addr[1]
- end
- end
- end
-
- ##
- # Retrieves +key+ from the configuration
-
- def [](key)
- @config[key]
- end
-
- ##
- # Adds listeners from +address+ and +port+ to the server. See
- # WEBrick::Utils::create_listeners for details.
-
- def listen(address, port)
- @listeners += Utils::create_listeners(address, port)
- end
-
- ##
- # Starts the server and runs the +block+ for each connection. This method
- # does not return until the server is stopped from a signal handler or
- # another thread using #stop or #shutdown.
- #
- # If the block raises a subclass of StandardError the exception is logged
- # and ignored. If an IOError or Errno::EBADF exception is raised the
- # exception is ignored. If an Exception subclass is raised the exception
- # is logged and re-raised which stops the server.
- #
- # To completely shut down a server call #shutdown from ensure:
- #
- # server = WEBrick::GenericServer.new
- # # or WEBrick::HTTPServer.new
- #
- # begin
- # server.start
- # ensure
- # server.shutdown
- # end
-
- def start(&block)
- raise ServerError, "already started." if @status != :Stop
- server_type = @config[:ServerType] || SimpleServer
-
- setup_shutdown_pipe
-
- server_type.start{
- @logger.info \
- "#{self.class}#start: pid=#{$$} port=#{@config[:Port]}"
- @status = :Running
- call_callback(:StartCallback)
-
- shutdown_pipe = @shutdown_pipe
-
- thgroup = ThreadGroup.new
- begin
- while @status == :Running
- begin
- sp = shutdown_pipe[0]
- if svrs = IO.select([sp, *@listeners])
- if svrs[0].include? sp
- # swallow shutdown pipe
- buf = String.new
- nil while String ===
- sp.read_nonblock([sp.nread, 8].max, buf, exception: false)
- break
- end
- svrs[0].each{|svr|
- @tokens.pop # blocks while no token is there.
- if sock = accept_client(svr)
- unless config[:DoNotReverseLookup].nil?
- sock.do_not_reverse_lookup = !!config[:DoNotReverseLookup]
- end
- th = start_thread(sock, &block)
- th[:WEBrickThread] = true
- thgroup.add(th)
- else
- @tokens.push(nil)
- end
- }
- end
- rescue Errno::EBADF, Errno::ENOTSOCK, IOError => ex
- # if the listening socket was closed in GenericServer#shutdown,
- # IO::select raise it.
- rescue StandardError => ex
- msg = "#{ex.class}: #{ex.message}\n\t#{ex.backtrace[0]}"
- @logger.error msg
- rescue Exception => ex
- @logger.fatal ex
- raise
- end
- end
- ensure
- cleanup_shutdown_pipe(shutdown_pipe)
- cleanup_listener
- @status = :Shutdown
- @logger.info "going to shutdown ..."
- thgroup.list.each{|th| th.join if th[:WEBrickThread] }
- call_callback(:StopCallback)
- @logger.info "#{self.class}#start done."
- @status = :Stop
- end
- }
- end
-
- ##
- # Stops the server from accepting new connections.
-
- def stop
- if @status == :Running
- @status = :Shutdown
- end
-
- alarm_shutdown_pipe {|f| f.write_nonblock("\0")}
- end
-
- ##
- # Shuts down the server and all listening sockets. New listeners must be
- # provided to restart the server.
-
- def shutdown
- stop
-
- alarm_shutdown_pipe(&:close)
- end
-
- ##
- # You must subclass GenericServer and implement \#run which accepts a TCP
- # client socket
-
- def run(sock)
- @logger.fatal "run() must be provided by user."
- end
-
- private
-
- # :stopdoc:
-
- ##
- # Accepts a TCP client socket from the TCP server socket +svr+ and returns
- # the client socket.
-
- def accept_client(svr)
- case sock = svr.to_io.accept_nonblock(exception: false)
- when :wait_readable
- nil
- else
- if svr.respond_to?(:start_immediately)
- sock = OpenSSL::SSL::SSLSocket.new(sock, ssl_context)
- sock.sync_close = true
- # we cannot do OpenSSL::SSL::SSLSocket#accept here because
- # a slow client can prevent us from accepting connections
- # from other clients
- end
- sock
- end
- rescue Errno::ECONNRESET, Errno::ECONNABORTED,
- Errno::EPROTO, Errno::EINVAL
- nil
- rescue StandardError => ex
- msg = "#{ex.class}: #{ex.message}\n\t#{ex.backtrace[0]}"
- @logger.error msg
- nil
- end
-
- ##
- # Starts a server thread for the client socket +sock+ that runs the given
- # +block+.
- #
- # Sets the socket to the :WEBrickSocket
thread local variable
- # in the thread.
- #
- # If any errors occur in the block they are logged and handled.
-
- def start_thread(sock, &block)
- Thread.start{
- begin
- Thread.current[:WEBrickSocket] = sock
- begin
- addr = sock.peeraddr
- @logger.debug "accept: #{addr[3]}:#{addr[1]}"
- rescue SocketError
- @logger.debug "accept: "
- raise
- end
- if sock.respond_to?(:sync_close=) && @config[:SSLStartImmediately]
- WEBrick::Utils.timeout(@config[:RequestTimeout]) do
- begin
- sock.accept # OpenSSL::SSL::SSLSocket#accept
- rescue Errno::ECONNRESET, Errno::ECONNABORTED,
- Errno::EPROTO, Errno::EINVAL
- Thread.exit
- end
- end
- end
- call_callback(:AcceptCallback, sock)
- block ? block.call(sock) : run(sock)
- rescue Errno::ENOTCONN
- @logger.debug "Errno::ENOTCONN raised"
- rescue ServerError => ex
- msg = "#{ex.class}: #{ex.message}\n\t#{ex.backtrace[0]}"
- @logger.error msg
- rescue Exception => ex
- @logger.error ex
- ensure
- @tokens.push(nil)
- Thread.current[:WEBrickSocket] = nil
- if addr
- @logger.debug "close: #{addr[3]}:#{addr[1]}"
- else
- @logger.debug "close: "
- end
- sock.close
- end
- }
- end
-
- ##
- # Calls the callback +callback_name+ from the configuration with +args+
-
- def call_callback(callback_name, *args)
- @config[callback_name]&.call(*args)
- end
-
- def setup_shutdown_pipe
- return @shutdown_pipe ||= IO.pipe
- end
-
- def cleanup_shutdown_pipe(shutdown_pipe)
- @shutdown_pipe = nil
- shutdown_pipe&.each(&:close)
- end
-
- def alarm_shutdown_pipe
- _, pipe = @shutdown_pipe # another thread may modify @shutdown_pipe.
- if pipe
- if !pipe.closed?
- begin
- yield pipe
- rescue IOError # closed by another thread.
- end
- end
- end
- end
-
- def cleanup_listener
- @listeners.each{|s|
- if @logger.debug?
- addr = s.addr
- @logger.debug("close TCPSocket(#{addr[2]}, #{addr[1]})")
- end
- begin
- s.shutdown
- rescue Errno::ENOTCONN
- # when `Errno::ENOTCONN: Socket is not connected' on some platforms,
- # call #close instead of #shutdown.
- # (ignore @config[:ShutdownSocketWithoutClose])
- s.close
- else
- unless @config[:ShutdownSocketWithoutClose]
- s.close
- end
- end
- }
- @listeners.clear
- end
- end # end of GenericServer
-end
diff --git a/tool/lib/webrick/ssl.rb b/tool/lib/webrick/ssl.rb
deleted file mode 100644
index e448095a12f6dd..00000000000000
--- a/tool/lib/webrick/ssl.rb
+++ /dev/null
@@ -1,215 +0,0 @@
-# frozen_string_literal: false
-#
-# ssl.rb -- SSL/TLS enhancement for GenericServer
-#
-# Copyright (c) 2003 GOTOU Yuuzou All rights reserved.
-#
-# $Id$
-
-require 'webrick'
-require 'openssl'
-
-module WEBrick
- module Config
- svrsoft = General[:ServerSoftware]
- osslv = ::OpenSSL::OPENSSL_VERSION.split[1]
-
- ##
- # Default SSL server configuration.
- #
- # WEBrick can automatically create a self-signed certificate if
- # :SSLCertName
is set. For more information on the various
- # SSL options see OpenSSL::SSL::SSLContext.
- #
- # :ServerSoftware ::
- # The server software name used in the Server: header.
- # :SSLEnable :: false,
- # Enable SSL for this server. Defaults to false.
- # :SSLCertificate ::
- # The SSL certificate for the server.
- # :SSLPrivateKey ::
- # The SSL private key for the server certificate.
- # :SSLClientCA :: nil,
- # Array of certificates that will be sent to the client.
- # :SSLExtraChainCert :: nil,
- # Array of certificates that will be added to the certificate chain
- # :SSLCACertificateFile :: nil,
- # Path to a CA certificate file
- # :SSLCACertificatePath :: nil,
- # Path to a directory containing CA certificates
- # :SSLCertificateStore :: nil,
- # OpenSSL::X509::Store used for certificate validation of the client
- # :SSLTmpDhCallback :: nil,
- # Callback invoked when DH parameters are required.
- # :SSLVerifyClient ::
- # Sets whether the client is verified. This defaults to VERIFY_NONE
- # which is typical for an HTTPS server.
- # :SSLVerifyDepth ::
- # Number of CA certificates to walk when verifying a certificate chain
- # :SSLVerifyCallback ::
- # Custom certificate verification callback
- # :SSLServerNameCallback::
- # Custom servername indication callback
- # :SSLTimeout ::
- # Maximum session lifetime
- # :SSLOptions ::
- # Various SSL options
- # :SSLCiphers ::
- # Ciphers to be used
- # :SSLStartImmediately ::
- # Immediately start SSL upon connection? Defaults to true
- # :SSLCertName ::
- # SSL certificate name. Must be set to enable automatic certificate
- # creation.
- # :SSLCertComment ::
- # Comment used during automatic certificate creation.
-
- SSL = {
- :ServerSoftware => "#{svrsoft} OpenSSL/#{osslv}",
- :SSLEnable => false,
- :SSLCertificate => nil,
- :SSLPrivateKey => nil,
- :SSLClientCA => nil,
- :SSLExtraChainCert => nil,
- :SSLCACertificateFile => nil,
- :SSLCACertificatePath => nil,
- :SSLCertificateStore => nil,
- :SSLTmpDhCallback => nil,
- :SSLVerifyClient => ::OpenSSL::SSL::VERIFY_NONE,
- :SSLVerifyDepth => nil,
- :SSLVerifyCallback => nil, # custom verification
- :SSLTimeout => nil,
- :SSLOptions => nil,
- :SSLCiphers => nil,
- :SSLStartImmediately => true,
- # Must specify if you use auto generated certificate.
- :SSLCertName => nil,
- :SSLCertComment => "Generated by Ruby/OpenSSL"
- }
- General.update(SSL)
- end
-
- module Utils
- ##
- # Creates a self-signed certificate with the given number of +bits+,
- # the issuer +cn+ and a +comment+ to be stored in the certificate.
-
- def create_self_signed_cert(bits, cn, comment)
- rsa = OpenSSL::PKey::RSA.new(bits){|p, n|
- case p
- when 0; $stderr.putc "." # BN_generate_prime
- when 1; $stderr.putc "+" # BN_generate_prime
- when 2; $stderr.putc "*" # searching good prime,
- # n = #of try,
- # but also data from BN_generate_prime
- when 3; $stderr.putc "\n" # found good prime, n==0 - p, n==1 - q,
- # but also data from BN_generate_prime
- else; $stderr.putc "*" # BN_generate_prime
- end
- }
- cert = OpenSSL::X509::Certificate.new
- cert.version = 2
- cert.serial = 1
- name = (cn.kind_of? String) ? OpenSSL::X509::Name.parse(cn)
- : OpenSSL::X509::Name.new(cn)
- cert.subject = name
- cert.issuer = name
- cert.not_before = Time.now
- cert.not_after = Time.now + (365*24*60*60)
- cert.public_key = rsa.public_key
-
- ef = OpenSSL::X509::ExtensionFactory.new(nil,cert)
- ef.issuer_certificate = cert
- cert.extensions = [
- ef.create_extension("basicConstraints","CA:FALSE"),
- ef.create_extension("keyUsage", "keyEncipherment, digitalSignature, keyAgreement, dataEncipherment"),
- ef.create_extension("subjectKeyIdentifier", "hash"),
- ef.create_extension("extendedKeyUsage", "serverAuth"),
- ef.create_extension("nsComment", comment),
- ]
- aki = ef.create_extension("authorityKeyIdentifier",
- "keyid:always,issuer:always")
- cert.add_extension(aki)
- cert.sign(rsa, "SHA256")
-
- return [ cert, rsa ]
- end
- module_function :create_self_signed_cert
- end
-
- ##
- #--
- # Updates WEBrick::GenericServer with SSL functionality
-
- class GenericServer
-
- ##
- # SSL context for the server when run in SSL mode
-
- def ssl_context # :nodoc:
- @ssl_context ||= begin
- if @config[:SSLEnable]
- ssl_context = setup_ssl_context(@config)
- @logger.info("\n" + @config[:SSLCertificate].to_text)
- ssl_context
- end
- end
- end
-
- undef listen
-
- ##
- # Updates +listen+ to enable SSL when the SSL configuration is active.
-
- def listen(address, port) # :nodoc:
- listeners = Utils::create_listeners(address, port)
- if @config[:SSLEnable]
- listeners.collect!{|svr|
- ssvr = ::OpenSSL::SSL::SSLServer.new(svr, ssl_context)
- ssvr.start_immediately = @config[:SSLStartImmediately]
- ssvr
- }
- end
- @listeners += listeners
- setup_shutdown_pipe
- end
-
- ##
- # Sets up an SSL context for +config+
-
- def setup_ssl_context(config) # :nodoc:
- unless config[:SSLCertificate]
- cn = config[:SSLCertName]
- comment = config[:SSLCertComment]
- cert, key = Utils::create_self_signed_cert(2048, cn, comment)
- config[:SSLCertificate] = cert
- config[:SSLPrivateKey] = key
- end
- ctx = OpenSSL::SSL::SSLContext.new
- ctx.key = config[:SSLPrivateKey]
- ctx.cert = config[:SSLCertificate]
- ctx.client_ca = config[:SSLClientCA]
- ctx.extra_chain_cert = config[:SSLExtraChainCert]
- ctx.ca_file = config[:SSLCACertificateFile]
- ctx.ca_path = config[:SSLCACertificatePath]
- ctx.cert_store = config[:SSLCertificateStore]
- ctx.tmp_dh_callback = config[:SSLTmpDhCallback]
- ctx.verify_mode = config[:SSLVerifyClient]
- ctx.verify_depth = config[:SSLVerifyDepth]
- ctx.verify_callback = config[:SSLVerifyCallback]
- ctx.servername_cb = config[:SSLServerNameCallback] || proc { |args| ssl_servername_callback(*args) }
- ctx.timeout = config[:SSLTimeout]
- ctx.options = config[:SSLOptions]
- ctx.ciphers = config[:SSLCiphers]
- ctx
- end
-
- ##
- # ServerNameIndication callback
-
- def ssl_servername_callback(sslsocket, hostname = nil)
- # default
- end
-
- end
-end
diff --git a/tool/lib/webrick/utils.rb b/tool/lib/webrick/utils.rb
deleted file mode 100644
index a96d6f03fd0492..00000000000000
--- a/tool/lib/webrick/utils.rb
+++ /dev/null
@@ -1,265 +0,0 @@
-# frozen_string_literal: false
-#
-# utils.rb -- Miscellaneous utilities
-#
-# Author: IPR -- Internet Programming with Ruby -- writers
-# Copyright (c) 2001 TAKAHASHI Masayoshi, GOTOU Yuuzou
-# Copyright (c) 2002 Internet Programming with Ruby writers. All rights
-# reserved.
-#
-# $IPR: utils.rb,v 1.10 2003/02/16 22:22:54 gotoyuzo Exp $
-
-require 'socket'
-require 'io/nonblock'
-require 'etc'
-
-module WEBrick
- module Utils
- ##
- # Sets IO operations on +io+ to be non-blocking
- def set_non_blocking(io)
- io.nonblock = true if io.respond_to?(:nonblock=)
- end
- module_function :set_non_blocking
-
- ##
- # Sets the close on exec flag for +io+
- def set_close_on_exec(io)
- io.close_on_exec = true if io.respond_to?(:close_on_exec=)
- end
- module_function :set_close_on_exec
-
- ##
- # Changes the process's uid and gid to the ones of +user+
- def su(user)
- if pw = Etc.getpwnam(user)
- Process::initgroups(user, pw.gid)
- Process::Sys::setgid(pw.gid)
- Process::Sys::setuid(pw.uid)
- else
- warn("WEBrick::Utils::su doesn't work on this platform", uplevel: 1)
- end
- end
- module_function :su
-
- ##
- # The server hostname
- def getservername
- Socket::gethostname
- end
- module_function :getservername
-
- ##
- # Creates TCP server sockets bound to +address+:+port+ and returns them.
- #
- # It will create IPV4 and IPV6 sockets on all interfaces.
- def create_listeners(address, port)
- unless port
- raise ArgumentError, "must specify port"
- end
- sockets = Socket.tcp_server_sockets(address, port)
- sockets = sockets.map {|s|
- s.autoclose = false
- ts = TCPServer.for_fd(s.fileno)
- s.close
- ts
- }
- return sockets
- end
- module_function :create_listeners
-
- ##
- # Characters used to generate random strings
- RAND_CHARS = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" +
- "0123456789" +
- "abcdefghijklmnopqrstuvwxyz"
-
- ##
- # Generates a random string of length +len+
- def random_string(len)
- rand_max = RAND_CHARS.bytesize
- ret = ""
- len.times{ ret << RAND_CHARS[rand(rand_max)] }
- ret
- end
- module_function :random_string
-
- ###########
-
- require "timeout"
- require "singleton"
-
- ##
- # Class used to manage timeout handlers across multiple threads.
- #
- # Timeout handlers should be managed by using the class methods which are
- # synchronized.
- #
- # id = TimeoutHandler.register(10, Timeout::Error)
- # begin
- # sleep 20
- # puts 'foo'
- # ensure
- # TimeoutHandler.cancel(id)
- # end
- #
- # will raise Timeout::Error
- #
- # id = TimeoutHandler.register(10, Timeout::Error)
- # begin
- # sleep 5
- # puts 'foo'
- # ensure
- # TimeoutHandler.cancel(id)
- # end
- #
- # will print 'foo'
- #
- class TimeoutHandler
- include Singleton
-
- ##
- # Mutex used to synchronize access across threads
- TimeoutMutex = Thread::Mutex.new # :nodoc:
-
- ##
- # Registers a new timeout handler
- #
- # +time+:: Timeout in seconds
- # +exception+:: Exception to raise when timeout elapsed
- def TimeoutHandler.register(seconds, exception)
- at = Process.clock_gettime(Process::CLOCK_MONOTONIC) + seconds
- instance.register(Thread.current, at, exception)
- end
-
- ##
- # Cancels the timeout handler +id+
- def TimeoutHandler.cancel(id)
- instance.cancel(Thread.current, id)
- end
-
- def self.terminate
- instance.terminate
- end
-
- ##
- # Creates a new TimeoutHandler. You should use ::register and ::cancel
- # instead of creating the timeout handler directly.
- def initialize
- TimeoutMutex.synchronize{
- @timeout_info = Hash.new
- }
- @queue = Thread::Queue.new
- @watcher = nil
- end
-
- # :nodoc:
- private \
- def watch
- to_interrupt = []
- while true
- now = Process.clock_gettime(Process::CLOCK_MONOTONIC)
- wakeup = nil
- to_interrupt.clear
- TimeoutMutex.synchronize{
- @timeout_info.each {|thread, ary|
- next unless ary
- ary.each{|info|
- time, exception = *info
- if time < now
- to_interrupt.push [thread, info.object_id, exception]
- elsif !wakeup || time < wakeup
- wakeup = time
- end
- }
- }
- }
- to_interrupt.each {|arg| interrupt(*arg)}
- if !wakeup
- @queue.pop
- elsif (wakeup -= now) > 0
- begin
- (th = Thread.start {@queue.pop}).join(wakeup)
- ensure
- th&.kill&.join
- end
- end
- @queue.clear
- end
- end
-
- # :nodoc:
- private \
- def watcher
- (w = @watcher)&.alive? and return w # usual case
- TimeoutMutex.synchronize{
- (w = @watcher)&.alive? and next w # pathological check
- @watcher = Thread.start(&method(:watch))
- }
- end
-
- ##
- # Interrupts the timeout handler +id+ and raises +exception+
- def interrupt(thread, id, exception)
- if cancel(thread, id) && thread.alive?
- thread.raise(exception, "execution timeout")
- end
- end
-
- ##
- # Registers a new timeout handler
- #
- # +time+:: Timeout in seconds
- # +exception+:: Exception to raise when timeout elapsed
- def register(thread, time, exception)
- info = nil
- TimeoutMutex.synchronize{
- (@timeout_info[thread] ||= []) << (info = [time, exception])
- }
- @queue.push nil
- watcher
- return info.object_id
- end
-
- ##
- # Cancels the timeout handler +id+
- def cancel(thread, id)
- TimeoutMutex.synchronize{
- if ary = @timeout_info[thread]
- ary.delete_if{|info| info.object_id == id }
- if ary.empty?
- @timeout_info.delete(thread)
- end
- return true
- end
- return false
- }
- end
-
- ##
- def terminate
- TimeoutMutex.synchronize{
- @timeout_info.clear
- @watcher&.kill&.join
- }
- end
- end
-
- ##
- # Executes the passed block and raises +exception+ if execution takes more
- # than +seconds+.
- #
- # If +seconds+ is zero or nil, simply executes the block
- def timeout(seconds, exception=Timeout::Error)
- return yield if seconds.nil? or seconds.zero?
- # raise ThreadError, "timeout within critical session" if Thread.critical
- id = TimeoutHandler.register(seconds, exception)
- begin
- yield(seconds)
- ensure
- TimeoutHandler.cancel(id)
- end
- end
- module_function :timeout
- end
-end
diff --git a/tool/lib/webrick/version.rb b/tool/lib/webrick/version.rb
deleted file mode 100644
index b62988bdbb5ec6..00000000000000
--- a/tool/lib/webrick/version.rb
+++ /dev/null
@@ -1,18 +0,0 @@
-# frozen_string_literal: false
-#--
-# version.rb -- version and release date
-#
-# Author: IPR -- Internet Programming with Ruby -- writers
-# Copyright (c) 2000 TAKAHASHI Masayoshi, GOTOU YUUZOU
-# Copyright (c) 2003 Internet Programming with Ruby writers. All rights
-# reserved.
-#
-# $IPR: version.rb,v 1.74 2003/07/22 19:20:43 gotoyuzo Exp $
-
-module WEBrick
-
- ##
- # The WEBrick version
-
- VERSION = "1.7.0"
-end
diff --git a/tool/m4/ruby_shared_gc.m4 b/tool/m4/ruby_shared_gc.m4
index a27b9b8505ca77..4882bca0575257 100644
--- a/tool/m4/ruby_shared_gc.m4
+++ b/tool/m4/ruby_shared_gc.m4
@@ -1,19 +1,38 @@
dnl -*- Autoconf -*-
AC_DEFUN([RUBY_SHARED_GC],[
AC_ARG_WITH(shared-gc,
- AS_HELP_STRING([--with-shared-gc],
- [Enable replacement of Ruby's GC from a shared library.]),
- [with_shared_gc=$withval], [unset with_shared_gc]
+ AS_HELP_STRING([--with-shared-gc=DIR],
+ [Enable replacement of Ruby's GC from a shared library in the specified directory.]),
+ [shared_gc_dir=$withval], [unset shared_gc_dir]
)
-AC_SUBST([with_shared_gc])
-AC_MSG_CHECKING([if Ruby is build with shared GC support])
-AS_IF([test "$with_shared_gc" = "yes"], [
+AC_MSG_CHECKING([if building with shared GC support])
+AS_IF([test x"$shared_gc_dir" != x], [
AC_MSG_RESULT([yes])
+
+ # Ensure that shared_gc_dir is always an absolute path so that Ruby
+ # never loads a shared GC from a relative path
+ AS_CASE(["$shared_gc_dir"],
+ [/*], [shared_gc_dir=$shared_gc_dir],
+ [shared_gc_dir=`pwd`/$shared_gc_dir]
+ )
+
+ # Ensure that shared_gc_dir always terminates with a /
+ AS_CASE(["$shared_gc_dir"],
+ [*/], [],
+ [shared_gc_dir="$shared_gc_dir/"]
+ )
+
AC_DEFINE([USE_SHARED_GC], [1])
+ AC_DEFINE_UNQUOTED([SHARED_GC_DIR], "$shared_gc_dir")
+
+ shared_gc_summary="yes (in $shared_gc_dir)"
], [
AC_MSG_RESULT([no])
- with_shared_gc="no"
AC_DEFINE([USE_SHARED_GC], [0])
+
+ shared_gc_summary="no"
])
+
+AC_SUBST(shared_gc_dir, "${shared_gc_dir}")
])dnl
diff --git a/tool/mk_builtin_loader.rb b/tool/mk_builtin_loader.rb
index 4abd497f0eae19..5afef655f4c536 100644
--- a/tool/mk_builtin_loader.rb
+++ b/tool/mk_builtin_loader.rb
@@ -303,13 +303,7 @@ def mk_builtin_header file
collect_iseq RubyVM::InstructionSequence.compile(code).to_a
collect_builtin(base, Ripper.sexp(code), 'top', bs = {}, inlines = {})
- begin
- f = File.open(ofile, 'w')
- rescue SystemCallError # EACCES, EPERM, EROFS, etc.
- # Fall back to the current directory
- f = File.open(File.basename(ofile), 'w')
- end
- begin
+ StringIO.open do |f|
if File::ALT_SEPARATOR
file = file.tr(File::ALT_SEPARATOR, File::SEPARATOR)
ofile = ofile.tr(File::ALT_SEPARATOR, File::SEPARATOR)
@@ -390,8 +384,13 @@ def mk_builtin_header file
f.puts " rb_load_with_builtin_functions(#{base.dump}, #{table});"
f.puts "}"
- ensure
- f.close
+
+ begin
+ File.write(ofile, f.string)
+ rescue SystemCallError # EACCES, EPERM, EROFS, etc.
+ # Fall back to the current directory
+ File.write(File.basename(ofile), f.string)
+ end
end
end
diff --git a/tool/sync_default_gems.rb b/tool/sync_default_gems.rb
index 8328a1680cc1a2..a6082b6e0399e2 100755
--- a/tool/sync_default_gems.rb
+++ b/tool/sync_default_gems.rb
@@ -384,6 +384,14 @@ def sync_default_gems(gem)
rm_rf("prism/templates/sig")
rm("prism/extconf.rb")
+ when "resolv"
+ rm_rf(%w[lib/resolv.* ext/win32/resolv test/resolv ext/win32/lib/win32/resolv.rb])
+ cp_r("#{upstream}/lib/resolv.rb", "lib")
+ cp_r("#{upstream}/resolv.gemspec", "lib")
+ cp_r("#{upstream}/ext/win32/resolv", "ext/win32")
+ move("ext/win32/resolv/lib/win32/resolv.rb", "ext/win32/lib/win32")
+ cp_r("#{upstream}/test/resolv", "test")
+ `git checkout ext/win32/resolv/depend`
else
sync_lib gem, upstream
end
diff --git a/tool/test/testunit/test_launchable.rb b/tool/test/testunit/test_launchable.rb
index 70c371e2129f03..76be876456ddb1 100644
--- a/tool/test/testunit/test_launchable.rb
+++ b/tool/test/testunit/test_launchable.rb
@@ -2,11 +2,12 @@
require 'test/unit'
require 'tempfile'
require 'json'
+require_relative '../../lib/launchable'
class TestLaunchable < Test::Unit::TestCase
def test_json_stream_writer
Tempfile.create(['launchable-test-', '.json']) do |f|
- json_stream_writer = Test::Unit::LaunchableOption::JsonStreamWriter.new(f.path)
+ json_stream_writer = Launchable::JsonStreamWriter.new(f.path)
json_stream_writer.write_array('testCases')
json_stream_writer.write_object(
{
diff --git a/tool/test/webrick/.htaccess b/tool/test/webrick/.htaccess
deleted file mode 100644
index 69d4659b9f1a33..00000000000000
--- a/tool/test/webrick/.htaccess
+++ /dev/null
@@ -1 +0,0 @@
-this file should not be published.
diff --git a/tool/test/webrick/test_cgi.rb b/tool/test/webrick/test_cgi.rb
deleted file mode 100644
index a9be8f353d989e..00000000000000
--- a/tool/test/webrick/test_cgi.rb
+++ /dev/null
@@ -1,148 +0,0 @@
-# coding: US-ASCII
-# frozen_string_literal: false
-require_relative "utils"
-require "webrick"
-require "test/unit"
-
-class TestWEBrickCGI < Test::Unit::TestCase
- CRLF = "\r\n"
-
- def teardown
- WEBrick::Utils::TimeoutHandler.terminate
- super
- end
-
- def test_cgi
- TestWEBrick.start_cgi_server{|server, addr, port, log|
- http = Net::HTTP.new(addr, port)
- req = Net::HTTP::Get.new("/webrick.cgi")
- http.request(req){|res| assert_equal("/webrick.cgi", res.body, log.call)}
- req = Net::HTTP::Get.new("/webrick.cgi/path/info")
- http.request(req){|res| assert_equal("/path/info", res.body, log.call)}
- req = Net::HTTP::Get.new("/webrick.cgi/%3F%3F%3F?foo=bar")
- http.request(req){|res| assert_equal("/???", res.body, log.call)}
- unless RUBY_PLATFORM =~ /mswin|mingw|cygwin|bccwin32|java/
- # Path info of res.body is passed via ENV.
- # ENV[] returns different value on Windows depending on locale.
- req = Net::HTTP::Get.new("/webrick.cgi/%A4%DB%A4%B2/%A4%DB%A4%B2")
- http.request(req){|res|
- assert_equal("/\xA4\xDB\xA4\xB2/\xA4\xDB\xA4\xB2", res.body, log.call)}
- end
- req = Net::HTTP::Get.new("/webrick.cgi?a=1;a=2;b=x")
- http.request(req){|res| assert_equal("a=1, a=2, b=x", res.body, log.call)}
- req = Net::HTTP::Get.new("/webrick.cgi?a=1&a=2&b=x")
- http.request(req){|res| assert_equal("a=1, a=2, b=x", res.body, log.call)}
-
- req = Net::HTTP::Post.new("/webrick.cgi?a=x;a=y;b=1")
- req["Content-Type"] = "application/x-www-form-urlencoded"
- http.request(req, "a=1;a=2;b=x"){|res|
- assert_equal("a=1, a=2, b=x", res.body, log.call)}
- req = Net::HTTP::Post.new("/webrick.cgi?a=x&a=y&b=1")
- req["Content-Type"] = "application/x-www-form-urlencoded"
- http.request(req, "a=1&a=2&b=x"){|res|
- assert_equal("a=1, a=2, b=x", res.body, log.call)}
- req = Net::HTTP::Get.new("/")
- http.request(req){|res|
- ary = res.body.lines.to_a
- assert_match(%r{/$}, ary[0], log.call)
- assert_match(%r{/webrick.cgi$}, ary[1], log.call)
- }
-
- req = Net::HTTP::Get.new("/webrick.cgi")
- req["Cookie"] = "CUSTOMER=WILE_E_COYOTE; PART_NUMBER=ROCKET_LAUNCHER_0001"
- http.request(req){|res|
- assert_equal(
- "CUSTOMER=WILE_E_COYOTE\nPART_NUMBER=ROCKET_LAUNCHER_0001\n",
- res.body, log.call)
- }
-
- req = Net::HTTP::Get.new("/webrick.cgi")
- cookie = %{$Version="1"; }
- cookie << %{Customer="WILE_E_COYOTE"; $Path="/acme"; }
- cookie << %{Part_Number="Rocket_Launcher_0001"; $Path="/acme"; }
- cookie << %{Shipping="FedEx"; $Path="/acme"}
- req["Cookie"] = cookie
- http.request(req){|res|
- assert_equal("Customer=WILE_E_COYOTE, Shipping=FedEx",
- res["Set-Cookie"], log.call)
- assert_equal("Customer=WILE_E_COYOTE\n" +
- "Part_Number=Rocket_Launcher_0001\n" +
- "Shipping=FedEx\n", res.body, log.call)
- }
- }
- end
-
- def test_bad_request
- log_tester = lambda {|log, access_log|
- assert_match(/BadRequest/, log.join)
- }
- TestWEBrick.start_cgi_server({}, log_tester) {|server, addr, port, log|
- sock = TCPSocket.new(addr, port)
- begin
- sock << "POST /webrick.cgi HTTP/1.0" << CRLF
- sock << "Content-Type: application/x-www-form-urlencoded" << CRLF
- sock << "Content-Length: 1024" << CRLF
- sock << CRLF
- sock << "a=1&a=2&b=x"
- sock.close_write
- assert_match(%r{\AHTTP/\d.\d 400 Bad Request}, sock.read, log.call)
- ensure
- sock.close
- end
- }
- end
-
- def test_cgi_env
- TestWEBrick.start_cgi_server do |server, addr, port, log|
- http = Net::HTTP.new(addr, port)
- req = Net::HTTP::Get.new("/webrick.cgi/dumpenv")
- req['proxy'] = 'http://example.com/'
- req['hello'] = 'world'
- http.request(req) do |res|
- env = Marshal.load(res.body)
- assert_equal 'world', env['HTTP_HELLO']
- assert_not_operator env, :include?, 'HTTP_PROXY'
- end
- end
- end
-
- CtrlSeq = [0x7f, *(1..31)].pack("C*").gsub(/\s+/, '')
- CtrlPat = /#{Regexp.quote(CtrlSeq)}/o
- DumpPat = /#{Regexp.quote(CtrlSeq.dump[1...-1])}/o
-
- def test_bad_uri
- log_tester = lambda {|log, access_log|
- assert_equal(1, log.length)
- assert_match(/ERROR bad URI/, log[0])
- }
- TestWEBrick.start_cgi_server({}, log_tester) {|server, addr, port, log|
- res = TCPSocket.open(addr, port) {|sock|
- sock << "GET /#{CtrlSeq}#{CRLF}#{CRLF}"
- sock.close_write
- sock.read
- }
- assert_match(%r{\AHTTP/\d.\d 400 Bad Request}, res)
- s = log.call.each_line.grep(/ERROR bad URI/)[0]
- assert_match(DumpPat, s)
- assert_not_match(CtrlPat, s)
- }
- end
-
- def test_bad_header
- log_tester = lambda {|log, access_log|
- assert_equal(1, log.length)
- assert_match(/ERROR bad header/, log[0])
- }
- TestWEBrick.start_cgi_server({}, log_tester) {|server, addr, port, log|
- res = TCPSocket.open(addr, port) {|sock|
- sock << "GET / HTTP/1.0#{CRLF}#{CtrlSeq}#{CRLF}#{CRLF}"
- sock.close_write
- sock.read
- }
- assert_match(%r{\AHTTP/\d.\d 400 Bad Request}, res)
- s = log.call.each_line.grep(/ERROR bad header/)[0]
- assert_match(DumpPat, s)
- assert_not_match(CtrlPat, s)
- }
- end
-end
diff --git a/tool/test/webrick/test_config.rb b/tool/test/webrick/test_config.rb
deleted file mode 100644
index a54a667452e92b..00000000000000
--- a/tool/test/webrick/test_config.rb
+++ /dev/null
@@ -1,17 +0,0 @@
-# frozen_string_literal: false
-require "test/unit"
-require "webrick/config"
-
-class TestWEBrickConfig < Test::Unit::TestCase
- def test_server_name_default
- config = WEBrick::Config::General.dup
- assert_equal(false, config.key?(:ServerName))
- assert_equal(WEBrick::Utils.getservername, config[:ServerName])
- assert_equal(true, config.key?(:ServerName))
- end
-
- def test_server_name_set_nil
- config = WEBrick::Config::General.dup.update(ServerName: nil)
- assert_equal(nil, config[:ServerName])
- end
-end
diff --git a/tool/test/webrick/test_cookie.rb b/tool/test/webrick/test_cookie.rb
deleted file mode 100644
index e46185f12752a6..00000000000000
--- a/tool/test/webrick/test_cookie.rb
+++ /dev/null
@@ -1,141 +0,0 @@
-# frozen_string_literal: false
-require "test/unit"
-require "webrick/cookie"
-
-class TestWEBrickCookie < Test::Unit::TestCase
- def test_new
- cookie = WEBrick::Cookie.new("foo","bar")
- assert_equal("foo", cookie.name)
- assert_equal("bar", cookie.value)
- assert_equal("foo=bar", cookie.to_s)
- end
-
- def test_time
- cookie = WEBrick::Cookie.new("foo","bar")
- t = 1000000000
- cookie.max_age = t
- assert_match(t.to_s, cookie.to_s)
-
- cookie = WEBrick::Cookie.new("foo","bar")
- t = Time.at(1000000000)
- cookie.expires = t
- assert_equal(Time, cookie.expires.class)
- assert_equal(t, cookie.expires)
- ts = t.httpdate
- cookie.expires = ts
- assert_equal(Time, cookie.expires.class)
- assert_equal(t, cookie.expires)
- assert_match(ts, cookie.to_s)
- end
-
- def test_parse
- data = ""
- data << '$Version="1"; '
- data << 'Customer="WILE_E_COYOTE"; $Path="/acme"; '
- data << 'Part_Number="Rocket_Launcher_0001"; $Path="/acme"; '
- data << 'Shipping="FedEx"; $Path="/acme"'
- cookies = WEBrick::Cookie.parse(data)
- assert_equal(3, cookies.size)
- assert_equal(1, cookies[0].version)
- assert_equal("Customer", cookies[0].name)
- assert_equal("WILE_E_COYOTE", cookies[0].value)
- assert_equal("/acme", cookies[0].path)
- assert_equal(1, cookies[1].version)
- assert_equal("Part_Number", cookies[1].name)
- assert_equal("Rocket_Launcher_0001", cookies[1].value)
- assert_equal(1, cookies[2].version)
- assert_equal("Shipping", cookies[2].name)
- assert_equal("FedEx", cookies[2].value)
-
- data = "hoge=moge; __div__session=9865ecfd514be7f7"
- cookies = WEBrick::Cookie.parse(data)
- assert_equal(2, cookies.size)
- assert_equal(0, cookies[0].version)
- assert_equal("hoge", cookies[0].name)
- assert_equal("moge", cookies[0].value)
- assert_equal("__div__session", cookies[1].name)
- assert_equal("9865ecfd514be7f7", cookies[1].value)
-
- # don't allow ,-separator
- data = "hoge=moge, __div__session=9865ecfd514be7f7"
- cookies = WEBrick::Cookie.parse(data)
- assert_equal(1, cookies.size)
- assert_equal(0, cookies[0].version)
- assert_equal("hoge", cookies[0].name)
- assert_equal("moge, __div__session=9865ecfd514be7f7", cookies[0].value)
- end
-
- def test_parse_no_whitespace
- data = [
- '$Version="1"; ',
- 'Customer="WILE_E_COYOTE";$Path="/acme";', # no SP between cookie-string
- 'Part_Number="Rocket_Launcher_0001";$Path="/acme";', # no SP between cookie-string
- 'Shipping="FedEx";$Path="/acme"'
- ].join
- cookies = WEBrick::Cookie.parse(data)
- assert_equal(1, cookies.size)
- end
-
- def test_parse_too_much_whitespaces
- # According to RFC6265,
- # cookie-string = cookie-pair *( ";" SP cookie-pair )
- # So single 0x20 is needed after ';'. We allow multiple spaces here for
- # compatibility with older WEBrick versions.
- data = [
- '$Version="1"; ',
- 'Customer="WILE_E_COYOTE";$Path="/acme"; ', # no SP between cookie-string
- 'Part_Number="Rocket_Launcher_0001";$Path="/acme"; ', # no SP between cookie-string
- 'Shipping="FedEx";$Path="/acme"'
- ].join
- cookies = WEBrick::Cookie.parse(data)
- assert_equal(3, cookies.size)
- end
-
- def test_parse_set_cookie
- data = %(Customer="WILE_E_COYOTE"; Version="1"; Path="/acme")
- cookie = WEBrick::Cookie.parse_set_cookie(data)
- assert_equal("Customer", cookie.name)
- assert_equal("WILE_E_COYOTE", cookie.value)
- assert_equal(1, cookie.version)
- assert_equal("/acme", cookie.path)
-
- data = %(Shipping="FedEx"; Version="1"; Path="/acme"; Secure)
- cookie = WEBrick::Cookie.parse_set_cookie(data)
- assert_equal("Shipping", cookie.name)
- assert_equal("FedEx", cookie.value)
- assert_equal(1, cookie.version)
- assert_equal("/acme", cookie.path)
- assert_equal(true, cookie.secure)
- end
-
- def test_parse_set_cookies
- data = %(Shipping="FedEx"; Version="1"; Path="/acme"; Secure)
- data << %(, CUSTOMER=WILE_E_COYOTE; path=/; expires=Wednesday, 09-Nov-99 23:12:40 GMT; path=/; Secure)
- data << %(, name="Aaron"; Version="1"; path="/acme")
- cookies = WEBrick::Cookie.parse_set_cookies(data)
- assert_equal(3, cookies.length)
-
- fed_ex = cookies.find { |c| c.name == 'Shipping' }
- assert_not_nil(fed_ex)
- assert_equal("Shipping", fed_ex.name)
- assert_equal("FedEx", fed_ex.value)
- assert_equal(1, fed_ex.version)
- assert_equal("/acme", fed_ex.path)
- assert_equal(true, fed_ex.secure)
-
- name = cookies.find { |c| c.name == 'name' }
- assert_not_nil(name)
- assert_equal("name", name.name)
- assert_equal("Aaron", name.value)
- assert_equal(1, name.version)
- assert_equal("/acme", name.path)
-
- customer = cookies.find { |c| c.name == 'CUSTOMER' }
- assert_not_nil(customer)
- assert_equal("CUSTOMER", customer.name)
- assert_equal("WILE_E_COYOTE", customer.value)
- assert_equal(0, customer.version)
- assert_equal("/", customer.path)
- assert_equal(Time.utc(1999, 11, 9, 23, 12, 40), customer.expires)
- end
-end
diff --git a/tool/test/webrick/test_do_not_reverse_lookup.rb b/tool/test/webrick/test_do_not_reverse_lookup.rb
deleted file mode 100644
index efcb5a92990194..00000000000000
--- a/tool/test/webrick/test_do_not_reverse_lookup.rb
+++ /dev/null
@@ -1,71 +0,0 @@
-# frozen_string_literal: false
-require "test/unit"
-require "webrick"
-require_relative "utils"
-
-class TestDoNotReverseLookup < Test::Unit::TestCase
- class DNRL < WEBrick::GenericServer
- def run(sock)
- sock << sock.do_not_reverse_lookup.to_s
- end
- end
-
- @@original_do_not_reverse_lookup_value = Socket.do_not_reverse_lookup
-
- def teardown
- Socket.do_not_reverse_lookup = @@original_do_not_reverse_lookup_value
- end
-
- def do_not_reverse_lookup?(config)
- result = nil
- TestWEBrick.start_server(DNRL, config) do |server, addr, port, log|
- TCPSocket.open(addr, port) do |sock|
- result = {'true' => true, 'false' => false}[sock.gets]
- end
- end
- result
- end
-
- # +--------------------------------------------------------------------------+
- # | Expected interaction between Socket.do_not_reverse_lookup |
- # | and WEBrick::Config::General[:DoNotReverseLookup] |
- # +----------------------------+---------------------------------------------+
- # | |WEBrick::Config::General[:DoNotReverseLookup]|
- # +----------------------------+--------------+---------------+--------------+
- # |Socket.do_not_reverse_lookup| TRUE | FALSE | NIL |
- # +----------------------------+--------------+---------------+--------------+
- # | TRUE | true | false | true |
- # +----------------------------+--------------+---------------+--------------+
- # | FALSE | true | false | false |
- # +----------------------------+--------------+---------------+--------------+
-
- def test_socket_dnrl_true_server_dnrl_true
- Socket.do_not_reverse_lookup = true
- assert_equal(true, do_not_reverse_lookup?(:DoNotReverseLookup => true))
- end
-
- def test_socket_dnrl_true_server_dnrl_false
- Socket.do_not_reverse_lookup = true
- assert_equal(false, do_not_reverse_lookup?(:DoNotReverseLookup => false))
- end
-
- def test_socket_dnrl_true_server_dnrl_nil
- Socket.do_not_reverse_lookup = true
- assert_equal(true, do_not_reverse_lookup?(:DoNotReverseLookup => nil))
- end
-
- def test_socket_dnrl_false_server_dnrl_true
- Socket.do_not_reverse_lookup = false
- assert_equal(true, do_not_reverse_lookup?(:DoNotReverseLookup => true))
- end
-
- def test_socket_dnrl_false_server_dnrl_false
- Socket.do_not_reverse_lookup = false
- assert_equal(false, do_not_reverse_lookup?(:DoNotReverseLookup => false))
- end
-
- def test_socket_dnrl_false_server_dnrl_nil
- Socket.do_not_reverse_lookup = false
- assert_equal(false, do_not_reverse_lookup?(:DoNotReverseLookup => nil))
- end
-end
diff --git a/tool/test/webrick/test_filehandler.rb b/tool/test/webrick/test_filehandler.rb
deleted file mode 100644
index 452667d4f4db10..00000000000000
--- a/tool/test/webrick/test_filehandler.rb
+++ /dev/null
@@ -1,397 +0,0 @@
-# frozen_string_literal: false
-require "test/unit"
-require_relative "utils.rb"
-require "webrick"
-require "stringio"
-require "tmpdir"
-
-class WEBrick::TestFileHandler < Test::Unit::TestCase
- def teardown
- WEBrick::Utils::TimeoutHandler.terminate
- super
- end
-
- def default_file_handler(filename)
- klass = WEBrick::HTTPServlet::DefaultFileHandler
- klass.new(WEBrick::Config::HTTP, filename)
- end
-
- def windows?
- File.directory?("\\")
- end
-
- def get_res_body(res)
- sio = StringIO.new
- sio.binmode
- res.send_body(sio)
- sio.string
- end
-
- def make_range_request(range_spec)
- msg = <<-END_OF_REQUEST
- GET / HTTP/1.0
- Range: #{range_spec}
-
- END_OF_REQUEST
- return StringIO.new(msg.gsub(/^ {6}/, ""))
- end
-
- def make_range_response(file, range_spec)
- req = WEBrick::HTTPRequest.new(WEBrick::Config::HTTP)
- req.parse(make_range_request(range_spec))
- res = WEBrick::HTTPResponse.new(WEBrick::Config::HTTP)
- size = File.size(file)
- handler = default_file_handler(file)
- handler.make_partial_content(req, res, file, size)
- return res
- end
-
- def test_make_partial_content
- filename = __FILE__
- filesize = File.size(filename)
-
- res = make_range_response(filename, "bytes=#{filesize-100}-")
- assert_match(%r{^text/plain}, res["content-type"])
- assert_equal(100, get_res_body(res).size)
-
- res = make_range_response(filename, "bytes=-100")
- assert_match(%r{^text/plain}, res["content-type"])
- assert_equal(100, get_res_body(res).size)
-
- res = make_range_response(filename, "bytes=0-99")
- assert_match(%r{^text/plain}, res["content-type"])
- assert_equal(100, get_res_body(res).size)
-
- res = make_range_response(filename, "bytes=100-199")
- assert_match(%r{^text/plain}, res["content-type"])
- assert_equal(100, get_res_body(res).size)
-
- res = make_range_response(filename, "bytes=0-0")
- assert_match(%r{^text/plain}, res["content-type"])
- assert_equal(1, get_res_body(res).size)
-
- res = make_range_response(filename, "bytes=-1")
- assert_match(%r{^text/plain}, res["content-type"])
- assert_equal(1, get_res_body(res).size)
-
- res = make_range_response(filename, "bytes=0-0, -2")
- assert_match(%r{^multipart/byteranges}, res["content-type"])
- body = get_res_body(res)
- boundary = /; boundary=(.+)/.match(res['content-type'])[1]
- off = filesize - 2
- last = filesize - 1
-
- exp = "--#{boundary}\r\n" \
- "Content-Type: text/plain\r\n" \
- "Content-Range: bytes 0-0/#{filesize}\r\n" \
- "\r\n" \
- "#{File.read(__FILE__, 1)}\r\n" \
- "--#{boundary}\r\n" \
- "Content-Type: text/plain\r\n" \
- "Content-Range: bytes #{off}-#{last}/#{filesize}\r\n" \
- "\r\n" \
- "#{File.read(__FILE__, 2, off)}\r\n" \
- "--#{boundary}--\r\n"
- assert_equal exp, body
- end
-
- def test_filehandler
- config = { :DocumentRoot => File.dirname(__FILE__), }
- this_file = File.basename(__FILE__)
- filesize = File.size(__FILE__)
- this_data = File.binread(__FILE__)
- range = nil
- bug2593 = '[ruby-dev:40030]'
-
- TestWEBrick.start_httpserver(config) do |server, addr, port, log|
- begin
- server[:DocumentRootOptions][:NondisclosureName] = []
- http = Net::HTTP.new(addr, port)
- req = Net::HTTP::Get.new("/")
- http.request(req){|res|
- assert_equal("200", res.code, log.call)
- assert_equal("text/html", res.content_type, log.call)
- assert_match(/HREF="#{this_file}"/, res.body, log.call)
- }
- req = Net::HTTP::Get.new("/#{this_file}")
- http.request(req){|res|
- assert_equal("200", res.code, log.call)
- assert_equal("text/plain", res.content_type, log.call)
- assert_equal(this_data, res.body, log.call)
- }
-
- req = Net::HTTP::Get.new("/#{this_file}", "range"=>"bytes=#{filesize-100}-")
- http.request(req){|res|
- assert_equal("206", res.code, log.call)
- assert_equal("text/plain", res.content_type, log.call)
- assert_nothing_raised(bug2593) {range = res.content_range}
- assert_equal((filesize-100)..(filesize-1), range, log.call)
- assert_equal(this_data[-100..-1], res.body, log.call)
- }
-
- req = Net::HTTP::Get.new("/#{this_file}", "range"=>"bytes=-100")
- http.request(req){|res|
- assert_equal("206", res.code, log.call)
- assert_equal("text/plain", res.content_type, log.call)
- assert_nothing_raised(bug2593) {range = res.content_range}
- assert_equal((filesize-100)..(filesize-1), range, log.call)
- assert_equal(this_data[-100..-1], res.body, log.call)
- }
-
- req = Net::HTTP::Get.new("/#{this_file}", "range"=>"bytes=0-99")
- http.request(req){|res|
- assert_equal("206", res.code, log.call)
- assert_equal("text/plain", res.content_type, log.call)
- assert_nothing_raised(bug2593) {range = res.content_range}
- assert_equal(0..99, range, log.call)
- assert_equal(this_data[0..99], res.body, log.call)
- }
-
- req = Net::HTTP::Get.new("/#{this_file}", "range"=>"bytes=100-199")
- http.request(req){|res|
- assert_equal("206", res.code, log.call)
- assert_equal("text/plain", res.content_type, log.call)
- assert_nothing_raised(bug2593) {range = res.content_range}
- assert_equal(100..199, range, log.call)
- assert_equal(this_data[100..199], res.body, log.call)
- }
-
- req = Net::HTTP::Get.new("/#{this_file}", "range"=>"bytes=0-0")
- http.request(req){|res|
- assert_equal("206", res.code, log.call)
- assert_equal("text/plain", res.content_type, log.call)
- assert_nothing_raised(bug2593) {range = res.content_range}
- assert_equal(0..0, range, log.call)
- assert_equal(this_data[0..0], res.body, log.call)
- }
-
- req = Net::HTTP::Get.new("/#{this_file}", "range"=>"bytes=-1")
- http.request(req){|res|
- assert_equal("206", res.code, log.call)
- assert_equal("text/plain", res.content_type, log.call)
- assert_nothing_raised(bug2593) {range = res.content_range}
- assert_equal((filesize-1)..(filesize-1), range, log.call)
- assert_equal(this_data[-1, 1], res.body, log.call)
- }
-
- req = Net::HTTP::Get.new("/#{this_file}", "range"=>"bytes=0-0, -2")
- http.request(req){|res|
- assert_equal("206", res.code, log.call)
- assert_equal("multipart/byteranges", res.content_type, log.call)
- }
- ensure
- server[:DocumentRootOptions].delete :NondisclosureName
- end
- end
- end
-
- def test_non_disclosure_name
- config = { :DocumentRoot => File.dirname(__FILE__), }
- log_tester = lambda {|log, access_log|
- log = log.reject {|s| /ERROR `.*\' not found\./ =~ s }
- log = log.reject {|s| /WARN the request refers nondisclosure name/ =~ s }
- assert_equal([], log)
- }
- this_file = File.basename(__FILE__)
- TestWEBrick.start_httpserver(config, log_tester) do |server, addr, port, log|
- http = Net::HTTP.new(addr, port)
- doc_root_opts = server[:DocumentRootOptions]
- doc_root_opts[:NondisclosureName] = %w(.ht* *~ test_*)
- req = Net::HTTP::Get.new("/")
- http.request(req){|res|
- assert_equal("200", res.code, log.call)
- assert_equal("text/html", res.content_type, log.call)
- assert_no_match(/HREF="#{File.basename(__FILE__)}"/, res.body)
- }
- req = Net::HTTP::Get.new("/#{this_file}")
- http.request(req){|res|
- assert_equal("404", res.code, log.call)
- }
- doc_root_opts[:NondisclosureName] = %w(.ht* *~ TEST_*)
- http.request(req){|res|
- assert_equal("404", res.code, log.call)
- }
- end
- end
-
- def test_directory_traversal
- return if File.executable?(__FILE__) # skip on strange file system
-
- config = { :DocumentRoot => File.dirname(__FILE__), }
- log_tester = lambda {|log, access_log|
- log = log.reject {|s| /ERROR bad URI/ =~ s }
- log = log.reject {|s| /ERROR `.*\' not found\./ =~ s }
- assert_equal([], log)
- }
- TestWEBrick.start_httpserver(config, log_tester) do |server, addr, port, log|
- http = Net::HTTP.new(addr, port)
- req = Net::HTTP::Get.new("/../../")
- http.request(req){|res| assert_equal("400", res.code, log.call) }
- req = Net::HTTP::Get.new("/..%5c../#{File.basename(__FILE__)}")
- http.request(req){|res| assert_equal(windows? ? "200" : "404", res.code, log.call) }
- req = Net::HTTP::Get.new("/..%5c..%5cruby.c")
- http.request(req){|res| assert_equal("404", res.code, log.call) }
- end
- end
-
- def test_unwise_in_path
- if windows?
- config = { :DocumentRoot => File.dirname(__FILE__), }
- TestWEBrick.start_httpserver(config) do |server, addr, port, log|
- http = Net::HTTP.new(addr, port)
- req = Net::HTTP::Get.new("/..%5c..")
- http.request(req){|res| assert_equal("301", res.code, log.call) }
- end
- end
- end
-
- def test_short_filename
- return if File.executable?(__FILE__) # skip on strange file system
-
- log_tester = lambda {|log, access_log|
- log = log.reject {|s| /ERROR `.*\' not found\./ =~ s }
- log = log.reject {|s| /WARN the request refers nondisclosure name/ =~ s }
- assert_equal([], log)
- }
- TestWEBrick.start_cgi_server({}, log_tester) do |server, addr, port, log|
- http = Net::HTTP.new(addr, port)
- if windows?
- root = File.dirname(__FILE__).tr("/", "\\")
- fname = IO.popen(%W[dir /x #{root}\\webrick_long_filename.cgi], encoding: "binary", &:read)
- fname.sub!(/\A.*$^$.*$^$/m, '')
- if fname
- fname = fname[/\s(w.+?cgi)\s/i, 1]
- fname.downcase!
- end
- else
- fname = "webric~1.cgi"
- end
- req = Net::HTTP::Get.new("/#{fname}/test")
- http.request(req) do |res|
- if windows?
- assert_equal("200", res.code, log.call)
- assert_equal("/test", res.body, log.call)
- else
- assert_equal("404", res.code, log.call)
- end
- end
-
- req = Net::HTTP::Get.new("/.htaccess")
- http.request(req) {|res| assert_equal("404", res.code, log.call) }
- req = Net::HTTP::Get.new("/htacce~1")
- http.request(req) {|res| assert_equal("404", res.code, log.call) }
- req = Net::HTTP::Get.new("/HTACCE~1")
- http.request(req) {|res| assert_equal("404", res.code, log.call) }
- end
- end
-
- def test_multibyte_char_in_path
- if Encoding.default_external == Encoding.find('US-ASCII')
- reset_encoding = true
- verb = $VERBOSE
- $VERBOSE = false
- Encoding.default_external = Encoding.find('UTF-8')
- end
-
- c = "\u00a7"
- begin
- c = c.encode('filesystem')
- rescue EncodingError
- c = c.b
- end
- Dir.mktmpdir(c) do |dir|
- basename = "#{c}.txt"
- File.write("#{dir}/#{basename}", "test_multibyte_char_in_path")
- Dir.mkdir("#{dir}/#{c}")
- File.write("#{dir}/#{c}/#{basename}", "nested")
- config = {
- :DocumentRoot => dir,
- :DirectoryIndex => [basename],
- }
- TestWEBrick.start_httpserver(config) do |server, addr, port, log|
- http = Net::HTTP.new(addr, port)
- path = "/#{basename}"
- req = Net::HTTP::Get.new(WEBrick::HTTPUtils::escape(path))
- http.request(req){|res| assert_equal("200", res.code, log.call + "\nFilesystem encoding is #{Encoding.find('filesystem')}") }
- path = "/#{c}/#{basename}"
- req = Net::HTTP::Get.new(WEBrick::HTTPUtils::escape(path))
- http.request(req){|res| assert_equal("200", res.code, log.call) }
- req = Net::HTTP::Get.new('/')
- http.request(req){|res|
- assert_equal("test_multibyte_char_in_path", res.body, log.call)
- }
- end
- end
- ensure
- if reset_encoding
- Encoding.default_external = Encoding.find('US-ASCII')
- $VERBOSE = verb
- end
- end
-
- def test_script_disclosure
- return if File.executable?(__FILE__) # skip on strange file system
-
- config = {
- :CGIInterpreter => TestWEBrick::RubyBinArray,
- :DocumentRoot => File.dirname(__FILE__),
- :CGIPathEnv => ENV['PATH'],
- :RequestCallback => Proc.new{|req, res|
- def req.meta_vars
- meta = super
- meta["RUBYLIB"] = $:.join(File::PATH_SEPARATOR)
- meta[RbConfig::CONFIG['LIBPATHENV']] = ENV[RbConfig::CONFIG['LIBPATHENV']] if RbConfig::CONFIG['LIBPATHENV']
- return meta
- end
- },
- }
- log_tester = lambda {|log, access_log|
- log = log.reject {|s| /ERROR `.*\' not found\./ =~ s }
- assert_equal([], log)
- }
- TestWEBrick.start_httpserver(config, log_tester) do |server, addr, port, log|
- http = Net::HTTP.new(addr, port)
- http.read_timeout = EnvUtil.apply_timeout_scale(60)
- http.write_timeout = EnvUtil.apply_timeout_scale(60) if http.respond_to?(:write_timeout=)
-
- req = Net::HTTP::Get.new("/webrick.cgi/test")
- http.request(req) do |res|
- assert_equal("200", res.code, log.call)
- assert_equal("/test", res.body, log.call)
- end
-
- resok = windows?
- response_assertion = Proc.new do |res|
- if resok
- assert_equal("200", res.code, log.call)
- assert_equal("/test", res.body, log.call)
- else
- assert_equal("404", res.code, log.call)
- end
- end
- req = Net::HTTP::Get.new("/webrick.cgi%20/test")
- http.request(req, &response_assertion)
- req = Net::HTTP::Get.new("/webrick.cgi./test")
- http.request(req, &response_assertion)
- resok &&= File.exist?(__FILE__+"::$DATA")
- req = Net::HTTP::Get.new("/webrick.cgi::$DATA/test")
- http.request(req, &response_assertion)
- end
- end
-
- def test_erbhandler
- config = { :DocumentRoot => File.dirname(__FILE__) }
- log_tester = lambda {|log, access_log|
- log = log.reject {|s| /ERROR `.*\' not found\./ =~ s }
- assert_equal([], log)
- }
- TestWEBrick.start_httpserver(config, log_tester) do |server, addr, port, log|
- http = Net::HTTP.new(addr, port)
- req = Net::HTTP::Get.new("/webrick.rhtml")
- http.request(req) do |res|
- assert_equal("200", res.code, log.call)
- assert_match %r!\Areq to http://[^/]+/webrick\.rhtml {}\n!, res.body
- end
- end
- end
-end
diff --git a/tool/test/webrick/test_htgroup.rb b/tool/test/webrick/test_htgroup.rb
deleted file mode 100644
index 8749711df5f62b..00000000000000
--- a/tool/test/webrick/test_htgroup.rb
+++ /dev/null
@@ -1,19 +0,0 @@
-require "tempfile"
-require "test/unit"
-require "webrick/httpauth/htgroup"
-
-class TestHtgroup < Test::Unit::TestCase
- def test_htgroup
- Tempfile.create('test_htgroup') do |tmpfile|
- tmpfile.close
- tmp_group = WEBrick::HTTPAuth::Htgroup.new(tmpfile.path)
- tmp_group.add 'superheroes', %w[spiderman batman]
- tmp_group.add 'supervillains', %w[joker]
- tmp_group.flush
-
- htgroup = WEBrick::HTTPAuth::Htgroup.new(tmpfile.path)
- assert_equal(htgroup.members('superheroes'), %w[spiderman batman])
- assert_equal(htgroup.members('supervillains'), %w[joker])
- end
- end
-end
diff --git a/tool/test/webrick/test_htmlutils.rb b/tool/test/webrick/test_htmlutils.rb
deleted file mode 100644
index ae1b8efa954a58..00000000000000
--- a/tool/test/webrick/test_htmlutils.rb
+++ /dev/null
@@ -1,21 +0,0 @@
-# frozen_string_literal: false
-require "test/unit"
-require "webrick/htmlutils"
-
-class TestWEBrickHTMLUtils < Test::Unit::TestCase
- include WEBrick::HTMLUtils
-
- def test_escape
- assert_equal("foo", escape("foo"))
- assert_equal("foo bar", escape("foo bar"))
- assert_equal("foo&bar", escape("foo&bar"))
- assert_equal("foo"bar", escape("foo\"bar"))
- assert_equal("foo>bar", escape("foo>bar"))
- assert_equal("foo<bar", escape("foo