ethon-impersonate 0.17.9-arm64-darwin
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 +7 -0
- data/CHANGELOG.md +391 -0
- data/LICENSE +20 -0
- data/README.md +116 -0
- data/config/puma.rb +1 -0
- data/ethon-impersonate.gemspec +29 -0
- data/ext/libcurl-impersonate.4.dylib +0 -0
- data/lib/ethon-impersonate.rb +1 -0
- data/lib/ethon_impersonate/curl.rb +90 -0
- data/lib/ethon_impersonate/curls/classes.rb +65 -0
- data/lib/ethon_impersonate/curls/codes.rb +122 -0
- data/lib/ethon_impersonate/curls/constants.rb +81 -0
- data/lib/ethon_impersonate/curls/form_options.rb +37 -0
- data/lib/ethon_impersonate/curls/functions.rb +59 -0
- data/lib/ethon_impersonate/curls/infos.rb +151 -0
- data/lib/ethon_impersonate/curls/messages.rb +19 -0
- data/lib/ethon_impersonate/curls/options.rb +503 -0
- data/lib/ethon_impersonate/curls/settings.rb +13 -0
- data/lib/ethon_impersonate/easy/callbacks.rb +149 -0
- data/lib/ethon_impersonate/easy/debug_info.rb +49 -0
- data/lib/ethon_impersonate/easy/features.rb +31 -0
- data/lib/ethon_impersonate/easy/form.rb +107 -0
- data/lib/ethon_impersonate/easy/header.rb +65 -0
- data/lib/ethon_impersonate/easy/http/actionable.rb +157 -0
- data/lib/ethon_impersonate/easy/http/custom.rb +29 -0
- data/lib/ethon_impersonate/easy/http/delete.rb +25 -0
- data/lib/ethon_impersonate/easy/http/get.rb +24 -0
- data/lib/ethon_impersonate/easy/http/head.rb +24 -0
- data/lib/ethon_impersonate/easy/http/options.rb +24 -0
- data/lib/ethon_impersonate/easy/http/patch.rb +24 -0
- data/lib/ethon_impersonate/easy/http/post.rb +26 -0
- data/lib/ethon_impersonate/easy/http/postable.rb +32 -0
- data/lib/ethon_impersonate/easy/http/put.rb +27 -0
- data/lib/ethon_impersonate/easy/http/putable.rb +25 -0
- data/lib/ethon_impersonate/easy/http.rb +68 -0
- data/lib/ethon_impersonate/easy/informations.rb +118 -0
- data/lib/ethon_impersonate/easy/mirror.rb +38 -0
- data/lib/ethon_impersonate/easy/operations.rb +64 -0
- data/lib/ethon_impersonate/easy/options.rb +50 -0
- data/lib/ethon_impersonate/easy/params.rb +29 -0
- data/lib/ethon_impersonate/easy/queryable.rb +154 -0
- data/lib/ethon_impersonate/easy/response_callbacks.rb +136 -0
- data/lib/ethon_impersonate/easy/util.rb +28 -0
- data/lib/ethon_impersonate/easy.rb +332 -0
- data/lib/ethon_impersonate/errors/ethon_error.rb +9 -0
- data/lib/ethon_impersonate/errors/global_init.rb +13 -0
- data/lib/ethon_impersonate/errors/invalid_option.rb +13 -0
- data/lib/ethon_impersonate/errors/invalid_value.rb +13 -0
- data/lib/ethon_impersonate/errors/multi_add.rb +12 -0
- data/lib/ethon_impersonate/errors/multi_fdset.rb +12 -0
- data/lib/ethon_impersonate/errors/multi_remove.rb +12 -0
- data/lib/ethon_impersonate/errors/multi_timeout.rb +13 -0
- data/lib/ethon_impersonate/errors/select.rb +13 -0
- data/lib/ethon_impersonate/errors.rb +17 -0
- data/lib/ethon_impersonate/impersonate/fingerprints.rb +16 -0
- data/lib/ethon_impersonate/impersonate/settings.rb +85 -0
- data/lib/ethon_impersonate/impersonate/targets.rb +99 -0
- data/lib/ethon_impersonate/impersonate/tls.rb +189 -0
- data/lib/ethon_impersonate/impersonate.rb +10 -0
- data/lib/ethon_impersonate/libc.rb +21 -0
- data/lib/ethon_impersonate/loggable.rb +59 -0
- data/lib/ethon_impersonate/multi/operations.rb +228 -0
- data/lib/ethon_impersonate/multi/options.rb +117 -0
- data/lib/ethon_impersonate/multi/stack.rb +49 -0
- data/lib/ethon_impersonate/multi.rb +126 -0
- data/lib/ethon_impersonate/version.rb +6 -0
- data/lib/ethon_impersonate.rb +28 -0
- metadata +122 -0
@@ -0,0 +1,68 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require 'ethon_impersonate/easy/http/actionable'
|
3
|
+
require 'ethon_impersonate/easy/http/post'
|
4
|
+
require 'ethon_impersonate/easy/http/get'
|
5
|
+
require 'ethon_impersonate/easy/http/head'
|
6
|
+
require 'ethon_impersonate/easy/http/put'
|
7
|
+
require 'ethon_impersonate/easy/http/delete'
|
8
|
+
require 'ethon_impersonate/easy/http/patch'
|
9
|
+
require 'ethon_impersonate/easy/http/options'
|
10
|
+
require 'ethon_impersonate/easy/http/custom'
|
11
|
+
|
12
|
+
module EthonImpersonate
|
13
|
+
class Easy
|
14
|
+
|
15
|
+
# This module contains logic about making valid HTTP requests.
|
16
|
+
module Http
|
17
|
+
|
18
|
+
# Set specified options in order to make a HTTP request.
|
19
|
+
# Look at {EthonImpersonate::Easy::Options Options} to see what you can
|
20
|
+
# provide in the options hash.
|
21
|
+
#
|
22
|
+
# @example Set options for HTTP request.
|
23
|
+
# easy.http_request("www.google.com", :get, {})
|
24
|
+
#
|
25
|
+
# @param [ String ] url The url.
|
26
|
+
# @param [ String ] action_name The HTTP action name.
|
27
|
+
# @param [ Hash ] options The options hash.
|
28
|
+
#
|
29
|
+
# @option options :params [ Hash ] Params hash which
|
30
|
+
# is attached to the url.
|
31
|
+
# @option options :body [ Hash ] Body hash which
|
32
|
+
# becomes the request body. It is a PUT body for
|
33
|
+
# PUT requests and a POST for everything else.
|
34
|
+
# @option options :headers [ Hash ] Request headers.
|
35
|
+
#
|
36
|
+
# @return [ void ]
|
37
|
+
#
|
38
|
+
# @see EthonImpersonate::Easy::Options
|
39
|
+
def http_request(url, action_name, options = {})
|
40
|
+
fabricate(url, action_name, options).setup(self)
|
41
|
+
end
|
42
|
+
|
43
|
+
private
|
44
|
+
|
45
|
+
# Return the corresponding action class.
|
46
|
+
#
|
47
|
+
# @example Return the action.
|
48
|
+
# Action.fabricate(:get)
|
49
|
+
# Action.fabricate(:smash)
|
50
|
+
#
|
51
|
+
# @param [ String ] url The url.
|
52
|
+
# @param [ String ] action_name The HTTP action name.
|
53
|
+
# @param [ Hash ] options The option hash.
|
54
|
+
#
|
55
|
+
# @return [ Easy::EthonImpersonate::Actionable ] The request instance.
|
56
|
+
def fabricate(url, action_name, options)
|
57
|
+
constant_name = action_name.to_s.capitalize
|
58
|
+
|
59
|
+
if EthonImpersonate::Easy::Http.const_defined?(constant_name)
|
60
|
+
EthonImpersonate::Easy::Http.const_get(constant_name).new(url, options)
|
61
|
+
else
|
62
|
+
EthonImpersonate::Easy::Http::Custom.new(constant_name.upcase, url, options)
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
@@ -0,0 +1,118 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
module EthonImpersonate
|
3
|
+
class Easy
|
4
|
+
|
5
|
+
# This module contains the methods to return informations
|
6
|
+
# from the easy handle. See http://curl.haxx.se/libcurl/c/curl_easy_getinfo.html
|
7
|
+
# for more information.
|
8
|
+
module Informations
|
9
|
+
|
10
|
+
# Holds available informations and their type, which is needed to
|
11
|
+
# request the informations from libcurl.
|
12
|
+
AVAILABLE_INFORMATIONS = {
|
13
|
+
# Return the available HTTP auth methods.
|
14
|
+
httpauth_avail: :long,
|
15
|
+
|
16
|
+
# Return the total time in seconds for the previous
|
17
|
+
# transfer, including name resolution, TCP connection, etc.
|
18
|
+
total_time: :double,
|
19
|
+
|
20
|
+
# Return the time, in seconds, it took from the start
|
21
|
+
# until the first byte was received by libcurl. This
|
22
|
+
# includes pre-transfer time and also the time the
|
23
|
+
# server needs to calculate the result.
|
24
|
+
starttransfer_time: :double,
|
25
|
+
|
26
|
+
# Return the time, in seconds, it took from the start
|
27
|
+
# until the SSL/SSH connect/handshake to the remote
|
28
|
+
# host was completed. This time is most often very near
|
29
|
+
# to the pre-transfer time, except for cases such as HTTP
|
30
|
+
# pipelining where the pre-transfer time can be delayed
|
31
|
+
# due to waits in line for the pipeline and more.
|
32
|
+
appconnect_time: :double,
|
33
|
+
|
34
|
+
# Return the time, in seconds, it took from the start
|
35
|
+
# until the file transfer was just about to begin. This
|
36
|
+
# includes all pre-transfer commands and negotiations
|
37
|
+
# that are specific to the particular protocol(s) involved.
|
38
|
+
# It does not involve the sending of the protocol-
|
39
|
+
# specific request that triggers a transfer.
|
40
|
+
pretransfer_time: :double,
|
41
|
+
|
42
|
+
# Return the time, in seconds, it took from the start
|
43
|
+
# until the connect to the remote host (or proxy) was completed.
|
44
|
+
connect_time: :double,
|
45
|
+
|
46
|
+
# Return the time, in seconds, it took from the
|
47
|
+
# start until the name resolution was completed.
|
48
|
+
namelookup_time: :double,
|
49
|
+
|
50
|
+
# Return the time, in seconds, it took for all redirection steps
|
51
|
+
# include name lookup, connect, pretransfer and transfer before the
|
52
|
+
# final transaction was started. time_redirect shows the complete
|
53
|
+
# execution time for multiple redirections. (Added in 7.12.3)
|
54
|
+
redirect_time: :double,
|
55
|
+
|
56
|
+
# Return the last used effective url.
|
57
|
+
effective_url: :string,
|
58
|
+
|
59
|
+
# Return the string holding the IP address of the most recent
|
60
|
+
# connection done with this curl handle. This string
|
61
|
+
# may be IPv6 if that's enabled.
|
62
|
+
primary_ip: :string,
|
63
|
+
|
64
|
+
# Return the last received HTTP, FTP or SMTP response code.
|
65
|
+
# The value will be zero if no server response code has
|
66
|
+
# been received. Note that a proxy's CONNECT response should
|
67
|
+
# be read with http_connect_code and not this.
|
68
|
+
response_code: :long,
|
69
|
+
|
70
|
+
request_size: :long,
|
71
|
+
|
72
|
+
# Return the total number of redirections that were
|
73
|
+
# actually followed.
|
74
|
+
redirect_count: :long,
|
75
|
+
|
76
|
+
# URL a redirect would take you to, had you enabled redirects (Added in 7.18.2)
|
77
|
+
redirect_url: :string,
|
78
|
+
|
79
|
+
# Return the bytes, the total amount of bytes that were uploaded
|
80
|
+
size_upload: :double,
|
81
|
+
|
82
|
+
# Return the bytes, the total amount of bytes that were downloaded.
|
83
|
+
# The amount is only for the latest transfer and will be reset again
|
84
|
+
# for each new transfer. This counts actual payload data, what's
|
85
|
+
# also commonly called body. All meta and header data are excluded
|
86
|
+
# and will not be counted in this number.
|
87
|
+
size_download: :double,
|
88
|
+
|
89
|
+
# Return the bytes/second, the average upload speed that curl
|
90
|
+
# measured for the complete upload
|
91
|
+
speed_upload: :double,
|
92
|
+
|
93
|
+
# Return the bytes/second, the average download speed that curl
|
94
|
+
# measured for the complete download
|
95
|
+
speed_download: :double
|
96
|
+
}
|
97
|
+
|
98
|
+
AVAILABLE_INFORMATIONS.each do |name, type|
|
99
|
+
define_method(name) do
|
100
|
+
Curl.send("get_info_#{type}".to_sym, name.to_sym, handle)
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
# Returns true if this curl version supports zlib.
|
105
|
+
#
|
106
|
+
# @example Return wether zlib is supported.
|
107
|
+
# easy.supports_zlib?
|
108
|
+
#
|
109
|
+
# @return [ Boolean ] True if supported, else false.
|
110
|
+
# @deprecated Please use the static version instead
|
111
|
+
def supports_zlib?
|
112
|
+
Kernel.warn("EthonImpersonate: Easy#supports_zlib? is deprecated and will be removed, please use Easy#.")
|
113
|
+
Easy.supports_zlib?
|
114
|
+
end
|
115
|
+
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
module EthonImpersonate
|
3
|
+
class Easy
|
4
|
+
class Mirror
|
5
|
+
attr_reader :options
|
6
|
+
alias_method :to_hash, :options
|
7
|
+
|
8
|
+
INFORMATIONS_TO_MIRROR = Informations::AVAILABLE_INFORMATIONS.keys +
|
9
|
+
[:return_code, :response_headers, :response_body, :debug_info]
|
10
|
+
|
11
|
+
INFORMATIONS_TO_LOG = [:effective_url, :response_code, :return_code, :total_time]
|
12
|
+
|
13
|
+
def self.from_easy(easy)
|
14
|
+
options = {}
|
15
|
+
INFORMATIONS_TO_MIRROR.each do |info|
|
16
|
+
options[info] = easy.send(info)
|
17
|
+
end
|
18
|
+
new(options)
|
19
|
+
end
|
20
|
+
|
21
|
+
def initialize(options = {})
|
22
|
+
@options = options
|
23
|
+
end
|
24
|
+
|
25
|
+
def log_informations
|
26
|
+
Hash[*INFORMATIONS_TO_LOG.map do |info|
|
27
|
+
[info, options[info]]
|
28
|
+
end.flatten]
|
29
|
+
end
|
30
|
+
|
31
|
+
INFORMATIONS_TO_MIRROR.each do |info|
|
32
|
+
define_method(info) do
|
33
|
+
options[info]
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
module EthonImpersonate
|
3
|
+
class Easy
|
4
|
+
# This module contains the logic to prepare and perform
|
5
|
+
# an easy.
|
6
|
+
module Operations
|
7
|
+
# Returns a pointer to the curl easy handle.
|
8
|
+
#
|
9
|
+
# @example Return the handle.
|
10
|
+
# easy.handle
|
11
|
+
#
|
12
|
+
# @return [ FFI::Pointer ] A pointer to the curl easy handle.
|
13
|
+
def handle
|
14
|
+
@handle ||= FFI::AutoPointer.new(Curl.easy_init, Curl.method(:easy_cleanup))
|
15
|
+
end
|
16
|
+
|
17
|
+
# Sets a pointer to the curl easy handle.
|
18
|
+
# @param [ ::FFI::Pointer ] Easy handle that will be assigned.
|
19
|
+
def handle=(h)
|
20
|
+
@handle = h
|
21
|
+
end
|
22
|
+
|
23
|
+
# Perform the easy request.
|
24
|
+
#
|
25
|
+
# @example Perform the request.
|
26
|
+
# easy.perform
|
27
|
+
#
|
28
|
+
# @return [ Integer ] The return code.
|
29
|
+
def perform
|
30
|
+
@return_code = Curl.easy_perform(handle)
|
31
|
+
if EthonImpersonate.logger.debug?
|
32
|
+
EthonImpersonate.logger.debug { "ETHON: performed #{log_inspect}" }
|
33
|
+
end
|
34
|
+
complete
|
35
|
+
@return_code
|
36
|
+
end
|
37
|
+
|
38
|
+
# Clean up the easy.
|
39
|
+
#
|
40
|
+
# @example Perform clean up.
|
41
|
+
# easy.cleanup
|
42
|
+
#
|
43
|
+
# @return the result of the free which is nil
|
44
|
+
def cleanup
|
45
|
+
handle.free
|
46
|
+
end
|
47
|
+
|
48
|
+
# Prepare the easy. Options, headers and callbacks
|
49
|
+
# were set.
|
50
|
+
#
|
51
|
+
# @example Prepare easy.
|
52
|
+
# easy.prepare
|
53
|
+
#
|
54
|
+
# @deprecated It is no longer necessary to call prepare.
|
55
|
+
def prepare
|
56
|
+
EthonImpersonate.logger.warn(
|
57
|
+
"ETHON: It is no longer necessary to call "+
|
58
|
+
"Easy#prepare. It's going to be removed "+
|
59
|
+
"in future versions."
|
60
|
+
)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
module EthonImpersonate
|
3
|
+
class Easy
|
4
|
+
|
5
|
+
# This module contains the logic and knowledge about the
|
6
|
+
# available options on easy.
|
7
|
+
module Options
|
8
|
+
attr_reader :url
|
9
|
+
|
10
|
+
def url=(value)
|
11
|
+
@url = value
|
12
|
+
Curl.set_option(:url, value, handle)
|
13
|
+
end
|
14
|
+
|
15
|
+
def escape=( b )
|
16
|
+
@escape = b
|
17
|
+
end
|
18
|
+
|
19
|
+
def escape?
|
20
|
+
return true if !defined?(@escape) || @escape.nil?
|
21
|
+
@escape
|
22
|
+
end
|
23
|
+
|
24
|
+
def multipart=(b)
|
25
|
+
@multipart = b
|
26
|
+
end
|
27
|
+
|
28
|
+
def multipart?
|
29
|
+
!!@multipart
|
30
|
+
end
|
31
|
+
|
32
|
+
Curl.easy_options(nil).each do |opt, props|
|
33
|
+
method_name = "#{opt}=".freeze
|
34
|
+
unless method_defined? method_name
|
35
|
+
define_method(method_name) do |value|
|
36
|
+
Curl.set_option(opt, value, handle)
|
37
|
+
value
|
38
|
+
end
|
39
|
+
end
|
40
|
+
next if props[:type] != :callback || method_defined?(opt)
|
41
|
+
define_method(opt) do |&block|
|
42
|
+
@procs ||= {}
|
43
|
+
@procs[opt.to_sym] = block
|
44
|
+
Curl.set_option(opt, block, handle)
|
45
|
+
nil
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require 'ethon_impersonate/easy/util'
|
3
|
+
require 'ethon_impersonate/easy/queryable'
|
4
|
+
|
5
|
+
module EthonImpersonate
|
6
|
+
class Easy
|
7
|
+
|
8
|
+
# This class represents HTTP request parameters.
|
9
|
+
#
|
10
|
+
# @api private
|
11
|
+
class Params
|
12
|
+
include EthonImpersonate::Easy::Util
|
13
|
+
include EthonImpersonate::Easy::Queryable
|
14
|
+
|
15
|
+
# Create a new Params.
|
16
|
+
#
|
17
|
+
# @example Create a new Params.
|
18
|
+
# Params.new({})
|
19
|
+
#
|
20
|
+
# @param [ Hash ] params The params to use.
|
21
|
+
#
|
22
|
+
# @return [ Params ] A new Params.
|
23
|
+
def initialize(easy, params)
|
24
|
+
@easy = easy
|
25
|
+
@params = params || {}
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,154 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
module EthonImpersonate
|
3
|
+
class Easy
|
4
|
+
|
5
|
+
# This module contains logic about building
|
6
|
+
# query parameters for url or form.
|
7
|
+
module Queryable
|
8
|
+
|
9
|
+
# :nodoc:
|
10
|
+
def self.included(base)
|
11
|
+
base.send(:attr_accessor, :escape)
|
12
|
+
base.send(:attr_accessor, :params_encoding)
|
13
|
+
end
|
14
|
+
|
15
|
+
# Return wether there are elements in params or not.
|
16
|
+
#
|
17
|
+
# @example Return if params is empty.
|
18
|
+
# form.empty?
|
19
|
+
#
|
20
|
+
# @return [ Boolean ] True if params is empty, else false.
|
21
|
+
def empty?
|
22
|
+
@params.empty?
|
23
|
+
end
|
24
|
+
|
25
|
+
# Return the string representation of params.
|
26
|
+
#
|
27
|
+
# @example Return string representation.
|
28
|
+
# params.to_s
|
29
|
+
#
|
30
|
+
# @return [ String ] The string representation.
|
31
|
+
def to_s
|
32
|
+
@to_s ||= query_pairs.map{ |pair|
|
33
|
+
return pair if pair.is_a?(String)
|
34
|
+
|
35
|
+
if escape && @easy
|
36
|
+
pair.map{ |e| @easy.escape(e.to_s) }.join("=")
|
37
|
+
else
|
38
|
+
pair.join("=")
|
39
|
+
end
|
40
|
+
}.join('&')
|
41
|
+
end
|
42
|
+
|
43
|
+
# Return the query pairs.
|
44
|
+
#
|
45
|
+
# @example Return the query pairs.
|
46
|
+
# params.query_pairs
|
47
|
+
#
|
48
|
+
# @return [ Array ] The query pairs.
|
49
|
+
def query_pairs
|
50
|
+
@query_pairs ||= build_query_pairs(@params)
|
51
|
+
end
|
52
|
+
|
53
|
+
# Return query pairs build from a hash.
|
54
|
+
#
|
55
|
+
# @example Build query pairs.
|
56
|
+
# action.build_query_pairs({a: 1, b: 2})
|
57
|
+
# #=> [[:a, 1], [:b, 2]]
|
58
|
+
#
|
59
|
+
# @param [ Hash ] hash The hash to go through.
|
60
|
+
#
|
61
|
+
# @return [ Array ] The array of query pairs.
|
62
|
+
def build_query_pairs(hash)
|
63
|
+
return [hash] if hash.is_a?(String)
|
64
|
+
|
65
|
+
pairs = []
|
66
|
+
recursively_generate_pairs(hash, nil, pairs)
|
67
|
+
pairs
|
68
|
+
end
|
69
|
+
|
70
|
+
# Return file info for a file.
|
71
|
+
#
|
72
|
+
# @example Return file info.
|
73
|
+
# action.file_info(File.open('fubar', 'r'))
|
74
|
+
#
|
75
|
+
# @param [ File ] file The file to handle.
|
76
|
+
#
|
77
|
+
# @return [ Array ] Array of informations.
|
78
|
+
def file_info(file)
|
79
|
+
filename = File.basename(file.path)
|
80
|
+
[
|
81
|
+
filename,
|
82
|
+
mime_type(filename),
|
83
|
+
File.expand_path(file.path)
|
84
|
+
]
|
85
|
+
end
|
86
|
+
|
87
|
+
private
|
88
|
+
|
89
|
+
def mime_type(filename)
|
90
|
+
if defined?(MIME) && t = MIME::Types.type_for(filename).first
|
91
|
+
t.to_s
|
92
|
+
else
|
93
|
+
'application/octet-stream'
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
def recursively_generate_pairs(h, prefix, pairs)
|
98
|
+
case h
|
99
|
+
when Hash
|
100
|
+
encode_hash_pairs(h, prefix, pairs)
|
101
|
+
when Array
|
102
|
+
if params_encoding == :rack
|
103
|
+
encode_rack_array_pairs(h, prefix, pairs)
|
104
|
+
elsif params_encoding == :multi
|
105
|
+
encode_multi_array_pairs(h, prefix, pairs)
|
106
|
+
elsif params_encoding == :none
|
107
|
+
pairs << [prefix, h]
|
108
|
+
else
|
109
|
+
encode_indexed_array_pairs(h, prefix, pairs)
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
def encode_hash_pairs(h, prefix, pairs)
|
115
|
+
h.each_pair do |k,v|
|
116
|
+
key = prefix.nil? ? k : "#{prefix}[#{k}]"
|
117
|
+
pairs_for(v, key, pairs)
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
def encode_indexed_array_pairs(h, prefix, pairs)
|
122
|
+
h.each_with_index do |v, i|
|
123
|
+
key = "#{prefix}[#{i}]"
|
124
|
+
pairs_for(v, key, pairs)
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
def encode_rack_array_pairs(h, prefix, pairs)
|
129
|
+
h.each do |v|
|
130
|
+
key = "#{prefix}[]"
|
131
|
+
pairs_for(v, key, pairs)
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
def encode_multi_array_pairs(h, prefix, pairs)
|
136
|
+
h.each_with_index do |v, i|
|
137
|
+
key = prefix
|
138
|
+
pairs_for(v, key, pairs)
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
def pairs_for(v, key, pairs)
|
143
|
+
case v
|
144
|
+
when Hash, Array
|
145
|
+
recursively_generate_pairs(v, key, pairs)
|
146
|
+
when File, Tempfile
|
147
|
+
pairs << [key, file_info(v)]
|
148
|
+
else
|
149
|
+
pairs << [key, v]
|
150
|
+
end
|
151
|
+
end
|
152
|
+
end
|
153
|
+
end
|
154
|
+
end
|
@@ -0,0 +1,136 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
module EthonImpersonate
|
3
|
+
class Easy
|
4
|
+
|
5
|
+
# This module contains the logic for the response callbacks.
|
6
|
+
# The on_complete callback is the only one at the moment.
|
7
|
+
#
|
8
|
+
# You can set multiple callbacks, which are then executed
|
9
|
+
# in the same order.
|
10
|
+
#
|
11
|
+
# easy.on_complete { p 1 }
|
12
|
+
# easy.on_complete { p 2 }
|
13
|
+
# easy.complete
|
14
|
+
# #=> 1
|
15
|
+
# #=> 2
|
16
|
+
#
|
17
|
+
# You can clear the callbacks:
|
18
|
+
#
|
19
|
+
# easy.on_complete { p 1 }
|
20
|
+
# easy.on_complete { p 2 }
|
21
|
+
# easy.on_complete.clear
|
22
|
+
# easy.on_complete
|
23
|
+
# #=> []
|
24
|
+
module ResponseCallbacks
|
25
|
+
|
26
|
+
# Set on_headers callback.
|
27
|
+
#
|
28
|
+
# @example Set on_headers.
|
29
|
+
# request.on_headers { p "yay" }
|
30
|
+
#
|
31
|
+
# @param [ Block ] block The block to execute.
|
32
|
+
def on_headers(&block)
|
33
|
+
@on_headers ||= []
|
34
|
+
@on_headers << block if block_given?
|
35
|
+
@on_headers
|
36
|
+
end
|
37
|
+
|
38
|
+
# Execute on_headers callbacks.
|
39
|
+
#
|
40
|
+
# @example Execute on_headers.
|
41
|
+
# request.headers
|
42
|
+
def headers
|
43
|
+
return if @headers_called
|
44
|
+
@headers_called = true
|
45
|
+
if defined?(@on_headers) and not @on_headers.nil?
|
46
|
+
result = nil
|
47
|
+
@on_headers.each do |callback|
|
48
|
+
result = callback.call(self)
|
49
|
+
break if result == :abort
|
50
|
+
end
|
51
|
+
result
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
# Set on_complete callback.
|
56
|
+
#
|
57
|
+
# @example Set on_complete.
|
58
|
+
# request.on_complete { p "yay" }
|
59
|
+
#
|
60
|
+
# @param [ Block ] block The block to execute.
|
61
|
+
def on_complete(&block)
|
62
|
+
@on_complete ||= []
|
63
|
+
@on_complete << block if block_given?
|
64
|
+
@on_complete
|
65
|
+
end
|
66
|
+
|
67
|
+
# Execute on_complete callbacks.
|
68
|
+
#
|
69
|
+
# @example Execute on_completes.
|
70
|
+
# request.complete
|
71
|
+
def complete
|
72
|
+
headers unless @response_headers.empty?
|
73
|
+
if defined?(@on_complete) and not @on_complete.nil?
|
74
|
+
@on_complete.each{ |callback| callback.call(self) }
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
# Set on_progress callback.
|
79
|
+
#
|
80
|
+
# @example Set on_progress.
|
81
|
+
# request.on_progress {|dltotal, dlnow, ultotal, ulnow| p "#{dltotal} #{dlnow} #{ultotal} #{ulnow}" }
|
82
|
+
#
|
83
|
+
# @param [ Block ] block The block to execute.
|
84
|
+
def on_progress(&block)
|
85
|
+
@on_progress ||= []
|
86
|
+
if block_given?
|
87
|
+
@on_progress << block
|
88
|
+
set_progress_callback
|
89
|
+
self.noprogress = 0
|
90
|
+
end
|
91
|
+
@on_progress
|
92
|
+
end
|
93
|
+
|
94
|
+
# Execute on_progress callbacks.
|
95
|
+
#
|
96
|
+
# @example Execute on_progress.
|
97
|
+
# request.body(1, 1, 1, 1)
|
98
|
+
def progress(dltotal, dlnow, ultotal, ulnow)
|
99
|
+
if defined?(@on_progress) and not @on_progress.nil?
|
100
|
+
@on_progress.each{ |callback| callback.call(dltotal, dlnow, ultotal, ulnow) }
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
# Set on_body callback.
|
105
|
+
#
|
106
|
+
# @example Set on_body.
|
107
|
+
# request.on_body { |chunk| p "yay" }
|
108
|
+
#
|
109
|
+
# @param [ Block ] block The block to execute.
|
110
|
+
def on_body(&block)
|
111
|
+
@on_body ||= []
|
112
|
+
@on_body << block if block_given?
|
113
|
+
@on_body
|
114
|
+
end
|
115
|
+
|
116
|
+
# Execute on_body callbacks.
|
117
|
+
#
|
118
|
+
# @example Execute on_body.
|
119
|
+
# request.body("This data came from HTTP.")
|
120
|
+
#
|
121
|
+
# @return [ Object ] If there are no on_body callbacks, returns the symbol :unyielded.
|
122
|
+
def body(chunk)
|
123
|
+
if defined?(@on_body) and not @on_body.nil?
|
124
|
+
result = nil
|
125
|
+
@on_body.each do |callback|
|
126
|
+
result = callback.call(chunk, self)
|
127
|
+
break if result == :abort
|
128
|
+
end
|
129
|
+
result
|
130
|
+
else
|
131
|
+
:unyielded
|
132
|
+
end
|
133
|
+
end
|
134
|
+
end
|
135
|
+
end
|
136
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
module EthonImpersonate
|
3
|
+
class Easy # :nodoc:
|
4
|
+
|
5
|
+
# This module contains small helpers.
|
6
|
+
#
|
7
|
+
# @api private
|
8
|
+
module Util
|
9
|
+
|
10
|
+
# Escapes zero bytes in strings.
|
11
|
+
#
|
12
|
+
# @example Escape zero bytes.
|
13
|
+
# Util.escape_zero_byte("1\0")
|
14
|
+
# #=> "1\\0"
|
15
|
+
#
|
16
|
+
# @param [ Object ] value The value to escape.
|
17
|
+
#
|
18
|
+
# @return [ String, Object ] Escaped String if
|
19
|
+
# zero byte found, original object if not.
|
20
|
+
def escape_zero_byte(value)
|
21
|
+
return value unless value.to_s.include?(0.chr)
|
22
|
+
value.to_s.gsub(0.chr, '\\\0')
|
23
|
+
end
|
24
|
+
|
25
|
+
extend self
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|