funnel_http 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,78 @@
1
+ github.com/cockroachdb/errors v1.11.3 h1:5bA+k2Y6r+oz/6Z/RFlNeVCesGARKuC6YymtcDrbC/I=
2
+ github.com/cockroachdb/errors v1.11.3/go.mod h1:m4UIW4CDjx+R5cybPsNrRbreomiFqt8o1h1wUVazSd8=
3
+ github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b h1:r6VH0faHjZeQy818SGhaone5OnYfxFR/+AzdY3sf5aE=
4
+ github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b/go.mod h1:Vz9DsVWQQhf3vs21MhPMZpMGSht7O/2vFW2xusFUVOs=
5
+ github.com/cockroachdb/redact v1.1.5 h1:u1PMllDkdFfPWaNGMyLD1+so+aq3uUItthCFqzwPJ30=
6
+ github.com/cockroachdb/redact v1.1.5/go.mod h1:BVNblN9mBWFyMyqK1k3AAiSxhvhfK2oOZZ2lK+dpvRg=
7
+ github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
8
+ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
9
+ github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
10
+ github.com/getsentry/sentry-go v0.27.0 h1:Pv98CIbtB3LkMWmXi4Joa5OOcwbmnX88sF5qbK3r3Ps=
11
+ github.com/getsentry/sentry-go v0.27.0/go.mod h1:lc76E2QywIyW8WuBnwl8Lc4bkmQH4+w1gwTf25trprY=
12
+ github.com/go-errors/errors v1.4.2 h1:J6MZopCL4uSllY1OfXM374weqZFFItUbrImctkmUxIA=
13
+ github.com/go-errors/errors v1.4.2/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3BopGUQ5a5Og=
14
+ github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
15
+ github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
16
+ github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
17
+ github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
18
+ github.com/jarcoal/httpmock v1.3.1 h1:iUx3whfZWVf3jT01hQTO/Eo5sAYtB2/rqaUuOtpInww=
19
+ github.com/jarcoal/httpmock v1.3.1/go.mod h1:3yb8rc4BI7TCBhFY8ng0gjuLKJNquuDNiPaZjnENuYg=
20
+ github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
21
+ github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
22
+ github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
23
+ github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
24
+ github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
25
+ github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
26
+ github.com/maxatome/go-testdeep v1.12.0 h1:Ql7Go8Tg0C1D/uMMX59LAoYK7LffeJQ6X2T04nTH68g=
27
+ github.com/maxatome/go-testdeep v1.12.0/go.mod h1:lPZc/HAcJMP92l7yI6TRz1aZN5URwUBUAfUNvrclaNM=
28
+ github.com/pingcap/errors v0.11.4 h1:lFuQV/oaUMGcD2tqt+01ROSmJs75VG1ToEOkZIZ4nE4=
29
+ github.com/pingcap/errors v0.11.4/go.mod h1:Oi8TUi2kEtXXLMJk9l1cGmz20kV3TaQ0usTwv5KuLY8=
30
+ github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
31
+ github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
32
+ github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
33
+ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
34
+ github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
35
+ github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8=
36
+ github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
37
+ github.com/ruby-go-gem/go-gem-wrapper v0.5.1 h1:TFGH/eOJl0uYzYMwrcLbZxXNQiQbQjCq/VLXyRk1HRM=
38
+ github.com/ruby-go-gem/go-gem-wrapper v0.5.1/go.mod h1:k2k+LziSCMxNYP4J9/9v90xdU6zlU1DJpJDTU6oJhHE=
39
+ github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
40
+ github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
41
+ github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
42
+ github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
43
+ golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
44
+ golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
45
+ golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
46
+ golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
47
+ golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
48
+ golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
49
+ golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
50
+ golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
51
+ golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
52
+ golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
53
+ golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
54
+ golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
55
+ golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ=
56
+ golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
57
+ golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
58
+ golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
59
+ golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
60
+ golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4=
61
+ golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
62
+ golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
63
+ golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
64
+ golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
65
+ golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
66
+ golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
67
+ golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
68
+ golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
69
+ golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
70
+ golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
71
+ golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
72
+ golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
73
+ golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
74
+ gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
75
+ gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
76
+ gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
77
+ gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
78
+ gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
@@ -0,0 +1,77 @@
1
+ package main
2
+
3
+ import (
4
+ "bytes"
5
+ "github.com/cockroachdb/errors"
6
+ "golang.org/x/sync/errgroup"
7
+ "io"
8
+ "net/http"
9
+ )
10
+
11
+ // Request is proxy between CRuby and Go
12
+ type Request struct {
13
+ Method string
14
+ URL string
15
+ Header map[string][]string
16
+ }
17
+
18
+ // Response is proxy between CRuby and Go
19
+ type Response struct {
20
+ StatusCode int
21
+ Header map[string][]string
22
+ Body []byte
23
+ }
24
+
25
+ // RunRequests perform HTTP requests in parallel
26
+ func RunRequests(httpClient *http.Client, requests []Request) ([]Response, error) {
27
+ g := new(errgroup.Group)
28
+ responses := make([]Response, len(requests))
29
+
30
+ for i, request := range requests {
31
+ // https://golang.org/doc/faq#closures_and_goroutines
32
+ i := i
33
+ request := request
34
+
35
+ g.Go(func() error {
36
+ var body []byte
37
+ httpReq, err := http.NewRequest(request.Method, request.URL, bytes.NewBuffer(body))
38
+ if err != nil {
39
+ return errors.WithStack(err)
40
+ }
41
+
42
+ for key, values := range request.Header {
43
+ for _, value := range values {
44
+ httpReq.Header.Add(key, value)
45
+ }
46
+ }
47
+
48
+ httpResp, err := httpClient.Do(httpReq)
49
+ if err != nil {
50
+ return errors.WithStack(err)
51
+ }
52
+ defer httpResp.Body.Close()
53
+
54
+ responses[i].StatusCode = httpResp.StatusCode
55
+
56
+ buf, err := io.ReadAll(httpResp.Body)
57
+ if err != nil {
58
+ return errors.WithStack(err)
59
+ }
60
+
61
+ responses[i].Body = buf
62
+ responses[i].Header = map[string][]string{}
63
+
64
+ for key, values := range httpResp.Header {
65
+ responses[i].Header[key] = append(responses[i].Header[key], values...)
66
+ }
67
+
68
+ return nil
69
+ })
70
+ }
71
+
72
+ if err := g.Wait(); err != nil {
73
+ return []Response{}, errors.WithStack(err)
74
+ }
75
+
76
+ return responses, nil
77
+ }
@@ -0,0 +1,120 @@
1
+ package main_test
2
+
3
+ import (
4
+ "github.com/jarcoal/httpmock"
5
+ "github.com/stretchr/testify/assert"
6
+ "github.com/sue445/funnel_http"
7
+ "net/http"
8
+ "testing"
9
+ )
10
+
11
+ func TestRunRequests(t *testing.T) {
12
+ httpmock.Activate()
13
+ t.Cleanup(httpmock.DeactivateAndReset)
14
+
15
+ httpmock.RegisterResponder("GET", "http://example.com/1",
16
+ func(req *http.Request) (*http.Response, error) {
17
+ resp := httpmock.NewStringResponse(200, "GET http://example.com/1")
18
+
19
+ resp.Header.Set("Content-Type", "text/plain")
20
+
21
+ for key, values := range req.Header {
22
+ for _, value := range values {
23
+ resp.Header.Add(key, value)
24
+ }
25
+ }
26
+
27
+ return resp, nil
28
+ })
29
+
30
+ httpmock.RegisterResponder("GET", "http://example.com/2",
31
+ func(req *http.Request) (*http.Response, error) {
32
+ resp := httpmock.NewStringResponse(200, "GET http://example.com/2")
33
+
34
+ resp.Header.Set("Content-Type", "text/plain")
35
+
36
+ for key, values := range req.Header {
37
+ for _, value := range values {
38
+ resp.Header.Add(key, value)
39
+ }
40
+ }
41
+
42
+ return resp, nil
43
+ })
44
+
45
+ tests := []struct {
46
+ name string
47
+ requests []main.Request
48
+ expected []main.Response
49
+ }{
50
+ {
51
+ name: "1 request",
52
+ requests: []main.Request{
53
+ {
54
+ Method: "GET",
55
+ URL: "http://example.com/1",
56
+ Header: map[string][]string{
57
+ "X-My-Request-Header": {"a", "b"},
58
+ },
59
+ },
60
+ },
61
+ expected: []main.Response{
62
+ {
63
+ StatusCode: 200,
64
+ Body: []byte("GET http://example.com/1"),
65
+ Header: map[string][]string{
66
+ "Content-Type": {"text/plain"},
67
+ "X-My-Request-Header": {"a", "b"},
68
+ },
69
+ },
70
+ },
71
+ },
72
+ {
73
+ name: "multiple requests",
74
+ requests: []main.Request{
75
+ {
76
+ Method: "GET",
77
+ URL: "http://example.com/1",
78
+ Header: map[string][]string{
79
+ "X-My-Request-Header": {"a", "b"},
80
+ },
81
+ },
82
+ {
83
+ Method: "GET",
84
+ URL: "http://example.com/2",
85
+ Header: map[string][]string{
86
+ "X-My-Request-Header": {"c", "d"},
87
+ },
88
+ },
89
+ },
90
+ expected: []main.Response{
91
+ {
92
+ StatusCode: 200,
93
+ Body: []byte("GET http://example.com/1"),
94
+ Header: map[string][]string{
95
+ "Content-Type": {"text/plain"},
96
+ "X-My-Request-Header": {"a", "b"},
97
+ },
98
+ },
99
+ {
100
+ StatusCode: 200,
101
+ Body: []byte("GET http://example.com/2"),
102
+ Header: map[string][]string{
103
+ "Content-Type": {"text/plain"},
104
+ "X-My-Request-Header": {"c", "d"},
105
+ },
106
+ },
107
+ },
108
+ },
109
+ }
110
+
111
+ httpClient := http.Client{}
112
+ for _, tt := range tests {
113
+ t.Run(tt.name, func(t *testing.T) {
114
+ actual, err := main.RunRequests(&httpClient, tt.requests)
115
+ if assert.NoError(t, err) {
116
+ assert.Equal(t, tt.expected, actual)
117
+ }
118
+ })
119
+ }
120
+ }
@@ -0,0 +1,112 @@
1
+ # frozen_string_literal: true
2
+
3
+ module FunnelHttp
4
+ class Client
5
+ # @!attribute default_request_header
6
+ # @return [Hash{String => String, Array<String>}]
7
+ attr_accessor :default_request_header
8
+
9
+ # @param default_request_header [Hash{String => String, Array<String>}]
10
+ def initialize(default_request_header: {})
11
+ @default_request_header = {"User-Agent" => USER_AGENT}.merge(default_request_header)
12
+ end
13
+
14
+ # Add header to {default_request_header}
15
+ # @param name [String] Header name
16
+ # @param value [String, Array<String>] Header value
17
+ #
18
+ # @return [Hash{String => String, Array<String>}] {default_request_header} after adding header
19
+ def add_default_request_header(name, value)
20
+ default_request_header.merge!(name => value)
21
+ end
22
+
23
+ # perform HTTP requests in parallel
24
+ #
25
+ # @overload perform(requests)
26
+ # @param requests [Array<Hash{Symbol => Object}>] `Array` of following `Hash`
27
+ # @option requests :method [String, Symbol] **[required]** Request method (e.g. `:get`, `"POST"`)
28
+ # @option requests :url [String] **[required]** Request url
29
+ # @option requests :header [Hash{String => String, Array<String>}, nil] Request header
30
+ #
31
+ # @overload perform(request)
32
+ # @param request [Hash{Symbol => Object}]
33
+ # @option request :method [String, Symbol] **[required]** Request method (e.g. `:get`, `"POST"`)
34
+ # @option request :url [String] **[required]** Request url
35
+ # @option request :header [Hash{String => String, Array<String>}, nil] Request header
36
+ #
37
+ # @return [Array<Hash<Symbol => Object>>] `Array` of following `Hash`
38
+ # @return [Integer] `:status_code`
39
+ # @return [String] `:body` Response body
40
+ # @return [Hash{String => Array<String>}] `:header` Response header
41
+ def perform(requests)
42
+ ext_client.run_requests(normalize_requests(requests))
43
+ end
44
+
45
+ # @overload normalize_requests(requests)
46
+ # @param requests [Array<Hash{Symbol => Object}>] `Array` of following `Hash`
47
+ # @option requests :method [String, Symbol] **[required]** Request method (e.g. `:get`, `"POST"`)
48
+ # @option requests :url [String] **[required]** Request url
49
+ # @option requests :header [Hash{String => String, Array<String>}, nil] Request header
50
+ #
51
+ # @overload normalize_requests(request)
52
+ # @param request [Hash{Symbol => Object}]
53
+ # @option request :method [String, Symbol] **[required]** Request method (e.g. `:get`, `"POST"`)
54
+ # @option request :url [String] **[required]** Request url
55
+ # @option request :header [Hash{String => String, Array<String>}, nil] Request header
56
+ #
57
+ # @return [Array<Hash{Symbol => Object}>] `Array` of following `Hash`
58
+ # @return [String] `:method` Request method (e.g. `"POST"`)
59
+ # @return [String] `:url` Request url
60
+ # @return [Hash{String => Array<String>}] `:header` Request header
61
+ def normalize_requests(arg)
62
+ requests =
63
+ case arg
64
+ when Array
65
+ arg
66
+ when Hash
67
+ [arg]
68
+ else
69
+ raise ArgumentError, "#{arg} must be Array or Hash"
70
+ end
71
+
72
+ requests.map do |request|
73
+ raise ArgumentError, "#{arg} contains something other than Hash" unless request.is_a?(Hash)
74
+
75
+ raise ArgumentError, "#{arg} key does not contain all :method and :url" if !request.key?(:method) || !request.key?(:url)
76
+
77
+ {
78
+ url: request[:url].to_s,
79
+ method: request[:method].to_s.upcase,
80
+ header: normalize_header(request[:header]),
81
+ }
82
+ end
83
+ end
84
+
85
+ private
86
+
87
+ # @param header [Hash{String => String, Array<String>}, nil] Request header
88
+ # @return [Hash{String => Array<String>}] Request header
89
+ def normalize_header(header)
90
+ full_header =
91
+ if header
92
+ default_request_header.dup.merge(header)
93
+ else
94
+ default_request_header.dup
95
+ end
96
+
97
+ # Workaround for Ruby::UnannotatedEmptyCollection on steep 1.9.0+
98
+ result = {} #: strict_header
99
+
100
+ full_header.each_with_object(result) do |(k, v), hash|
101
+ # FIXME: Fails `steep check` when use Array(v)...
102
+ # hash[k] = Array(v)
103
+ hash[k] = v.is_a?(Array) ? v : Array(v)
104
+ end
105
+ end
106
+
107
+ # @return [FunnelHttp::Ext::Client]
108
+ def ext_client
109
+ @ext_client ||= FunnelHttp::Ext::Client.new
110
+ end
111
+ end
112
+ end
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ module FunnelHttp
4
+ module Ext
5
+ end
6
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module FunnelHttp
4
+ VERSION = "0.1.0"
5
+ end
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "funnel_http/version"
4
+ require_relative "funnel_http/client"
5
+ require_relative "funnel_http/ext"
6
+ require_relative "funnel_http/funnel_http"
7
+
8
+ module FunnelHttp
9
+ class Error < StandardError; end
10
+
11
+ USER_AGENT = "funnel_http/#{FunnelHttp::VERSION} (+https://github.com/sue445/funnel_http)"
12
+
13
+ # Your code goes here...
14
+ end
@@ -0,0 +1,68 @@
1
+ ---
2
+ path: ".gem_rbs_collection"
3
+ gems:
4
+ - name: base64
5
+ version: '0'
6
+ source:
7
+ type: stdlib
8
+ - name: cgi
9
+ version: '0'
10
+ source:
11
+ type: stdlib
12
+ - name: diff-lcs
13
+ version: '1.5'
14
+ source:
15
+ type: git
16
+ name: ruby/gem_rbs_collection
17
+ revision: 0a6ea105a0afc7eaee4494585a7775f47eea6145
18
+ remote: https://github.com/ruby/gem_rbs_collection.git
19
+ repo_dir: gems
20
+ - name: fileutils
21
+ version: '0'
22
+ source:
23
+ type: stdlib
24
+ - name: logger
25
+ version: '0'
26
+ source:
27
+ type: stdlib
28
+ - name: monitor
29
+ version: '0'
30
+ source:
31
+ type: stdlib
32
+ - name: rack
33
+ version: '2.2'
34
+ source:
35
+ type: git
36
+ name: ruby/gem_rbs_collection
37
+ revision: 0a6ea105a0afc7eaee4494585a7775f47eea6145
38
+ remote: https://github.com/ruby/gem_rbs_collection.git
39
+ repo_dir: gems
40
+ - name: rake
41
+ version: '13.0'
42
+ source:
43
+ type: git
44
+ name: ruby/gem_rbs_collection
45
+ revision: 0a6ea105a0afc7eaee4494585a7775f47eea6145
46
+ remote: https://github.com/ruby/gem_rbs_collection.git
47
+ repo_dir: gems
48
+ - name: sinatra
49
+ version: '4.0'
50
+ source:
51
+ type: git
52
+ name: ruby/gem_rbs_collection
53
+ revision: 0a6ea105a0afc7eaee4494585a7775f47eea6145
54
+ remote: https://github.com/ruby/gem_rbs_collection.git
55
+ repo_dir: gems
56
+ - name: stringio
57
+ version: '0'
58
+ source:
59
+ type: stdlib
60
+ - name: tempfile
61
+ version: '0'
62
+ source:
63
+ type: stdlib
64
+ - name: uri
65
+ version: '0'
66
+ source:
67
+ type: stdlib
68
+ gemfile_lock_path: Gemfile.lock
@@ -0,0 +1,24 @@
1
+ # Download sources
2
+ sources:
3
+ - type: git
4
+ name: ruby/gem_rbs_collection
5
+ remote: https://github.com/ruby/gem_rbs_collection.git
6
+ revision: main
7
+ repo_dir: gems
8
+
9
+ # You can specify local directories as sources also.
10
+ # - type: local
11
+ # path: path/to/your/local/repository
12
+
13
+ # A directory to install the downloaded RBSs
14
+ path: .gem_rbs_collection
15
+
16
+ gems:
17
+ - name: rbs
18
+ ignore: true
19
+ - name: steep
20
+ ignore: true
21
+ - name: yard
22
+ ignore: true
23
+ - name: funnel_http
24
+ ignore: true
@@ -0,0 +1,21 @@
1
+ module FunnelHttp
2
+ class Client
3
+ @ext_client: FunnelHttp::Ext::Client
4
+
5
+ attr_accessor default_request_header: fuzzy_header
6
+
7
+ def initialize: (?default_request_header: fuzzy_header) -> void
8
+
9
+ def add_default_request_header: (String name, String | Array[String] value) -> fuzzy_header
10
+
11
+ def perform: (fuzzy_request | Array[fuzzy_request] requests) -> Array[response]
12
+
13
+ def normalize_requests: (fuzzy_request | Array[fuzzy_request] arg) -> Array[strict_request]
14
+
15
+ private
16
+
17
+ def normalize_header: (fuzzy_header? header) -> strict_header
18
+
19
+ def ext_client: () -> FunnelHttp::Ext::Client
20
+ end
21
+ end
@@ -0,0 +1,11 @@
1
+ module FunnelHttp
2
+ module Ext
3
+ # defined in ext/funnel_http/funnel_http.go
4
+ class Client
5
+ def run_requests: (Array[strict_request] requests) -> Array[response]
6
+ end
7
+
8
+ class GoData
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,31 @@
1
+ module FunnelHttp
2
+ VERSION: String
3
+ USER_AGENT: String
4
+
5
+ class Error < StandardError
6
+ end
7
+
8
+ type fuzzy_header = Hash[String, String | Array[String]]
9
+
10
+ type fuzzy_request = {
11
+ method: String | Symbol,
12
+ url: String,
13
+ header: fuzzy_header?
14
+ }
15
+
16
+ type strict_header = Hash[String, Array[String]]
17
+
18
+ type strict_request = {
19
+ method: String,
20
+ url: String,
21
+ header: strict_header
22
+ }
23
+
24
+ type response = {
25
+ status_code: Integer,
26
+ body: String,
27
+ header: strict_header
28
+ }
29
+
30
+ # See the writing guide of rbs: https://github.com/ruby/rbs#guides
31
+ end