scnr-ethon 0.15.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (60) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +379 -0
  3. data/LICENSE +20 -0
  4. data/README.md +118 -0
  5. data/ethon.gemspec +29 -0
  6. data/lib/ethon/curl.rb +90 -0
  7. data/lib/ethon/curls/classes.rb +65 -0
  8. data/lib/ethon/curls/codes.rb +122 -0
  9. data/lib/ethon/curls/constants.rb +80 -0
  10. data/lib/ethon/curls/form_options.rb +37 -0
  11. data/lib/ethon/curls/functions.rb +58 -0
  12. data/lib/ethon/curls/infos.rb +151 -0
  13. data/lib/ethon/curls/messages.rb +19 -0
  14. data/lib/ethon/curls/options.rb +503 -0
  15. data/lib/ethon/curls/settings.rb +12 -0
  16. data/lib/ethon/easy/callbacks.rb +149 -0
  17. data/lib/ethon/easy/debug_info.rb +47 -0
  18. data/lib/ethon/easy/features.rb +31 -0
  19. data/lib/ethon/easy/form.rb +107 -0
  20. data/lib/ethon/easy/header.rb +61 -0
  21. data/lib/ethon/easy/http/actionable.rb +157 -0
  22. data/lib/ethon/easy/http/custom.rb +29 -0
  23. data/lib/ethon/easy/http/delete.rb +25 -0
  24. data/lib/ethon/easy/http/get.rb +24 -0
  25. data/lib/ethon/easy/http/head.rb +24 -0
  26. data/lib/ethon/easy/http/options.rb +24 -0
  27. data/lib/ethon/easy/http/patch.rb +24 -0
  28. data/lib/ethon/easy/http/post.rb +26 -0
  29. data/lib/ethon/easy/http/postable.rb +32 -0
  30. data/lib/ethon/easy/http/put.rb +27 -0
  31. data/lib/ethon/easy/http/putable.rb +25 -0
  32. data/lib/ethon/easy/http.rb +68 -0
  33. data/lib/ethon/easy/informations.rb +116 -0
  34. data/lib/ethon/easy/mirror.rb +36 -0
  35. data/lib/ethon/easy/operations.rb +65 -0
  36. data/lib/ethon/easy/options.rb +50 -0
  37. data/lib/ethon/easy/params.rb +29 -0
  38. data/lib/ethon/easy/queryable.rb +154 -0
  39. data/lib/ethon/easy/response_callbacks.rb +136 -0
  40. data/lib/ethon/easy/util.rb +28 -0
  41. data/lib/ethon/easy.rb +315 -0
  42. data/lib/ethon/errors/ethon_error.rb +9 -0
  43. data/lib/ethon/errors/global_init.rb +13 -0
  44. data/lib/ethon/errors/invalid_option.rb +13 -0
  45. data/lib/ethon/errors/invalid_value.rb +13 -0
  46. data/lib/ethon/errors/multi_add.rb +12 -0
  47. data/lib/ethon/errors/multi_fdset.rb +12 -0
  48. data/lib/ethon/errors/multi_remove.rb +12 -0
  49. data/lib/ethon/errors/multi_timeout.rb +13 -0
  50. data/lib/ethon/errors/select.rb +13 -0
  51. data/lib/ethon/errors.rb +17 -0
  52. data/lib/ethon/libc.rb +21 -0
  53. data/lib/ethon/loggable.rb +59 -0
  54. data/lib/ethon/multi/operations.rb +228 -0
  55. data/lib/ethon/multi/options.rb +117 -0
  56. data/lib/ethon/multi/stack.rb +49 -0
  57. data/lib/ethon/multi.rb +126 -0
  58. data/lib/ethon/version.rb +6 -0
  59. data/lib/ethon.rb +36 -0
  60. metadata +117 -0
@@ -0,0 +1,47 @@
1
+ # frozen_string_literal: true
2
+ module Ethon
3
+ class Easy
4
+
5
+ # This class is used to store and retreive debug information,
6
+ # which is only saved when verbose is set to true.
7
+ #
8
+ # @api private
9
+ class DebugInfo
10
+
11
+ MESSAGE_TYPES = Ethon::Curl::DebugInfoType.to_h.keys
12
+
13
+ class Message
14
+ attr_reader :type, :message
15
+
16
+ def initialize(type, message)
17
+ @type = type
18
+ @message = message
19
+ end
20
+ end
21
+
22
+ def initialize
23
+ @messages = []
24
+ end
25
+
26
+ def add(type, message)
27
+ @messages << Message.new(type, message)
28
+ end
29
+
30
+ def messages_for(type)
31
+ @messages.select {|m| m.type == type }.map(&:message)
32
+ end
33
+
34
+ MESSAGE_TYPES.each do |type|
35
+ eval %Q|def #{type}; messages_for(:#{type}); end|
36
+ end
37
+
38
+ def to_a
39
+ @messages.map(&:message)
40
+ end
41
+
42
+ def to_h
43
+ Hash[MESSAGE_TYPES.map {|k| [k, send(k)] }]
44
+ end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+ module Ethon
3
+ class Easy
4
+
5
+ # This module contains class methods for feature checks
6
+ module Features
7
+ # Returns true if this curl version supports zlib.
8
+ #
9
+ # @example Return wether zlib is supported.
10
+ # Ethon::Easy.supports_zlib?
11
+ #
12
+ # @return [ Boolean ] True if supported, else false.
13
+ def supports_zlib?
14
+ !!(Curl.version_info[:features] & Curl::VERSION_LIBZ)
15
+ end
16
+
17
+ # Returns true if this curl version supports AsynchDNS.
18
+ #
19
+ # @example
20
+ # Ethon::Easy.supports_asynch_dns?
21
+ #
22
+ # @return [ Boolean ] True if supported, else false.
23
+ def supports_asynch_dns?
24
+ !!(Curl.version_info[:features] & Curl::VERSION_ASYNCHDNS)
25
+ end
26
+
27
+ alias :supports_timeout_ms? :supports_asynch_dns?
28
+
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,107 @@
1
+ # frozen_string_literal: true
2
+ require 'ethon/easy/util'
3
+ require 'ethon/easy/queryable'
4
+
5
+ module Ethon
6
+ class Easy
7
+
8
+ # This class represents a form and is used to send a payload in the
9
+ # request body via POST/PUT.
10
+ # It handles multipart forms, too.
11
+ #
12
+ # @api private
13
+ class Form
14
+ include Ethon::Easy::Util
15
+ include Ethon::Easy::Queryable
16
+
17
+ # Return a new Form.
18
+ #
19
+ # @example Return a new Form.
20
+ # Form.new({})
21
+ #
22
+ # @param [ Hash ] params The parameter with which to initialize the form.
23
+ #
24
+ # @return [ Form ] A new Form.
25
+ def initialize(easy, params, multipart = nil)
26
+ @easy = easy
27
+ @params = params || {}
28
+ @multipart = multipart
29
+ end
30
+
31
+ # Return a pointer to the first form element in libcurl.
32
+ #
33
+ # @example Return the first form element.
34
+ # form.first
35
+ #
36
+ # @return [ FFI::Pointer ] The first element.
37
+ def first
38
+ @first ||= FFI::MemoryPointer.new(:pointer)
39
+ end
40
+
41
+ # Return a pointer to the last form element in libcurl.
42
+ #
43
+ # @example Return the last form element.
44
+ # form.last
45
+ #
46
+ # @return [ FFI::Pointer ] The last element.
47
+ def last
48
+ @last ||= FFI::MemoryPointer.new(:pointer)
49
+ end
50
+
51
+ # Return if form is multipart. The form is multipart
52
+ # when it contains a file or multipart option is set on the form during creation.
53
+ #
54
+ # @example Return if form is multipart.
55
+ # form.multipart?
56
+ #
57
+ # @return [ Boolean ] True if form is multipart, else false.
58
+ def multipart?
59
+ return true if @multipart
60
+ query_pairs.any?{|pair| pair.respond_to?(:last) && pair.last.is_a?(Array)}
61
+ end
62
+
63
+ # Add form elements to libcurl.
64
+ #
65
+ # @example Add form to libcurl.
66
+ # form.materialize
67
+ def materialize
68
+ query_pairs.each { |pair| form_add(pair.first.to_s, pair.last) }
69
+ end
70
+
71
+ private
72
+
73
+ def form_add(name, content)
74
+ case content
75
+ when Array
76
+ Curl.formadd(first, last,
77
+ :form_option, :copyname, :pointer, name,
78
+ :form_option, :namelength, :long, name.bytesize,
79
+ :form_option, :file, :string, content[2],
80
+ :form_option, :filename, :string, content[0],
81
+ :form_option, :contenttype, :string, content[1],
82
+ :form_option, :end
83
+ )
84
+ else
85
+ Curl.formadd(first, last,
86
+ :form_option, :copyname, :pointer, name,
87
+ :form_option, :namelength, :long, name.bytesize,
88
+ :form_option, :copycontents, :pointer, content.to_s,
89
+ :form_option, :contentslength, :long, content ? content.to_s.bytesize : 0,
90
+ :form_option, :end
91
+ )
92
+ end
93
+
94
+ setup_garbage_collection
95
+ end
96
+
97
+ def setup_garbage_collection
98
+ # first is a pointer to a pointer. Since it's a MemoryPointer it will
99
+ # auto clean itself up, but we need to clean up the object it points
100
+ # to. So this results in (pseudo-c):
101
+ # form_data_cleanup_handler = *first
102
+ # curl_form_free(form_data_cleanup_handler)
103
+ @form_data_cleanup_handler ||= FFI::AutoPointer.new(@first.get_pointer(0), Curl.method(:formfree))
104
+ end
105
+ end
106
+ end
107
+ end
@@ -0,0 +1,61 @@
1
+ # frozen_string_literal: true
2
+ module Ethon
3
+ class Easy
4
+ # This module contains the logic around adding headers to libcurl.
5
+ #
6
+ # @api private
7
+ module Header
8
+ # Return headers, return empty hash if none.
9
+ #
10
+ # @example Return the headers.
11
+ # easy.headers
12
+ #
13
+ # @return [ Hash ] The headers.
14
+ def headers
15
+ @headers ||= {}
16
+ end
17
+
18
+ # Set the headers.
19
+ #
20
+ # @example Set the headers.
21
+ # easy.headers = {'User-Agent' => 'ethon'}
22
+ #
23
+ # @param [ Hash ] headers The headers.
24
+ def headers=(headers)
25
+ headers ||= {}
26
+ header_list = nil
27
+ headers.each do |k, v|
28
+ header_list = Curl.slist_append(header_list, compose_header(k,v))
29
+ end
30
+ Curl.set_option(:httpheader, header_list, handle)
31
+
32
+ @header_list = header_list && FFI::AutoPointer.new(header_list, Curl.method(:slist_free_all))
33
+ end
34
+
35
+ # Return header_list.
36
+ #
37
+ # @example Return header_list.
38
+ # easy.header_list
39
+ #
40
+ # @return [ FFI::Pointer ] The header list.
41
+ def header_list
42
+ @header_list
43
+ end
44
+
45
+ # Compose libcurl header string from key and value.
46
+ # Also replaces null bytes, because libcurl will complain
47
+ # otherwise.
48
+ #
49
+ # @example Compose header.
50
+ # easy.compose_header('User-Agent', 'Ethon')
51
+ #
52
+ # @param [ String ] key The header name.
53
+ # @param [ String ] value The header value.
54
+ #
55
+ # @return [ String ] The composed header.
56
+ def compose_header(key, value)
57
+ Util.escape_zero_byte("#{key}: #{value}")
58
+ end
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,157 @@
1
+ # frozen_string_literal: true
2
+ require 'ethon/easy/http/putable'
3
+ require 'ethon/easy/http/postable'
4
+
5
+ module Ethon
6
+ class Easy
7
+ module Http
8
+ # This module represents a Http Action and is a factory
9
+ # for more real actions like GET, HEAD, POST and PUT.
10
+ module Actionable
11
+
12
+ QUERY_OPTIONS = [ :params, :body, :params_encoding ]
13
+
14
+ # Create a new action.
15
+ #
16
+ # @example Create a new action.
17
+ # Action.new("www.example.com", {})
18
+ #
19
+ # @param [ String ] url The url.
20
+ # @param [ Hash ] options The options.
21
+ #
22
+ # @return [ Action ] A new action.
23
+ def initialize(url, options)
24
+ @url = url
25
+ @options, @query_options = parse_options(options)
26
+ end
27
+
28
+ # Return the url.
29
+ #
30
+ # @example Return url.
31
+ # action.url
32
+ #
33
+ # @return [ String ] The url.
34
+ def url
35
+ @url
36
+ end
37
+
38
+ # Return the options hash.
39
+ #
40
+ # @example Return options.
41
+ # action.options
42
+ #
43
+ # @return [ Hash ] The options.
44
+ def options
45
+ @options
46
+ end
47
+
48
+ # Returns the query options hash.
49
+ #
50
+ # @example Return query options.
51
+ # action.query_options
52
+ #
53
+ # @return [ Hash ] The query options.
54
+ def query_options
55
+ @query_options
56
+ end
57
+
58
+ # Return the params.
59
+ #
60
+ # @example Return params.
61
+ # action.params
62
+ #
63
+ # @return [ Params ] The params.
64
+ def params
65
+ @params ||= Params.new(@easy, query_options.fetch(:params, nil))
66
+ end
67
+
68
+ # Return the form.
69
+ #
70
+ # @example Return form.
71
+ # action.form
72
+ #
73
+ # @return [ Form ] The form.
74
+ def form
75
+ @form ||= Form.new(@easy, query_options.fetch(:body, nil), options.fetch(:multipart, nil))
76
+ end
77
+
78
+ # Get the requested array encoding. By default it's
79
+ # :typhoeus, but it can also be set to :rack.
80
+ #
81
+ # @example Get encoding from options
82
+ # action.params_encoding
83
+ #
84
+ def params_encoding
85
+ @params_encoding ||= query_options.fetch(:params_encoding, :typhoeus)
86
+ end
87
+
88
+ # Setup everything necessary for a proper request.
89
+ #
90
+ # @example setup.
91
+ # action.setup(easy)
92
+ #
93
+ # @param [ easy ] easy the easy to setup.
94
+ def setup(easy)
95
+ @easy = easy
96
+
97
+ # Order is important, @easy will be used to provide access to options
98
+ # relevant to the following operations (like whether or not to escape
99
+ # values).
100
+ easy.set_attributes(options)
101
+
102
+ set_form(easy) unless form.empty?
103
+
104
+ if params.empty?
105
+ easy.url = url
106
+ else
107
+ set_params(easy)
108
+ end
109
+ end
110
+
111
+ # Setup request with params.
112
+ #
113
+ # @example Setup nothing.
114
+ # action.set_params(easy)
115
+ #
116
+ # @param [ Easy ] easy The easy to setup.
117
+ def set_params(easy)
118
+ params.escape = easy.escape?
119
+ params.params_encoding = params_encoding
120
+
121
+ base_url, base_params = url.split('?')
122
+ base_url << '?'
123
+ base_url << base_params.to_s
124
+ base_url << '&' if base_params
125
+ base_url << params.to_s
126
+
127
+ easy.url = base_url
128
+ end
129
+
130
+ # Setup request with form.
131
+ #
132
+ # @example Setup nothing.
133
+ # action.set_form(easy)
134
+ #
135
+ # @param [ Easy ] easy The easy to setup.
136
+ def set_form(easy)
137
+ end
138
+
139
+ private
140
+
141
+ def parse_options(options)
142
+ query_options = {}
143
+ options = options.dup
144
+
145
+ QUERY_OPTIONS.each do |query_option|
146
+ if options.key?(query_option)
147
+ query_options[query_option] = options.delete(query_option)
148
+ end
149
+ end
150
+
151
+ return options, query_options
152
+ end
153
+ end
154
+ end
155
+ end
156
+ end
157
+
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+ module Ethon
3
+ class Easy
4
+ module Http
5
+
6
+ # This class knows everything about making requests for custom HTTP verbs.
7
+ class Custom
8
+ include Ethon::Easy::Http::Actionable
9
+ include Ethon::Easy::Http::Postable
10
+
11
+ def initialize(verb, url, options)
12
+ @verb = verb
13
+ super(url, options)
14
+ end
15
+
16
+ # Setup easy to make a request.
17
+ #
18
+ # @example Setup.
19
+ # custom.set_params(easy)
20
+ #
21
+ # @param [ Easy ] easy The easy to setup.
22
+ def setup(easy)
23
+ super
24
+ easy.customrequest = @verb
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+ module Ethon
3
+ class Easy
4
+ module Http
5
+
6
+ # This class knows everything about making DELETE requests.
7
+ class Delete
8
+ include Ethon::Easy::Http::Actionable
9
+ include Ethon::Easy::Http::Postable
10
+
11
+ # Setup easy to make a DELETE request.
12
+ #
13
+ # @example Setup customrequest.
14
+ # delete.setup(easy)
15
+ #
16
+ # @param [ Easy ] easy The easy to setup.
17
+ def setup(easy)
18
+ super
19
+ easy.customrequest = "DELETE"
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
25
+
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+ module Ethon
3
+ class Easy
4
+ module Http
5
+
6
+ # This class knows everything about making GET requests.
7
+ class Get
8
+ include Ethon::Easy::Http::Actionable
9
+ include Ethon::Easy::Http::Postable
10
+
11
+ # Setup easy to make a GET request.
12
+ #
13
+ # @example Setup.
14
+ # get.set_params(easy)
15
+ #
16
+ # @param [ Easy ] easy The easy to setup.
17
+ def setup(easy)
18
+ super
19
+ easy.customrequest = "GET" unless form.empty?
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+ module Ethon
3
+ class Easy
4
+ module Http
5
+
6
+ # This class knows everything about making HEAD requests.
7
+ class Head
8
+ include Ethon::Easy::Http::Actionable
9
+ include Ethon::Easy::Http::Postable
10
+
11
+ # Setup easy to make a HEAD request.
12
+ #
13
+ # @example Setup.
14
+ # get.set_params(easy)
15
+ #
16
+ # @param [ Easy ] easy The easy to setup.
17
+ def setup(easy)
18
+ super
19
+ easy.nobody = true
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+ module Ethon
3
+ class Easy
4
+ module Http
5
+
6
+ # This class knows everything about making OPTIONS requests.
7
+ class Options
8
+ include Ethon::Easy::Http::Actionable
9
+ include Ethon::Easy::Http::Postable
10
+
11
+ # Setup easy to make a OPTIONS request.
12
+ #
13
+ # @example Setup.
14
+ # options.setup(easy)
15
+ #
16
+ # @param [ Easy ] easy The easy to setup.
17
+ def setup(easy)
18
+ super
19
+ easy.customrequest = "OPTIONS"
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+ module Ethon
3
+ class Easy
4
+ module Http
5
+
6
+ # This class knows everything about making PATCH requests.
7
+ class Patch
8
+ include Ethon::Easy::Http::Actionable
9
+ include Ethon::Easy::Http::Postable
10
+
11
+ # Setup easy to make a PATCH request.
12
+ #
13
+ # @example Setup.
14
+ # patch.setup(easy)
15
+ #
16
+ # @param [ Easy ] easy The easy to setup.
17
+ def setup(easy)
18
+ super
19
+ easy.customrequest = "PATCH"
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+ module Ethon
3
+ class Easy
4
+ module Http
5
+ # This class knows everything about making POST requests.
6
+ class Post
7
+ include Ethon::Easy::Http::Actionable
8
+ include Ethon::Easy::Http::Postable
9
+
10
+ # Setup easy to make a POST request.
11
+ #
12
+ # @example Setup.
13
+ # post.setup(easy)
14
+ #
15
+ # @param [ Easy ] easy The easy to setup.
16
+ def setup(easy)
17
+ super
18
+ if form.empty?
19
+ easy.postfieldsize = 0
20
+ easy.copypostfields = ""
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+ module Ethon
3
+ class Easy
4
+ module Http
5
+
6
+ # This module contains logic for setting up a [multipart] POST body.
7
+ module Postable
8
+
9
+ # Set things up when form is provided.
10
+ # Deals with multipart forms.
11
+ #
12
+ # @example Setup.
13
+ # post.set_form(easy)
14
+ #
15
+ # @param [ Easy ] easy The easy to setup.
16
+ def set_form(easy)
17
+ easy.url ||= url
18
+ form.params_encoding = params_encoding
19
+ if form.multipart?
20
+ form.escape = false
21
+ form.materialize
22
+ easy.httppost = form.first.read_pointer
23
+ else
24
+ form.escape = easy.escape?
25
+ easy.postfieldsize = form.to_s.bytesize
26
+ easy.copypostfields = form.to_s
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+ module Ethon
3
+ class Easy
4
+ module Http
5
+
6
+ # This class knows everything about making PUT requests.
7
+ class Put
8
+ include Ethon::Easy::Http::Actionable
9
+ include Ethon::Easy::Http::Putable
10
+
11
+ # Setup easy to make a PUT request.
12
+ #
13
+ # @example Setup.
14
+ # put.setup(easy)
15
+ #
16
+ # @param [ Easy ] easy The easy to setup.
17
+ def setup(easy)
18
+ super
19
+ if form.empty?
20
+ easy.upload = true
21
+ easy.infilesize = 0
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+ module Ethon
3
+ class Easy
4
+ module Http
5
+
6
+ # This module contains logic about setting up a PUT body.
7
+ module Putable
8
+ # Set things up when form is provided.
9
+ # Deals with multipart forms.
10
+ #
11
+ # @example Setup.
12
+ # put.set_form(easy)
13
+ #
14
+ # @param [ Easy ] easy The easy to setup.
15
+ def set_form(easy)
16
+ easy.upload = true
17
+ form.escape = true
18
+ form.params_encoding = params_encoding
19
+ easy.infilesize = form.to_s.bytesize
20
+ easy.set_read_callback(form.to_s)
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end