wreq 1.2.1-aarch64-linux → 1.2.3-aarch64-linux
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/Gemfile +1 -0
- data/README.md +7 -4
- data/docs/windows-gnu-tokio-crash.md +70 -0
- data/lib/wreq_ruby/3.3/wreq_ruby.so +0 -0
- data/lib/wreq_ruby/3.4/wreq_ruby.so +0 -0
- data/lib/wreq_ruby/4.0/wreq_ruby.so +0 -0
- data/lib/wreq_ruby/emulate.rb +1 -0
- data/script/build_windows_gnu.ps1 +257 -0
- data/test/client_cookie_test.rb +6 -6
- data/test/client_test.rb +16 -16
- data/test/cookie_test.rb +8 -6
- data/test/error_handling_test.rb +4 -4
- data/test/header_test.rb +1 -1
- data/test/inspect_test.rb +4 -4
- data/test/module_methods_test.rb +11 -11
- data/test/request_parameters_test.rb +30 -30
- data/test/request_test.rb +22 -22
- data/test/response_test.rb +6 -6
- data/test/stream_test.rb +61 -35
- data/test/test_helper.rb +46 -3
- metadata +4 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: a47ab39ac373c828bb5dfe512749ece2faa56629c8db42e38bf18a22ce15d515
|
|
4
|
+
data.tar.gz: fb70422e9029953e2a53974f03bb398eac6f9b3a1453f47cd8d4bc6aec6ba40f
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: ccba25e28e305ce675f56dc8c3cf86e7433905e4029780cbe604e7e7ccfc129160ae6ab0955704a764f54dd2604efd29bfb88fcd067b7ff28fc3af3240edcdc5
|
|
7
|
+
data.tar.gz: 3d38f4979c3d2f761a287a3f64a69efbcc19256e825093d4ba59080966c540d7cf096a5b4af2e49920dce2e103ff4570b0db80e82d42d192e7e6abffa9548f73
|
data/Gemfile
CHANGED
|
@@ -8,6 +8,7 @@ group :development, :test do
|
|
|
8
8
|
gem "rb_sys", "~> 0.9.110" # for Makefile generation in extconf.rb
|
|
9
9
|
gem "rake-compiler", "~> 1.2.9" # to build a debug build
|
|
10
10
|
gem "minitest", "~> 6.0" # test library
|
|
11
|
+
gem "minitest-retry", "~> 0.3", require: false # retry flaky network tests
|
|
11
12
|
gem "activesupport", "~> 8.0.1" # testing support
|
|
12
13
|
gem "standard", "~> 1.52" # linter with pre-specified rules
|
|
13
14
|
gem "redcarpet", "~> 3.6" # for documentation markdown parsing
|
data/README.md
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
# wreq-ruby
|
|
2
2
|
|
|
3
3
|
[](https://github.com/SearchApi/wreq-ruby/actions/workflows/ci.yml)
|
|
4
|
+

|
|
4
5
|
[](https://rubygems.org/gems/wreq)
|
|
5
6
|
|
|
6
7
|
An easy and powerful Ruby HTTP client with advanced browser fingerprinting that accurately emulates **Chrome**, **Edge**, **Firefox**, **Safari**, **Opera**, and **OkHttp**, with precise **TLS/HTTP2** signatures, and powered by [wreq](https://github.com/0x676e67/wreq).
|
|
@@ -31,8 +32,10 @@ gem "wreq"
|
|
|
31
32
|
```
|
|
32
33
|
|
|
33
34
|
Pre-compiled native gems are available for:
|
|
35
|
+
|
|
34
36
|
- Linux (x86_64, aarch64)
|
|
35
37
|
- macOS (arm64)
|
|
38
|
+
- Windows (x86_64)
|
|
36
39
|
|
|
37
40
|
## Example
|
|
38
41
|
|
|
@@ -88,11 +91,11 @@ Most browser device models share identical TLS and HTTP/2 configurations, differ
|
|
|
88
91
|
|
|
89
92
|
| **Browser** | **Versions** |
|
|
90
93
|
| ----------- | ------------ |
|
|
91
|
-
| **Chrome** | `Chrome100`, `Chrome101`, `Chrome104`, `Chrome105`, `Chrome106`, `Chrome107`, `Chrome108`, `Chrome109`, `Chrome110`, `Chrome114`, `Chrome116`, `Chrome117`, `Chrome118`, `Chrome119`, `Chrome120`, `Chrome123`, `Chrome124`, `Chrome126`, `Chrome127`, `Chrome128`, `Chrome129`, `Chrome130`, `Chrome131`, `Chrome132`, `Chrome133`, `Chrome134`, `Chrome135`, `Chrome136`, `Chrome137`, `Chrome138`, `Chrome139`, `Chrome140`, `Chrome141`, `Chrome142`, `Chrome143`, `Chrome144`, `Chrome145`, `Chrome146`, `Chrome147` |
|
|
92
|
-
| **Edge** | `Edge101`, `Edge122`, `Edge127`, `Edge131`, `Edge134`, `Edge135`, `Edge136`, `Edge137`, `Edge138`, `Edge139`, `Edge140`, `Edge141`, `Edge142`, `Edge143`, `Edge144`, `Edge145`, `Edge146`, `Edge147` |
|
|
93
|
-
| **Firefox** | `Firefox109`, `Firefox117`, `Firefox128`, `Firefox133`, `Firefox135`, `FirefoxPrivate135`, `FirefoxAndroid135`, `Firefox136`, `FirefoxPrivate136`, `Firefox139`, `Firefox142`, `Firefox143`, `Firefox144`, `Firefox145`, `Firefox146`, `Firefox147`, `Firefox148`, `Firefox149` |
|
|
94
|
+
| **Chrome** | `Chrome100`, `Chrome101`, `Chrome104`, `Chrome105`, `Chrome106`, `Chrome107`, `Chrome108`, `Chrome109`, `Chrome110`, `Chrome114`, `Chrome116`, `Chrome117`, `Chrome118`, `Chrome119`, `Chrome120`, `Chrome123`, `Chrome124`, `Chrome126`, `Chrome127`, `Chrome128`, `Chrome129`, `Chrome130`, `Chrome131`, `Chrome132`, `Chrome133`, `Chrome134`, `Chrome135`, `Chrome136`, `Chrome137`, `Chrome138`, `Chrome139`, `Chrome140`, `Chrome141`, `Chrome142`, `Chrome143`, `Chrome144`, `Chrome145`, `Chrome146`, `Chrome147`, `Chrome148`, `Chrome149` |
|
|
95
|
+
| **Edge** | `Edge101`, `Edge122`, `Edge127`, `Edge131`, `Edge134`, `Edge135`, `Edge136`, `Edge137`, `Edge138`, `Edge139`, `Edge140`, `Edge141`, `Edge142`, `Edge143`, `Edge144`, `Edge145`, `Edge146`, `Edge147`, `Edge148` |
|
|
96
|
+
| **Firefox** | `Firefox109`, `Firefox117`, `Firefox128`, `Firefox133`, `Firefox135`, `FirefoxPrivate135`, `FirefoxAndroid135`, `Firefox136`, `FirefoxPrivate136`, `Firefox139`, `Firefox142`, `Firefox143`, `Firefox144`, `Firefox145`, `Firefox146`, `Firefox147`, `Firefox148`, `Firefox149`, `Firefox150`, `Firefox151` |
|
|
94
97
|
| **Safari** | `Safari15_3`, `Safari15_5`, `Safari15_6_1`, `Safari16`, `Safari16_5`, `Safari17_0`, `Safari17_2_1`, `Safari17_4_1`, `Safari17_5`, `Safari17_6`, `Safari18`, `Safari18_2`, `Safari18_3`, `Safari18_3_1`, `Safari18_5`, `Safari26`, `Safari26_1`, `Safari26_2`, `SafariIos16_5`, `SafariIos17_2`, `SafariIos17_4_1`, `SafariIos18_1_1`, `SafariIos26`, `SafariIos26_2`, `SafariIPad18`, `SafariIPad26`, `SafariIpad26_2` |
|
|
95
|
-
| **OkHttp** | `
|
|
98
|
+
| **OkHttp** | `Opera116`, `Opera117`, `Opera118`, `Opera119`, `Opera120`, `Opera121`, `Opera122`, `Opera123`, `Opera124`, `Opera125`, `Opera126`, `Opera127`, `Opera128`, `Opera129`, `Opera130` |
|
|
96
99
|
| **Opera** | `Opera116`, `Opera117`, `Opera118`, `Opera119` |
|
|
97
100
|
|
|
98
101
|
</details>
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
# Windows GNU Tokio multi-thread crash
|
|
2
|
+
|
|
3
|
+
## Summary
|
|
4
|
+
|
|
5
|
+
On RubyInstaller GNU/UCRT, Tokio's multi-thread runtime could crash when the
|
|
6
|
+
native extension was loaded by Ruby. The issue was not Tokio's scheduler logic
|
|
7
|
+
itself. It came from Windows symbol resolution while linking the Ruby extension.
|
|
8
|
+
|
|
9
|
+
## Root cause
|
|
10
|
+
|
|
11
|
+
RubyInstaller GNU/UCRT exports a Ruby-aware `Sleep` symbol from
|
|
12
|
+
`x64-ucrt-ruby400.dll`.
|
|
13
|
+
|
|
14
|
+
When the extension is linked with GNU ld, an unqualified `Sleep` reference can
|
|
15
|
+
bind to Ruby's `Sleep` instead of `KERNEL32!Sleep`. Tokio's multi-thread runtime
|
|
16
|
+
creates native worker threads that are not Ruby-managed threads. If one of those
|
|
17
|
+
threads calls Ruby's `Sleep`, Ruby expects Ruby thread-local state to exist, but
|
|
18
|
+
that TLS slot is empty on the Tokio worker thread. This can crash with an access
|
|
19
|
+
violation or Ruby VM bug report.
|
|
20
|
+
|
|
21
|
+
The local investigation found the fault inside:
|
|
22
|
+
|
|
23
|
+
```text
|
|
24
|
+
x64-ucrt-ruby400.dll!Sleep + 0x40
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
The import table also showed the bad binding:
|
|
28
|
+
|
|
29
|
+
```text
|
|
30
|
+
DLL Name: x64-ucrt-ruby400.dll
|
|
31
|
+
Sleep
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
## Fix
|
|
35
|
+
|
|
36
|
+
For `x86_64-pc-windows-gnu`, the build now wraps `Sleep` at link time:
|
|
37
|
+
|
|
38
|
+
```text
|
|
39
|
+
-Wl,--wrap=Sleep
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
The wrapper lives in `src/arch.rs` and forwards calls to `KERNEL32!SleepEx`.
|
|
43
|
+
This keeps Tokio's worker threads on the real Windows API path while preserving
|
|
44
|
+
Tokio's multi-thread runtime and Ruby GVL release behavior.
|
|
45
|
+
|
|
46
|
+
After the fix, the import table shows:
|
|
47
|
+
|
|
48
|
+
```text
|
|
49
|
+
DLL Name: KERNEL32.dll
|
|
50
|
+
Sleep
|
|
51
|
+
SleepEx
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
## Notes
|
|
55
|
+
|
|
56
|
+
`windows-sys` raw-dylib was also tested. It is not enough on its own because the
|
|
57
|
+
problematic `Sleep` reference can come from Rust std, MinGW, or pthread-related
|
|
58
|
+
linking paths, not only from `windows-sys`.
|
|
59
|
+
|
|
60
|
+
Verified with:
|
|
61
|
+
|
|
62
|
+
```powershell
|
|
63
|
+
.\script\build_windows_gnu.ps1 -SkipBundleInstall -SkipToolInstall -RunTests
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
Result:
|
|
67
|
+
|
|
68
|
+
```text
|
|
69
|
+
160 runs, 775 assertions, 0 failures, 0 errors
|
|
70
|
+
```
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
data/lib/wreq_ruby/emulate.rb
CHANGED
|
@@ -0,0 +1,257 @@
|
|
|
1
|
+
param(
|
|
2
|
+
[switch]$SkipBundleInstall,
|
|
3
|
+
[switch]$SkipToolInstall,
|
|
4
|
+
[switch]$BuildGem,
|
|
5
|
+
[switch]$TestGem,
|
|
6
|
+
[switch]$RunTests,
|
|
7
|
+
[string]$SmokeUrl = $env:WREQ_SMOKE_URL
|
|
8
|
+
)
|
|
9
|
+
|
|
10
|
+
$ErrorActionPreference = "Stop"
|
|
11
|
+
|
|
12
|
+
$target = "x86_64-pc-windows-gnu"
|
|
13
|
+
$platform = "x64-mingw-ucrt"
|
|
14
|
+
$gemPattern = "pkg\wreq-*-$platform.gem"
|
|
15
|
+
if ([string]::IsNullOrWhiteSpace($SmokeUrl)) {
|
|
16
|
+
$SmokeUrl = "https://httpbin.io/get"
|
|
17
|
+
}
|
|
18
|
+
$rubyRoot = Split-Path (Split-Path (Get-Command ruby).Source -Parent) -Parent
|
|
19
|
+
$ucrt = Join-Path $rubyRoot "msys64\ucrt64"
|
|
20
|
+
$ucrtBin = Join-Path $ucrt "bin"
|
|
21
|
+
$msysPackages = @(
|
|
22
|
+
"mingw-w64-ucrt-x86_64-gcc",
|
|
23
|
+
"mingw-w64-ucrt-x86_64-clang",
|
|
24
|
+
"mingw-w64-ucrt-x86_64-cmake",
|
|
25
|
+
"mingw-w64-ucrt-x86_64-pkgconf"
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
function Invoke-Step {
|
|
29
|
+
param(
|
|
30
|
+
[string]$Name,
|
|
31
|
+
[scriptblock]$Command
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
Write-Host "==> $Name"
|
|
35
|
+
& $Command
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function Get-MissingUcrtTools {
|
|
39
|
+
$missing = @()
|
|
40
|
+
|
|
41
|
+
if (-not (Test-Path (Join-Path $ucrtBin "gcc.exe")) -or -not (Test-Path (Join-Path $ucrtBin "g++.exe"))) {
|
|
42
|
+
$missing += "gcc/g++"
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
if (-not (Test-Path (Join-Path $ucrtBin "clang.exe")) -or -not (Test-Path (Join-Path $ucrtBin "libclang.dll"))) {
|
|
46
|
+
$missing += "clang/libclang"
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
if (-not (Test-Path (Join-Path $ucrtBin "cmake.exe"))) {
|
|
50
|
+
$missing += "cmake"
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
if (-not (Test-Path (Join-Path $ucrtBin "pkgconf.exe")) -and -not (Test-Path (Join-Path $ucrtBin "pkg-config.exe"))) {
|
|
54
|
+
$missing += "pkgconf"
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
$missing
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function Get-LatestPlatformGem {
|
|
61
|
+
$gem = Get-ChildItem -Path $gemPattern -ErrorAction SilentlyContinue |
|
|
62
|
+
Sort-Object LastWriteTime -Descending |
|
|
63
|
+
Select-Object -First 1
|
|
64
|
+
|
|
65
|
+
if (-not $gem) {
|
|
66
|
+
throw "No Windows GNU platform gem found at '$gemPattern'. Re-run with -BuildGem first."
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
$gem
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function Invoke-RubySmoke {
|
|
73
|
+
param(
|
|
74
|
+
[string]$Name,
|
|
75
|
+
[string]$Code
|
|
76
|
+
)
|
|
77
|
+
|
|
78
|
+
Write-Host "Smoke: $Name"
|
|
79
|
+
ruby -e $Code
|
|
80
|
+
if ($LASTEXITCODE -ne 0) {
|
|
81
|
+
throw "$Name failed with exit code $LASTEXITCODE."
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
Invoke-Step "Check Ruby platform" {
|
|
86
|
+
$rubyArch = & ruby -rrbconfig -e "print RbConfig::CONFIG['arch']"
|
|
87
|
+
if ($rubyArch -ne $platform) {
|
|
88
|
+
throw "Expected Ruby platform '$platform', got '$rubyArch'. Use RubyInstaller UCRT for this build."
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
ruby -v
|
|
92
|
+
ruby -rrbconfig -rrubygems -e "puts RbConfig::CONFIG.values_at('arch', 'host', 'CC').join(%q{ }); puts Gem::Platform.local"
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
Invoke-Step "Install Rust target" {
|
|
96
|
+
$installedTargets = @(rustup target list --installed)
|
|
97
|
+
if ($installedTargets -contains $target) {
|
|
98
|
+
Write-Host "Rust target $target already installed; skipping rustup."
|
|
99
|
+
return
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
rustup target add $target
|
|
103
|
+
if ($LASTEXITCODE -ne 0) {
|
|
104
|
+
throw "Failed to install Rust target $target."
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
Invoke-Step "Check MSYS2 UCRT build tools" {
|
|
109
|
+
$missing = @(Get-MissingUcrtTools)
|
|
110
|
+
if ($missing.Count -eq 0) {
|
|
111
|
+
Write-Host "MSYS2 UCRT build tools already present; skipping pacman."
|
|
112
|
+
return
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
if ($SkipToolInstall) {
|
|
116
|
+
throw "Missing MSYS2 UCRT build tools: $($missing -join ', '). Re-run without -SkipToolInstall or install them with ridk."
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
Write-Host "Missing MSYS2 UCRT build tools: $($missing -join ', ')"
|
|
120
|
+
ridk exec pacman -S --needed --noconfirm @msysPackages
|
|
121
|
+
if ($LASTEXITCODE -ne 0) {
|
|
122
|
+
$stillMissing = @(Get-MissingUcrtTools)
|
|
123
|
+
if ($stillMissing.Count -eq 0) {
|
|
124
|
+
Write-Warning "pacman returned exit code $LASTEXITCODE, but all required tools are present; continuing."
|
|
125
|
+
return
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
throw "Failed to install MSYS2 UCRT build tools. Missing: $($stillMissing -join ', '). If pacman cannot lock its database, close other pacman/ridk shells, run PowerShell as Administrator, or install RubyInstaller in a user-writable directory."
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
Invoke-Step "Configure Windows GNU toolchain" {
|
|
133
|
+
$env:CARGO_BUILD_TARGET = $target
|
|
134
|
+
$env:LIBCLANG_PATH = $ucrtBin
|
|
135
|
+
$env:CC_x86_64_pc_windows_gnu = Join-Path $ucrtBin "gcc.exe"
|
|
136
|
+
$env:CXX_x86_64_pc_windows_gnu = Join-Path $ucrtBin "g++.exe"
|
|
137
|
+
$env:CMAKE = Join-Path $ucrtBin "cmake.exe"
|
|
138
|
+
Remove-Item Env:\CMAKE_GENERATOR -ErrorAction SilentlyContinue
|
|
139
|
+
|
|
140
|
+
rustc -Vv
|
|
141
|
+
Write-Host "LIBCLANG_PATH=$env:LIBCLANG_PATH"
|
|
142
|
+
Write-Host "CC_x86_64_pc_windows_gnu=$env:CC_x86_64_pc_windows_gnu"
|
|
143
|
+
Write-Host "CXX_x86_64_pc_windows_gnu=$env:CXX_x86_64_pc_windows_gnu"
|
|
144
|
+
Write-Host "CMAKE=$env:CMAKE"
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
if (-not $SkipBundleInstall) {
|
|
148
|
+
Invoke-Step "Install Ruby dependencies" {
|
|
149
|
+
bundle install
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
Invoke-Step "Compile native extension" {
|
|
154
|
+
ridk exec ruby -S bundle exec rake compile
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
Invoke-Step "Verify extension loads" {
|
|
158
|
+
ruby -Ilib -rwreq -e "puts Wreq::VERSION; puts Wreq::Client.new.class"
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
if ($RunTests) {
|
|
162
|
+
Invoke-Step "Run tests" {
|
|
163
|
+
ridk exec ruby -S bundle exec rake test
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
if ($BuildGem) {
|
|
168
|
+
Invoke-Step "Build local platform gem" {
|
|
169
|
+
$rubyMinor = & ruby -e "print RUBY_VERSION[/\A\d+\.\d+/]"
|
|
170
|
+
$dest = "lib\wreq_ruby\$rubyMinor"
|
|
171
|
+
New-Item -ItemType Directory -Force $dest | Out-Null
|
|
172
|
+
Copy-Item "lib\wreq_ruby\wreq_ruby.so" (Join-Path $dest "wreq_ruby.so") -Force
|
|
173
|
+
|
|
174
|
+
ruby script/build_platform_gem.rb $platform
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
if ($TestGem) {
|
|
179
|
+
Invoke-Step "Install and smoke test platform gem" {
|
|
180
|
+
$gem = Get-LatestPlatformGem
|
|
181
|
+
$gemHome = Join-Path ([System.IO.Path]::GetTempPath()) "wreq-gem-test-$PID"
|
|
182
|
+
$gemBin = Join-Path $gemHome "bin"
|
|
183
|
+
$testDir = Join-Path ([System.IO.Path]::GetTempPath()) "wreq-gem-test-cwd-$PID"
|
|
184
|
+
|
|
185
|
+
Remove-Item -Recurse -Force $gemHome, $testDir -ErrorAction SilentlyContinue
|
|
186
|
+
New-Item -ItemType Directory -Force $gemHome, $gemBin, $testDir | Out-Null
|
|
187
|
+
|
|
188
|
+
$oldGemHome = $env:GEM_HOME
|
|
189
|
+
$oldGemPath = $env:GEM_PATH
|
|
190
|
+
$oldGemrc = $env:GEMRC
|
|
191
|
+
$oldRubyopt = $env:RUBYOPT
|
|
192
|
+
$oldRubylib = $env:RUBYLIB
|
|
193
|
+
$oldBundleGemfile = $env:BUNDLE_GEMFILE
|
|
194
|
+
$oldBundleBinPath = $env:BUNDLE_BIN_PATH
|
|
195
|
+
$oldBundlerVersion = $env:BUNDLER_VERSION
|
|
196
|
+
$oldSmokeUrl = $env:WREQ_SMOKE_URL
|
|
197
|
+
$oldWreqGemHome = $env:WREQ_GEM_HOME
|
|
198
|
+
|
|
199
|
+
try {
|
|
200
|
+
$env:GEM_HOME = $gemHome
|
|
201
|
+
$env:GEM_PATH = $gemHome
|
|
202
|
+
$env:GEMRC = ""
|
|
203
|
+
$env:RUBYOPT = ""
|
|
204
|
+
$env:RUBYLIB = ""
|
|
205
|
+
$env:BUNDLE_GEMFILE = ""
|
|
206
|
+
$env:BUNDLE_BIN_PATH = ""
|
|
207
|
+
$env:BUNDLER_VERSION = ""
|
|
208
|
+
$env:WREQ_SMOKE_URL = $SmokeUrl
|
|
209
|
+
$env:WREQ_GEM_HOME = $gemHome
|
|
210
|
+
|
|
211
|
+
gem install --norc --local --install-dir $gemHome --bindir $gemBin --no-document --force --no-user-install $gem.FullName
|
|
212
|
+
if ($LASTEXITCODE -ne 0) {
|
|
213
|
+
throw "Failed to install $($gem.FullName)."
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
Push-Location $testDir
|
|
217
|
+
try {
|
|
218
|
+
Invoke-RubySmoke "Load installed platform gem" @'
|
|
219
|
+
STDOUT.sync = true
|
|
220
|
+
STDERR.sync = true
|
|
221
|
+
require "wreq"
|
|
222
|
+
spec = Gem.loaded_specs.fetch("wreq")
|
|
223
|
+
expected = File.expand_path(ENV.fetch("WREQ_GEM_HOME"))
|
|
224
|
+
actual = File.expand_path(spec.full_gem_path)
|
|
225
|
+
raise "loaded #{actual}, expected under #{expected}" unless actual.start_with?(expected)
|
|
226
|
+
puts "loaded #{spec.full_name}"
|
|
227
|
+
puts actual
|
|
228
|
+
puts "wreq #{Wreq::VERSION}"
|
|
229
|
+
'@
|
|
230
|
+
|
|
231
|
+
Invoke-RubySmoke "Request through installed platform gem" @'
|
|
232
|
+
STDOUT.sync = true
|
|
233
|
+
STDERR.sync = true
|
|
234
|
+
require "wreq"
|
|
235
|
+
client = Wreq::Client.new
|
|
236
|
+
resp = client.get(ENV.fetch("WREQ_SMOKE_URL"), timeout: 20)
|
|
237
|
+
puts "HTTP #{resp.code}"
|
|
238
|
+
raise "unexpected status #{resp.code}" unless resp.code == 200
|
|
239
|
+
'@
|
|
240
|
+
} finally {
|
|
241
|
+
Pop-Location
|
|
242
|
+
}
|
|
243
|
+
} finally {
|
|
244
|
+
$env:GEM_HOME = $oldGemHome
|
|
245
|
+
$env:GEM_PATH = $oldGemPath
|
|
246
|
+
$env:GEMRC = $oldGemrc
|
|
247
|
+
$env:RUBYOPT = $oldRubyopt
|
|
248
|
+
$env:RUBYLIB = $oldRubylib
|
|
249
|
+
$env:BUNDLE_GEMFILE = $oldBundleGemfile
|
|
250
|
+
$env:BUNDLE_BIN_PATH = $oldBundleBinPath
|
|
251
|
+
$env:BUNDLER_VERSION = $oldBundlerVersion
|
|
252
|
+
$env:WREQ_SMOKE_URL = $oldSmokeUrl
|
|
253
|
+
$env:WREQ_GEM_HOME = $oldWreqGemHome
|
|
254
|
+
Remove-Item -Recurse -Force $gemHome, $testDir -ErrorAction SilentlyContinue
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
}
|
data/test/client_cookie_test.rb
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
require "test_helper"
|
|
4
4
|
|
|
5
5
|
class ClientCookieProviderTest < Minitest::Test
|
|
6
|
-
HOST =
|
|
6
|
+
HOST = HTTPBIN_URL
|
|
7
7
|
|
|
8
8
|
def setup
|
|
9
9
|
@jar = Wreq::Jar.new
|
|
@@ -29,9 +29,9 @@ class ClientCookieProviderTest < Minitest::Test
|
|
|
29
29
|
# subsequent request should send the stored cookie automatically
|
|
30
30
|
res2 = @client.get("#{HOST}/cookies")
|
|
31
31
|
assert_equal 200, res2.code
|
|
32
|
-
|
|
33
|
-
assert_kind_of Hash,
|
|
34
|
-
assert_equal "bar",
|
|
32
|
+
cookies = httpbin_cookies(res2.json)
|
|
33
|
+
assert_kind_of Hash, cookies
|
|
34
|
+
assert_equal "bar", httpbin_fetch(cookies, "foo")
|
|
35
35
|
end
|
|
36
36
|
|
|
37
37
|
def test_prepopulated_jar_is_used_by_client
|
|
@@ -40,7 +40,7 @@ class ClientCookieProviderTest < Minitest::Test
|
|
|
40
40
|
|
|
41
41
|
res = @client.get("#{HOST}/cookies")
|
|
42
42
|
assert_equal 200, res.code
|
|
43
|
-
cookies = res.json
|
|
44
|
-
assert_equal "1", cookies
|
|
43
|
+
cookies = httpbin_cookies(res.json)
|
|
44
|
+
assert_equal "1", httpbin_fetch(cookies, "pref")
|
|
45
45
|
end
|
|
46
46
|
end
|
data/test/client_test.rb
CHANGED
|
@@ -20,40 +20,40 @@ class ClientTest < Minitest::Test
|
|
|
20
20
|
end
|
|
21
21
|
|
|
22
22
|
def test_get_request
|
|
23
|
-
response = @client.get("
|
|
23
|
+
response = @client.get("#{HTTPBIN_URL}/get")
|
|
24
24
|
refute_nil response
|
|
25
25
|
assert_equal 200, response.code
|
|
26
26
|
end
|
|
27
27
|
|
|
28
28
|
def test_post_request
|
|
29
|
-
response = @client.post("
|
|
29
|
+
response = @client.post("#{HTTPBIN_URL}/post",
|
|
30
30
|
json: {test: "wreq-ruby"})
|
|
31
31
|
refute_nil response
|
|
32
32
|
assert_equal 200, response.code
|
|
33
33
|
end
|
|
34
34
|
|
|
35
35
|
def test_put_request
|
|
36
|
-
response = @client.put("
|
|
36
|
+
response = @client.put("#{HTTPBIN_URL}/put",
|
|
37
37
|
json: {data: "test"})
|
|
38
38
|
refute_nil response
|
|
39
39
|
assert_equal 200, response.code
|
|
40
40
|
end
|
|
41
41
|
|
|
42
42
|
def test_delete_request
|
|
43
|
-
response = @client.delete("
|
|
43
|
+
response = @client.delete("#{HTTPBIN_URL}/delete")
|
|
44
44
|
refute_nil response
|
|
45
45
|
assert_equal 200, response.code
|
|
46
46
|
end
|
|
47
47
|
|
|
48
48
|
def test_patch_request
|
|
49
|
-
response = @client.patch("
|
|
49
|
+
response = @client.patch("#{HTTPBIN_URL}/patch",
|
|
50
50
|
json: {update: "field"})
|
|
51
51
|
refute_nil response
|
|
52
52
|
assert_equal 200, response.code
|
|
53
53
|
end
|
|
54
54
|
|
|
55
55
|
def test_request_with_query_params
|
|
56
|
-
response = @client.get("
|
|
56
|
+
response = @client.get("#{HTTPBIN_URL}/get",
|
|
57
57
|
query: {"param" => "value"})
|
|
58
58
|
refute_nil response
|
|
59
59
|
assert_equal 200, response.code
|
|
@@ -61,7 +61,7 @@ class ClientTest < Minitest::Test
|
|
|
61
61
|
end
|
|
62
62
|
|
|
63
63
|
def test_request_with_form_data
|
|
64
|
-
response = @client.post("
|
|
64
|
+
response = @client.post("#{HTTPBIN_URL}/post",
|
|
65
65
|
form: {"field" => "value"})
|
|
66
66
|
refute_nil response
|
|
67
67
|
assert_equal 200, response.code
|
|
@@ -69,7 +69,7 @@ class ClientTest < Minitest::Test
|
|
|
69
69
|
end
|
|
70
70
|
|
|
71
71
|
def test_request_with_raw_body
|
|
72
|
-
response = @client.post("
|
|
72
|
+
response = @client.post("#{HTTPBIN_URL}/post",
|
|
73
73
|
body: "raw content",
|
|
74
74
|
headers: {"Content-Type" => "text/plain"})
|
|
75
75
|
refute_nil response
|
|
@@ -78,14 +78,14 @@ class ClientTest < Minitest::Test
|
|
|
78
78
|
end
|
|
79
79
|
|
|
80
80
|
def test_basic_authentication
|
|
81
|
-
response = @client.get("
|
|
81
|
+
response = @client.get("#{HTTPBIN_URL}/basic-auth/user/pass",
|
|
82
82
|
basic_auth: ["user", "pass"])
|
|
83
83
|
refute_nil response
|
|
84
84
|
assert_equal 200, response.code
|
|
85
85
|
end
|
|
86
86
|
|
|
87
87
|
def test_bearer_authentication
|
|
88
|
-
response = @client.get("
|
|
88
|
+
response = @client.get("#{HTTPBIN_URL}/bearer",
|
|
89
89
|
bearer_auth: "test-token")
|
|
90
90
|
refute_nil response
|
|
91
91
|
assert_equal 200, response.code
|
|
@@ -93,21 +93,21 @@ class ClientTest < Minitest::Test
|
|
|
93
93
|
end
|
|
94
94
|
|
|
95
95
|
def test_redirect_following
|
|
96
|
-
response = @client.get("
|
|
96
|
+
response = @client.get("#{HTTPBIN_URL}/redirect/1",
|
|
97
97
|
allow_redirects: true)
|
|
98
98
|
refute_nil response
|
|
99
99
|
assert_equal 200, response.code
|
|
100
100
|
end
|
|
101
101
|
|
|
102
102
|
def test_redirect_blocking
|
|
103
|
-
response = @client.get("
|
|
103
|
+
response = @client.get("#{HTTPBIN_URL}/redirect/1",
|
|
104
104
|
allow_redirects: false)
|
|
105
105
|
refute_nil response
|
|
106
106
|
assert_equal 302, response.code
|
|
107
107
|
end
|
|
108
108
|
|
|
109
109
|
def test_gzip_compression
|
|
110
|
-
response = @client.get("
|
|
110
|
+
response = @client.get("#{HTTPBIN_URL}/gzip", gzip: true)
|
|
111
111
|
refute_nil response
|
|
112
112
|
assert_equal 200, response.code
|
|
113
113
|
end
|
|
@@ -115,12 +115,12 @@ class ClientTest < Minitest::Test
|
|
|
115
115
|
def test_timeout_handling
|
|
116
116
|
# Test that short timeouts properly raise exceptions
|
|
117
117
|
assert_raises(Wreq::TimeoutError) do
|
|
118
|
-
@client.get("
|
|
118
|
+
@client.get("#{HTTPBIN_URL}/delay/10", timeout: 1)
|
|
119
119
|
end
|
|
120
120
|
|
|
121
121
|
# Test that reasonable timeouts work normally
|
|
122
122
|
start_time = Time.now
|
|
123
|
-
response = @client.get("
|
|
123
|
+
response = @client.get("#{HTTPBIN_URL}/delay/1", timeout: 5)
|
|
124
124
|
elapsed = Time.now - start_time
|
|
125
125
|
|
|
126
126
|
refute_nil response
|
|
@@ -129,7 +129,7 @@ class ClientTest < Minitest::Test
|
|
|
129
129
|
end
|
|
130
130
|
|
|
131
131
|
def test_status_codes
|
|
132
|
-
response = @client.get("
|
|
132
|
+
response = @client.get("#{HTTPBIN_URL}/status/404")
|
|
133
133
|
refute_nil response
|
|
134
134
|
assert_equal 404, response.code
|
|
135
135
|
end
|
data/test/cookie_test.rb
CHANGED
|
@@ -157,24 +157,26 @@ class CookieTest < Minitest::Test
|
|
|
157
157
|
def test_request_uncompressed_cookies
|
|
158
158
|
client = Wreq::Client.new
|
|
159
159
|
resp = client.get(
|
|
160
|
-
"
|
|
160
|
+
"#{HTTPBIN_URL}/cookies",
|
|
161
161
|
cookies: {"foo" => "bar", "baz" => "qux"}
|
|
162
162
|
)
|
|
163
163
|
json = resp.json
|
|
164
164
|
assert_instance_of Hash, json
|
|
165
|
-
|
|
166
|
-
assert_equal "
|
|
165
|
+
cookies = json.fetch("cookies", json)
|
|
166
|
+
assert_equal "bar", cookies["foo"]
|
|
167
|
+
assert_equal "qux", cookies["baz"]
|
|
167
168
|
end
|
|
168
169
|
|
|
169
170
|
def test_request_compressed_cookies
|
|
170
171
|
client = Wreq::Client.new
|
|
171
172
|
resp = client.get(
|
|
172
|
-
"
|
|
173
|
+
"#{HTTPBIN_URL}/cookies",
|
|
173
174
|
cookies: "foo=bar; baz=qux"
|
|
174
175
|
)
|
|
175
176
|
json = resp.json
|
|
176
177
|
assert_instance_of Hash, json
|
|
177
|
-
|
|
178
|
-
assert_equal "
|
|
178
|
+
cookies = json.fetch("cookies", json)
|
|
179
|
+
assert_equal "bar", cookies["foo"]
|
|
180
|
+
assert_equal "qux", cookies["baz"]
|
|
179
181
|
end
|
|
180
182
|
end
|
data/test/error_handling_test.rb
CHANGED
|
@@ -21,7 +21,7 @@ class ErrorHandlingTest < Minitest::Test
|
|
|
21
21
|
def test_http_error_status_codes
|
|
22
22
|
# These should not raise errors, just return responses with error codes
|
|
23
23
|
[400, 401, 403, 404, 500, 502, 503].each do |status_code|
|
|
24
|
-
response = Wreq.get("
|
|
24
|
+
response = Wreq.get("#{HTTPBIN_URL}/status/#{status_code}")
|
|
25
25
|
assert_equal status_code, response.code
|
|
26
26
|
# Should not raise an exception for HTTP error codes
|
|
27
27
|
end
|
|
@@ -31,7 +31,7 @@ class ErrorHandlingTest < Minitest::Test
|
|
|
31
31
|
# Test timeout with a delay that should definitely cause timeout
|
|
32
32
|
|
|
33
33
|
# Request with a very short timeout that should fail
|
|
34
|
-
response = Wreq.get("
|
|
34
|
+
response = Wreq.get("#{HTTPBIN_URL}/delay/10", timeout: 1)
|
|
35
35
|
# If we get here, the request didn't timeout (unexpected)
|
|
36
36
|
flunk "Expected timeout error but got response: #{response.code}"
|
|
37
37
|
rescue => e
|
|
@@ -42,7 +42,7 @@ class ErrorHandlingTest < Minitest::Test
|
|
|
42
42
|
|
|
43
43
|
def test_invalid_json_response
|
|
44
44
|
# Get a non-JSON response and try to parse as JSON
|
|
45
|
-
response = Wreq.get("
|
|
45
|
+
response = Wreq.get("#{HTTPBIN_URL}/html")
|
|
46
46
|
assert_equal 200, response.code
|
|
47
47
|
|
|
48
48
|
# Should raise an error when trying to parse HTML as JSON
|
|
@@ -54,7 +54,7 @@ class ErrorHandlingTest < Minitest::Test
|
|
|
54
54
|
end
|
|
55
55
|
|
|
56
56
|
def test_empty_response_json
|
|
57
|
-
response = Wreq.get("
|
|
57
|
+
response = Wreq.get("#{HTTPBIN_URL}/status/204")
|
|
58
58
|
assert_equal 204, response.code
|
|
59
59
|
assert_equal "", response.text
|
|
60
60
|
|
data/test/header_test.rb
CHANGED
|
@@ -2,7 +2,7 @@ require "test_helper"
|
|
|
2
2
|
|
|
3
3
|
class HeadersTest < Minitest::Test
|
|
4
4
|
def setup
|
|
5
|
-
@response = Wreq.get("
|
|
5
|
+
@response = Wreq.get("#{HTTPBIN_URL}/response-headers",
|
|
6
6
|
query: {
|
|
7
7
|
"X-Custom-Header" => "custom-value",
|
|
8
8
|
"X-Multi-Header" => "value1"
|
data/test/inspect_test.rb
CHANGED
|
@@ -84,12 +84,12 @@ class InspectTest < Minitest::Test
|
|
|
84
84
|
# ---- Response ----
|
|
85
85
|
|
|
86
86
|
def test_response_to_s_returns_body
|
|
87
|
-
response = Wreq.get("
|
|
87
|
+
response = Wreq.get("#{HTTPBIN_URL}/json")
|
|
88
88
|
assert_equal response.text, response.to_s
|
|
89
89
|
end
|
|
90
90
|
|
|
91
91
|
def test_response_inspect_format
|
|
92
|
-
response = Wreq.get("
|
|
92
|
+
response = Wreq.get("#{HTTPBIN_URL}/json")
|
|
93
93
|
result = response.inspect
|
|
94
94
|
assert result.start_with?("#<Wreq::Response")
|
|
95
95
|
assert_includes result, "200"
|
|
@@ -99,7 +99,7 @@ class InspectTest < Minitest::Test
|
|
|
99
99
|
# ---- StatusCode ----
|
|
100
100
|
|
|
101
101
|
def test_status_code_inspect
|
|
102
|
-
response = Wreq.get("
|
|
102
|
+
response = Wreq.get("#{HTTPBIN_URL}/status/200")
|
|
103
103
|
result = response.status.inspect
|
|
104
104
|
assert result.start_with?("#<Wreq::StatusCode")
|
|
105
105
|
assert_includes result, response.status.to_s
|
|
@@ -117,7 +117,7 @@ class InspectTest < Minitest::Test
|
|
|
117
117
|
end
|
|
118
118
|
|
|
119
119
|
def test_version_inspect_from_response
|
|
120
|
-
response = Wreq.get("
|
|
120
|
+
response = Wreq.get("#{HTTPBIN_URL}/get")
|
|
121
121
|
result = response.version.inspect
|
|
122
122
|
assert result.start_with?("#<Wreq::Version")
|
|
123
123
|
assert result.end_with?(">")
|