funnel_http 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.golangci.yml +21 -0
- data/.rspec +3 -0
- data/.yardopts +6 -0
- data/CHANGELOG.md +6 -0
- data/LICENSE.txt +21 -0
- data/README.md +87 -0
- data/Rakefile +56 -0
- data/Steepfile +31 -0
- data/benchmark/README.md +32 -0
- data/benchmark/benchmark.rb +61 -0
- data/benchmark/compose.yml +8 -0
- data/benchmark/html/index.html +4 -0
- data/benchmark/nginx.conf +25 -0
- data/ext/funnel_http/extconf.rb +11 -0
- data/ext/funnel_http/funnel_http.c +2 -0
- data/ext/funnel_http/funnel_http.go +164 -0
- data/ext/funnel_http/funnel_http.h +6 -0
- data/ext/funnel_http/go.mod +27 -0
- data/ext/funnel_http/go.sum +78 -0
- data/ext/funnel_http/run_requests.go +77 -0
- data/ext/funnel_http/run_requests_test.go +120 -0
- data/lib/funnel_http/client.rb +112 -0
- data/lib/funnel_http/ext.rb +6 -0
- data/lib/funnel_http/version.rb +5 -0
- data/lib/funnel_http.rb +14 -0
- data/rbs_collection.lock.yaml +68 -0
- data/rbs_collection.yaml +24 -0
- data/sig/funnel_http/client.rbs +21 -0
- data/sig/funnel_http/ext.rbs +11 -0
- data/sig/funnel_http.rbs +31 -0
- metadata +261 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: cc6d1f5ffef3ad3e41de07eb3b94342bd082724aa3d1daad338603fbedbabcdc
|
4
|
+
data.tar.gz: f3208ba0c9154bd99fd2d373e91d3679690f0f711c74a3f11038c500c08fbd8a
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 87f9ad18e8c0005f2a3520d06381f3e308bbf6372827b631d646546c48f929be5879da1296331d9bab4ec7667786ce353473930b6795b84d0a46c11f9e68ec12
|
7
|
+
data.tar.gz: c92e03744f09aa670972e188bb0c94db2b2920f028994db5bb0f26d3804d470345b43087d57195666d1236bfc2adcc9c8ee8ccde4c0ca50ae94b54887a0c1dd6
|
data/.golangci.yml
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
linters-settings:
|
2
|
+
gofmt:
|
3
|
+
revive:
|
4
|
+
rules:
|
5
|
+
- name: exported
|
6
|
+
arguments:
|
7
|
+
- disableStutteringCheck
|
8
|
+
testifylint:
|
9
|
+
wrapcheck:
|
10
|
+
|
11
|
+
linters:
|
12
|
+
enable:
|
13
|
+
- gofmt
|
14
|
+
- revive
|
15
|
+
- testifylint
|
16
|
+
- wrapcheck
|
17
|
+
|
18
|
+
issues:
|
19
|
+
include:
|
20
|
+
- EXC0012 # EXC0012 revive: Annoying issue about not having a comment. The rare codebase has such comments
|
21
|
+
- EXC0014 # EXC0014 revive: Annoying issue about not having a comment. The rare codebase has such comments
|
data/.rspec
ADDED
data/.yardopts
ADDED
data/CHANGELOG.md
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2024 sue445
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
13
|
+
all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,87 @@
|
|
1
|
+
# FunnelHttp
|
2
|
+
Perform HTTP requests in parallel
|
3
|
+
|
4
|
+
[![build](https://github.com/sue445/funnel_http/actions/workflows/build.yml/badge.svg)](https://github.com/sue445/funnel_http/actions/workflows/build.yml)
|
5
|
+
|
6
|
+
## Requirements
|
7
|
+
* Ruby
|
8
|
+
* Go
|
9
|
+
|
10
|
+
## Installation
|
11
|
+
|
12
|
+
Install the gem and add to the application's Gemfile by executing:
|
13
|
+
|
14
|
+
```bash
|
15
|
+
bundle add funnel_http
|
16
|
+
```
|
17
|
+
|
18
|
+
If bundler is not being used to manage dependencies, install the gem by executing:
|
19
|
+
|
20
|
+
```bash
|
21
|
+
gem install funnel_http
|
22
|
+
```
|
23
|
+
|
24
|
+
## Usage
|
25
|
+
Use [`FunnelHttp::Client#perform`](https://sue445.github.io/funnel_http/FunnelHttp/Client.html#perform-instance_method)
|
26
|
+
|
27
|
+
```ruby
|
28
|
+
require "funnel_http"
|
29
|
+
|
30
|
+
client = FunnelHttp::Client.new
|
31
|
+
|
32
|
+
requests = [
|
33
|
+
{
|
34
|
+
method: :get,
|
35
|
+
uri: "https://example.com/api/user/1",
|
36
|
+
},
|
37
|
+
|
38
|
+
# with request header
|
39
|
+
{
|
40
|
+
method: :get,
|
41
|
+
uri: "https://example.com/api/user/2",
|
42
|
+
header: {
|
43
|
+
"Authorization" => "Bearer xxxxxxxx",
|
44
|
+
"X-Multiple-Values" => ["1st value", "2nd value"],
|
45
|
+
},
|
46
|
+
},
|
47
|
+
]
|
48
|
+
|
49
|
+
responses = client.perform(requests)
|
50
|
+
# => [
|
51
|
+
# { status_code: 200, body: "Response of /api/user/1", header: { "Content-Type" => ["text/plain;charset=utf-8"]} }
|
52
|
+
# { status_code: 200, body: "Response of /api/user/2", header: { "Content-Type" => ["text/plain;charset=utf-8"]} }
|
53
|
+
# ]
|
54
|
+
```
|
55
|
+
|
56
|
+
## Customize
|
57
|
+
### Add default header
|
58
|
+
Example1. Pass default header to [`FunnelHttp::Client#initialize`](https://sue445.github.io/funnel_http/FunnelHttp/Client.html#normalize_requests-instance_method)
|
59
|
+
|
60
|
+
```ruby
|
61
|
+
default_header = { "Authorization" => "Bearer xxxxxx" }
|
62
|
+
|
63
|
+
client = FunnelHttp::Client.new(default_header)
|
64
|
+
```
|
65
|
+
|
66
|
+
Example 2. Use [`FunnelHttp::Client#add_default_request_header`](https://sue445.github.io/funnel_http/FunnelHttp/Client.html#add_default_request_header-instance_method)
|
67
|
+
|
68
|
+
```ruby
|
69
|
+
client.add_default_request_header("Authorization", "Bearer xxxxxx")
|
70
|
+
```
|
71
|
+
|
72
|
+
## API Reference
|
73
|
+
https://sue445.github.io/funnel_http/
|
74
|
+
|
75
|
+
## Development
|
76
|
+
|
77
|
+
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
78
|
+
|
79
|
+
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and the created tag, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
80
|
+
|
81
|
+
## Contributing
|
82
|
+
|
83
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/sue445/funnel_http.
|
84
|
+
|
85
|
+
## License
|
86
|
+
|
87
|
+
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
data/Rakefile
ADDED
@@ -0,0 +1,56 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "bundler/gem_tasks"
|
4
|
+
require "rspec/core/rake_task"
|
5
|
+
|
6
|
+
RSpec::Core::RakeTask.new(:spec)
|
7
|
+
|
8
|
+
require "rake/extensiontask"
|
9
|
+
|
10
|
+
task build: :compile
|
11
|
+
|
12
|
+
GEMSPEC = Gem::Specification.load("funnel_http.gemspec")
|
13
|
+
|
14
|
+
Rake::ExtensionTask.new("funnel_http", GEMSPEC) do |ext|
|
15
|
+
ext.lib_dir = "lib/funnel_http"
|
16
|
+
end
|
17
|
+
|
18
|
+
require "go_gem/rake_task"
|
19
|
+
|
20
|
+
go_task = GoGem::RakeTask.new("funnel_http")
|
21
|
+
|
22
|
+
namespace :go do
|
23
|
+
desc "Run golangci-lint"
|
24
|
+
task :lint do
|
25
|
+
go_task.within_target_dir do
|
26
|
+
sh "which golangci-lint" do |ok, _|
|
27
|
+
raise "golangci-lint isn't installed. See. https://golangci-lint.run/welcome/install/" unless ok
|
28
|
+
end
|
29
|
+
sh GoGem::RakeTask.build_env_vars, "golangci-lint run"
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
desc "Run go mod tidy"
|
34
|
+
task :mod_tidy do
|
35
|
+
go_task.within_target_dir do
|
36
|
+
sh "go mod tidy"
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
namespace :rbs do
|
42
|
+
desc "`rbs collection install` and `git commit`"
|
43
|
+
task :install do
|
44
|
+
sh "rbs collection install"
|
45
|
+
sh "git add rbs_collection.lock.yaml"
|
46
|
+
sh "git commit -m 'rbs collection install' || true"
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
desc "Check rbs"
|
51
|
+
task :rbs do
|
52
|
+
sh "rbs validate"
|
53
|
+
sh "steep check"
|
54
|
+
end
|
55
|
+
|
56
|
+
task default: %i[clobber compile go:test spec]
|
data/Steepfile
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
# D = Steep::Diagnostic
|
2
|
+
|
3
|
+
target :lib do
|
4
|
+
signature "sig"
|
5
|
+
|
6
|
+
check "lib" # Directory name
|
7
|
+
# check "Gemfile" # File name
|
8
|
+
# check "app/models/**/*.rb" # Glob
|
9
|
+
# ignore "lib/templates/*.rb"
|
10
|
+
|
11
|
+
# library "pathname" # Standard libraries
|
12
|
+
# library "strong_json" # Gems
|
13
|
+
|
14
|
+
collection_config "rbs_collection.yaml"
|
15
|
+
|
16
|
+
# configure_code_diagnostics(D::Ruby.default) # `default` diagnostics setting (applies by default)
|
17
|
+
# configure_code_diagnostics(D::Ruby.strict) # `strict` diagnostics setting
|
18
|
+
# configure_code_diagnostics(D::Ruby.lenient) # `lenient` diagnostics setting
|
19
|
+
# configure_code_diagnostics(D::Ruby.silent) # `silent` diagnostics setting
|
20
|
+
# configure_code_diagnostics do |hash| # You can setup everything yourself
|
21
|
+
# hash[D::Ruby::NoMethod] = :information
|
22
|
+
# end
|
23
|
+
end
|
24
|
+
|
25
|
+
# target :test do
|
26
|
+
# signature "sig", "sig-private"
|
27
|
+
#
|
28
|
+
# check "test"
|
29
|
+
#
|
30
|
+
# # library "pathname" # Standard libraries
|
31
|
+
# end
|
data/benchmark/README.md
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
# benchmark for funnel_http
|
2
|
+
## Usage
|
3
|
+
```bash
|
4
|
+
docker compose up --build
|
5
|
+
```
|
6
|
+
|
7
|
+
```bash
|
8
|
+
bundle exec ruby benchmark.rb
|
9
|
+
```
|
10
|
+
|
11
|
+
## Report
|
12
|
+
```
|
13
|
+
Warming up --------------------------------------
|
14
|
+
FunnelHttp::Client#perform
|
15
|
+
2.000 i/100ms
|
16
|
+
Parallel with 4 processes
|
17
|
+
1.000 i/100ms
|
18
|
+
Parallel with 4 threads
|
19
|
+
1.000 i/100ms
|
20
|
+
Calculating -------------------------------------
|
21
|
+
FunnelHttp::Client#perform
|
22
|
+
21.816 (± 4.6%) i/s (45.84 ms/i) - 44.000 in 2.026960s
|
23
|
+
Parallel with 4 processes
|
24
|
+
15.785 (± 6.3%) i/s (63.35 ms/i) - 32.000 in 2.035628s
|
25
|
+
Parallel with 4 threads
|
26
|
+
18.570 (±10.8%) i/s (53.85 ms/i) - 37.000 in 2.008485s
|
27
|
+
|
28
|
+
Comparison:
|
29
|
+
FunnelHttp::Client#perform: 21.8 i/s
|
30
|
+
Parallel with 4 threads: 18.6 i/s - 1.17x slower
|
31
|
+
Parallel with 4 processes: 15.8 i/s - 1.38x slower
|
32
|
+
```
|
@@ -0,0 +1,61 @@
|
|
1
|
+
require "benchmark/ips"
|
2
|
+
require "open-uri"
|
3
|
+
require "parallel"
|
4
|
+
require "etc"
|
5
|
+
|
6
|
+
ROOT_DIR = File.expand_path("..", __dir__)
|
7
|
+
|
8
|
+
TEST_SERVER_URL = "http://localhost:8080/"
|
9
|
+
|
10
|
+
REQUEST_COUNT = 100
|
11
|
+
|
12
|
+
BENCHMARK_CONCURRENCY = ENV.fetch("BENCHMARK_CONCURRENCY") { 4 }
|
13
|
+
|
14
|
+
# Build native extension before running benchmark
|
15
|
+
Dir.chdir(ROOT_DIR) do
|
16
|
+
system("bundle config set --local path 'vendor/bundle'", exception: true)
|
17
|
+
system("bundle install", exception: true)
|
18
|
+
system("bundle exec rake clobber compile", exception: true)
|
19
|
+
end
|
20
|
+
|
21
|
+
require_relative "../lib/funnel_http"
|
22
|
+
|
23
|
+
# Suppress Ractor warning
|
24
|
+
$VERBOSE = nil
|
25
|
+
|
26
|
+
system("go version", exception: true)
|
27
|
+
|
28
|
+
requests = Array.new(REQUEST_COUNT, { method: :get, url: TEST_SERVER_URL })
|
29
|
+
|
30
|
+
def fetch_server
|
31
|
+
URI.parse(TEST_SERVER_URL).open(open_timeout: 90, read_timeout: 90).read
|
32
|
+
end
|
33
|
+
|
34
|
+
Benchmark.ips do |x|
|
35
|
+
x.config(warmup: 1, time: 2)
|
36
|
+
|
37
|
+
x.report("FunnelHttp::Client#perform") do
|
38
|
+
FunnelHttp::Client.new.perform(requests)
|
39
|
+
end
|
40
|
+
|
41
|
+
x.report("Parallel with #{BENCHMARK_CONCURRENCY} processes") do
|
42
|
+
Parallel.each(requests, in_processes: BENCHMARK_CONCURRENCY) do
|
43
|
+
fetch_server
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
x.report("Parallel with #{BENCHMARK_CONCURRENCY} threads") do
|
48
|
+
Parallel.each(requests, in_threads: BENCHMARK_CONCURRENCY) do
|
49
|
+
fetch_server
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
# FIXME: open-uri doesn't work in Ractor
|
54
|
+
# x.report("Parallel with Ractor") do
|
55
|
+
# REQUEST_COUNT.times.map do
|
56
|
+
# Ractor.new { URI.parse("http://localhost:8080/").read }
|
57
|
+
# end.each(&:take)
|
58
|
+
# end
|
59
|
+
|
60
|
+
x.compare!
|
61
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
worker_processes auto;
|
2
|
+
worker_rlimit_nofile 12288;
|
3
|
+
|
4
|
+
events {
|
5
|
+
worker_connections 4096;
|
6
|
+
}
|
7
|
+
|
8
|
+
http {
|
9
|
+
include mime.types;
|
10
|
+
default_type application/octet-stream;
|
11
|
+
|
12
|
+
sendfile on;
|
13
|
+
keepalive_timeout 65;
|
14
|
+
access_log off;
|
15
|
+
|
16
|
+
server {
|
17
|
+
listen 80;
|
18
|
+
server_name localhost;
|
19
|
+
|
20
|
+
location / {
|
21
|
+
root /usr/share/nginx/html;
|
22
|
+
index index.html;
|
23
|
+
}
|
24
|
+
}
|
25
|
+
}
|
@@ -0,0 +1,11 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "mkmf"
|
4
|
+
require "go_gem/mkmf"
|
5
|
+
|
6
|
+
# Makes all symbols private by default to avoid unintended conflict
|
7
|
+
# with other gems. To explicitly export symbols you can use RUBY_FUNC_EXPORTED
|
8
|
+
# selectively, or entirely remove this flag.
|
9
|
+
append_cflags("-fvisibility=hidden")
|
10
|
+
|
11
|
+
create_go_makefile("funnel_http/funnel_http")
|
@@ -0,0 +1,164 @@
|
|
1
|
+
package main
|
2
|
+
|
3
|
+
/*
|
4
|
+
#include "funnel_http.h"
|
5
|
+
|
6
|
+
VALUE rb_go_data_alloc(VALUE klass);
|
7
|
+
VALUE rb_funnel_http_run_requests(VALUE self, VALUE rbAry);
|
8
|
+
*/
|
9
|
+
import "C"
|
10
|
+
|
11
|
+
import (
|
12
|
+
"github.com/ruby-go-gem/go-gem-wrapper/ruby"
|
13
|
+
"net/http"
|
14
|
+
"unsafe"
|
15
|
+
)
|
16
|
+
|
17
|
+
//export rb_funnel_http_run_requests
|
18
|
+
func rb_funnel_http_run_requests(self C.VALUE, rbAry C.VALUE) C.VALUE {
|
19
|
+
rbAryLength := int(ruby.RARRAY_LEN(ruby.VALUE(rbAry)))
|
20
|
+
requests := make([]Request, 0, rbAryLength)
|
21
|
+
|
22
|
+
for i := 0; i < rbAryLength; i++ {
|
23
|
+
rbHash := ruby.RbAryEntry(ruby.VALUE(rbAry), ruby.Long(i))
|
24
|
+
|
25
|
+
req := Request{
|
26
|
+
Method: getRbHashValueAsString(rbHash, "method"),
|
27
|
+
URL: getRbHashValueAsString(rbHash, "url"),
|
28
|
+
Header: getRbHashValueAsMap(rbHash, "header"),
|
29
|
+
}
|
30
|
+
requests = append(requests, req)
|
31
|
+
}
|
32
|
+
|
33
|
+
httpClient := getHttpClientFromInstanceVariable(ruby.VALUE(self))
|
34
|
+
|
35
|
+
responses, err := RunRequests(&httpClient, requests)
|
36
|
+
if err != nil {
|
37
|
+
ruby.RbRaise(rb_cFunnelHttpError, "%s", err.Error())
|
38
|
+
}
|
39
|
+
|
40
|
+
var rbHashSlice []ruby.VALUE
|
41
|
+
for _, response := range responses {
|
42
|
+
rbHash := ruby.RbHashNew()
|
43
|
+
|
44
|
+
ruby.RbHashAset(rbHash, ruby.RbId2Sym(ruby.RbIntern("status_code")), ruby.INT2NUM(response.StatusCode))
|
45
|
+
ruby.RbHashAset(rbHash, ruby.RbId2Sym(ruby.RbIntern("body")), ruby.String2Value(string(response.Body)))
|
46
|
+
|
47
|
+
rbHashHeader := ruby.RbHashNew()
|
48
|
+
ruby.RbGcRegisterAddress(&rbHashHeader)
|
49
|
+
defer ruby.RbGcUnregisterAddress(&rbHashHeader)
|
50
|
+
|
51
|
+
for key, values := range response.Header {
|
52
|
+
var headerValues []ruby.VALUE
|
53
|
+
for _, value := range values {
|
54
|
+
v := ruby.String2Value(value)
|
55
|
+
ruby.RbGcRegisterAddress(&v)
|
56
|
+
defer ruby.RbGcUnregisterAddress(&v)
|
57
|
+
|
58
|
+
headerValues = append(headerValues, v)
|
59
|
+
}
|
60
|
+
k := ruby.String2Value(key)
|
61
|
+
ruby.RbGcRegisterAddress(&k)
|
62
|
+
defer ruby.RbGcUnregisterAddress(&k)
|
63
|
+
|
64
|
+
v := ruby.Slice2rbAry(headerValues)
|
65
|
+
ruby.RbGcRegisterAddress(&v)
|
66
|
+
defer ruby.RbGcUnregisterAddress(&v)
|
67
|
+
|
68
|
+
ruby.RbHashAset(rbHashHeader, k, v)
|
69
|
+
}
|
70
|
+
ruby.RbHashAset(rbHash, ruby.RbId2Sym(ruby.RbIntern("header")), rbHashHeader)
|
71
|
+
|
72
|
+
rbHashSlice = append(rbHashSlice, rbHash)
|
73
|
+
}
|
74
|
+
|
75
|
+
return C.VALUE(ruby.Slice2rbAry(rbHashSlice))
|
76
|
+
}
|
77
|
+
|
78
|
+
func getHttpClientFromInstanceVariable(self ruby.VALUE) http.Client {
|
79
|
+
id := ruby.RbIntern("@__go_data")
|
80
|
+
value := ruby.RbIvarGet(self, id)
|
81
|
+
|
82
|
+
if !ruby.RB_NIL_P(value) {
|
83
|
+
// return http.Client in instance variable
|
84
|
+
data := (*goData)(ruby.GetGoStruct(value))
|
85
|
+
return data.httpClient
|
86
|
+
}
|
87
|
+
|
88
|
+
// Create instance of FunnelHttp::Ext::GoData
|
89
|
+
obj := ruby.RbObjAlloc(rb_cFunnelHttpExtGoData)
|
90
|
+
data := ruby.GetGoStruct(obj)
|
91
|
+
|
92
|
+
// Save FunnelHttp::Ext::GoData to instance variable of FunnelHttp::Ext::Client
|
93
|
+
dataValue := (*ruby.VALUE)(data)
|
94
|
+
ruby.RbIvarSet(self, id, *dataValue)
|
95
|
+
|
96
|
+
return ((*goData)(data)).httpClient
|
97
|
+
}
|
98
|
+
|
99
|
+
type goData struct {
|
100
|
+
httpClient http.Client
|
101
|
+
}
|
102
|
+
|
103
|
+
//export rb_go_data_alloc
|
104
|
+
func rb_go_data_alloc(klass C.VALUE) C.VALUE {
|
105
|
+
data := goData{}
|
106
|
+
return C.VALUE(ruby.NewGoStruct(ruby.VALUE(klass), unsafe.Pointer(&data)))
|
107
|
+
}
|
108
|
+
|
109
|
+
func getRbHashValueAsString(rbHash ruby.VALUE, key string) string {
|
110
|
+
value := ruby.RbHashAref(rbHash, ruby.RbToSymbol(ruby.String2Value(key)))
|
111
|
+
return ruby.Value2String(value)
|
112
|
+
}
|
113
|
+
|
114
|
+
func getRbHashValueAsMap(rbHash ruby.VALUE, key string) map[string][]string {
|
115
|
+
rbHashValue := ruby.RbHashAref(rbHash, ruby.RbToSymbol(ruby.String2Value(key)))
|
116
|
+
rbKeys := ruby.CallFunction(rbHashValue, "keys")
|
117
|
+
|
118
|
+
var ret = map[string][]string{}
|
119
|
+
|
120
|
+
for i := 0; i < int(ruby.RARRAY_LEN(rbKeys)); i++ {
|
121
|
+
rbKey := ruby.RbAryEntry(rbKeys, ruby.Long(i))
|
122
|
+
rbAryValue := ruby.RbHashAref(rbHashValue, rbKey)
|
123
|
+
|
124
|
+
var values []string
|
125
|
+
for j := 0; j < int(ruby.RARRAY_LEN(rbAryValue)); j++ {
|
126
|
+
str := ruby.Value2String(ruby.RbAryEntry(rbAryValue, ruby.Long(j)))
|
127
|
+
values = append(values, str)
|
128
|
+
}
|
129
|
+
|
130
|
+
keyName := ruby.Value2String(rbKey)
|
131
|
+
ret[keyName] = values
|
132
|
+
}
|
133
|
+
|
134
|
+
return ret
|
135
|
+
}
|
136
|
+
|
137
|
+
// revive:disable:exported
|
138
|
+
|
139
|
+
var rb_cFunnelHttpError ruby.VALUE
|
140
|
+
var rb_cFunnelHttpExtGoData ruby.VALUE
|
141
|
+
|
142
|
+
//export Init_funnel_http
|
143
|
+
func Init_funnel_http() {
|
144
|
+
rb_mFunnelHttp := ruby.RbDefineModule("FunnelHttp")
|
145
|
+
|
146
|
+
// FunnelHttp::Ext
|
147
|
+
rb_mFunnelHttpExt := ruby.RbDefineModuleUnder(rb_mFunnelHttp, "Ext")
|
148
|
+
|
149
|
+
// FunnelHttp::Ext::Client
|
150
|
+
rb_cFunnelHttpExtClient := ruby.RbDefineClassUnder(rb_mFunnelHttpExt, "Client", ruby.VALUE(C.rb_cObject))
|
151
|
+
ruby.RbDefineMethod(rb_cFunnelHttpExtClient, "run_requests", C.rb_funnel_http_run_requests, 1)
|
152
|
+
|
153
|
+
// FunnelHttp::Ext::GoData
|
154
|
+
rb_cFunnelHttpExtGoData = ruby.RbDefineClassUnder(rb_mFunnelHttpExt, "GoData", ruby.VALUE(C.rb_cObject))
|
155
|
+
ruby.RbDefineAllocFunc(rb_cFunnelHttpExtGoData, C.rb_go_data_alloc)
|
156
|
+
|
157
|
+
// FunnelHttp::Error
|
158
|
+
rb_cFunnelHttpError = ruby.RbDefineClassUnder(rb_mFunnelHttp, "Error", ruby.VALUE(C.rb_eStandardError))
|
159
|
+
}
|
160
|
+
|
161
|
+
// revive:enable:exported
|
162
|
+
|
163
|
+
func main() {
|
164
|
+
}
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module github.com/sue445/funnel_http
|
2
|
+
|
3
|
+
go 1.23
|
4
|
+
|
5
|
+
require (
|
6
|
+
github.com/cockroachdb/errors v1.11.3
|
7
|
+
github.com/jarcoal/httpmock v1.3.1
|
8
|
+
github.com/ruby-go-gem/go-gem-wrapper v0.5.1
|
9
|
+
github.com/stretchr/testify v1.10.0
|
10
|
+
golang.org/x/sync v0.10.0
|
11
|
+
)
|
12
|
+
|
13
|
+
require (
|
14
|
+
github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b // indirect
|
15
|
+
github.com/cockroachdb/redact v1.1.5 // indirect
|
16
|
+
github.com/davecgh/go-spew v1.1.1 // indirect
|
17
|
+
github.com/getsentry/sentry-go v0.27.0 // indirect
|
18
|
+
github.com/gogo/protobuf v1.3.2 // indirect
|
19
|
+
github.com/kr/pretty v0.3.1 // indirect
|
20
|
+
github.com/kr/text v0.2.0 // indirect
|
21
|
+
github.com/pkg/errors v0.9.1 // indirect
|
22
|
+
github.com/pmezard/go-difflib v1.0.0 // indirect
|
23
|
+
github.com/rogpeppe/go-internal v1.9.0 // indirect
|
24
|
+
golang.org/x/sys v0.18.0 // indirect
|
25
|
+
golang.org/x/text v0.14.0 // indirect
|
26
|
+
gopkg.in/yaml.v3 v3.0.1 // indirect
|
27
|
+
)
|