Module: Curl

Defined in:
lib/curl.rb,
lib/curl/easy.rb,
lib/curl/multi.rb,
ext/curb.c

Overview

expose shortcut methods

Defined Under Namespace

Modules: Err Classes: Easy, Multi, PostField, Upload

Constant Summary collapse

CURB_VERSION =
rb_str_new2(CURB_VERSION)
VERSION =
curlver
CURL_VERSION =
curlver
VERNUM =
curlvernum
CURL_VERNUM =
curlvernum
LONG_VERSION =
curllongver
CURL_LONG_VERSION =
curllongver
CURLINFO_TEXT =

Passed to on_debug handler to indicate that the data is informational text.

LONG2NUM(CURLINFO_TEXT)
CURLINFO_HEADER_IN =

Passed to on_debug handler to indicate that the data is header (or header-like) data received from the peer.

LONG2NUM(CURLINFO_HEADER_IN)
CURLINFO_HEADER_OUT =

Passed to on_debug handler to indicate that the data is header (or header-like) data sent to the peer.

LONG2NUM(CURLINFO_HEADER_OUT)
CURLINFO_DATA_IN =

Passed to on_debug handler to indicate that the data is protocol data received from the peer.

LONG2NUM(CURLINFO_DATA_IN)
CURLINFO_DATA_OUT =

Passed to on_debug handler to indicate that the data is protocol data sent to the peer.

LONG2NUM(CURLINFO_DATA_OUT)
CURL_MULTICWD =
LONG2NUM(CURLFTPMETHOD_MULTICWD)
CURL_NOCWD =
LONG2NUM(CURLFTPMETHOD_NOCWD)
CURL_SINGLECWD =
LONG2NUM(CURLFTPMETHOD_SINGLECWD)
CURLPROXY_HTTP =
LONG2NUM(-1)
CURL_SSLVERSION_DEFAULT =
LONG2NUM(-1)
CURL_SSLVERSION_MAX_DEFAULT =
LONG2NUM(CURL_SSLVERSION_MAX_DEFAULT)
CURL_SSLVERSION_TLSv1 =
LONG2NUM(-1)
CURL_SSLVERSION_SSLv2 =
LONG2NUM(-1)
CURL_SSLVERSION_SSLv3 =
LONG2NUM(-1)
CURL_SSLVERSION_TLSv1_0 =
LONG2NUM(-1)
CURL_SSLVERSION_MAX_TLSv1_0 =
LONG2NUM(-1)
CURL_SSLVERSION_TLSv1_1 =
LONG2NUM(-1)
CURL_SSLVERSION_MAX_TLSv1_1 =
LONG2NUM(-1)
CURL_SSLVERSION_TLSv1_2 =
LONG2NUM(-1)
CURL_SSLVERSION_MAX_TLSv1_2 =
LONG2NUM(-1)
CURL_SSLVERSION_TLSv1_3 =
LONG2NUM(-1)
CURL_SSLVERSION_MAX_TLSv1_3 =
LONG2NUM(-1)
CURL_USESSL_CONTROL =
LONG2NUM(-1)
CURL_USESSL_NONE =
LONG2NUM(-1)
CURL_USESSL_TRY =
LONG2NUM(-1)
CURL_USESSL_ALL =
LONG2NUM(-1)
CURLPROXY_SOCKS4 =
LONG2NUM(-2)
CURLPROXY_SOCKS4A =
LONG2NUM(-2)
CURLPROXY_SOCKS5 =
LONG2NUM(-2)
CURLPROXY_SOCKS5_HOSTNAME =
LONG2NUM(-2)
CURLAUTH_BASIC =
LONG2NUM(0)
CURLAUTH_DIGEST =
LONG2NUM(0)
CURLAUTH_GSSNEGOTIATE =
LONG2NUM(0)
CURLAUTH_NTLM =
LONG2NUM(0)
CURLAUTH_ANYSAFE =
LONG2NUM(0)
CURLAUTH_ANY =
LONG2NUM(0)
PIPE_NOTHING =
LONG2NUM(CURLPIPE_NOTHING)
PIPE_HTTP1 =
LONG2NUM(CURLPIPE_HTTP1)
PIPE_MULTIPLEX =
LONG2NUM(CURLPIPE_MULTIPLEX)
HTTP_2_0 =
LONG2NUM(CURL_HTTP_VERSION_2_0)
HTTP_2TLS =
LONG2NUM(CURL_HTTP_VERSION_2TLS)
HTTP_2_PRIOR_KNOWLEDGE =
LONG2NUM(CURL_HTTP_VERSION_2_PRIOR_KNOWLEDGE)
HTTP_1_1 =
LONG2NUM(CURL_HTTP_VERSION_1_1)
HTTP_1_0 =
LONG2NUM(CURL_HTTP_VERSION_1_0)
HTTP_NONE =
LONG2NUM(CURL_HTTP_VERSION_NONE)

Class Method Summary collapse

Class Method Details

.asyncdns?Boolean

Returns true if the installed libcurl was built with support for asynchronous name lookups, which allows more exact timeouts (even on Windows) and less blocking when using the multi interface. For libcurl versions < 7.10.7, always returns false.

Returns:

  • (Boolean)


128
129
130
131
132
133
134
135
# File 'ext/curb.c', line 128

static VALUE ruby_curl_asyncdns_q(VALUE mod) {
#ifdef HAVE_CURL_VERSION_ASYNCHDNS
  curl_version_info_data *ver = curl_version_info(CURLVERSION_NOW);
  return((ver->features & CURL_VERSION_ASYNCHDNS) ? Qtrue : Qfalse);
#else
  return Qfalse;
#endif
}

.block_scheduler_waiter(waiter) ⇒ Object



57
58
59
60
61
62
63
64
65
66
67
68
69
70
# File 'lib/curl.rb', line 57

def self.block_scheduler_waiter(waiter)
  unless scheduler_waiter_blocking_supported?
    sleep 0
    return
  end

  waiter[:fiber] = Fiber.current
  waiter[:scheduler] ||= Fiber.scheduler
  return if waiter[:done] || waiter[:error]

  waiter[:scheduler].block(waiter, nil)
ensure
  waiter[:fiber] = nil if waiter[:fiber].equal?(Fiber.current)
end

.complete_scheduler_waiter(waiter) ⇒ Object



32
33
34
35
36
37
# File 'lib/curl.rb', line 32

def self.complete_scheduler_waiter(waiter)
  return if waiter[:done]

  waiter[:done] = true
  wake_scheduler_waiter(waiter)
end

.conv?Boolean

Returns true if the installed libcurl was built with support for character conversions. For libcurl versions < 7.15.4, always returns false.

Returns:

  • (Boolean)


215
216
217
218
219
220
221
222
# File 'ext/curb.c', line 215

static VALUE ruby_curl_conv_q(VALUE mod) {
#ifdef HAVE_CURL_VERSION_CONV
  curl_version_info_data *ver = curl_version_info(CURLVERSION_NOW);
  return((ver->features & CURL_VERSION_CONV) ? Qtrue : Qfalse);
#else
  return Qfalse;
#endif
}

.debug?Boolean

Returns true if the installed libcurl was built with extra debug capabilities built-in. For libcurl versions < 7.10.6, always returns false.

Returns:

  • (Boolean)


110
111
112
113
114
115
116
117
# File 'ext/curb.c', line 110

static VALUE ruby_curl_debug_q(VALUE mod) {
#ifdef HAVE_CURL_VERSION_DEBUG
  curl_version_info_data *ver = curl_version_info(CURLVERSION_NOW);
  return((ver->features & CURL_VERSION_DEBUG) ? Qtrue : Qfalse);
#else
  return Qfalse;
#endif
}

.deferred_exception_source_id(state) ⇒ Object



13
14
15
16
17
# File 'lib/curl.rb', line 13

def self.deferred_exception_source_id(state)
  return unless state[:multi].instance_variable_defined?(:@__curb_deferred_exception_source_id)

  state[:multi].instance_variable_get(:@__curb_deferred_exception_source_id)
end

.delete(url, params = {}, &block) ⇒ Object



234
235
236
# File 'lib/curl.rb', line 234

def self.delete(url, params={}, &block)
  http :DELETE, url, postalize(params), nil, &block
end

.drain_scheduler_pending(state) ⇒ Object



189
190
191
192
193
194
195
196
197
198
199
200
201
# File 'lib/curl.rb', line 189

def self.drain_scheduler_pending(state)
  pending = state[:pending]
  until pending.empty?
    easy = pending.first

    break if state[:multi].instance_variable_defined?(:@__curb_deferred_exception)

    state[:multi].add(easy)
    break unless state[:multi].requests.key?(easy.object_id)

    pending.shift
  end
end

.ensure_scheduler_driver(state) ⇒ Object



141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
# File 'lib/curl.rb', line 141

def self.ensure_scheduler_driver(state)
  return if state[:driver_running]

  state[:driver_running] = true
  state[:error] = nil

  runner = proc do
    begin
      # Give sibling fibers a chance to enqueue work so the shared multi can
      # batch scheduler-driven Easy#perform calls together.
      pending_count = -1
      until pending_count == state[:pending].size
        pending_count = state[:pending].size
        scheduler_yield
      end

      loop do
        drain_scheduler_pending(state)
        break if state[:multi].idle?

        begin
          state[:multi].perform do
            drain_scheduler_pending(state)
            release_scheduler_waiters(state)
            scheduler_yield
          end
        ensure
          # Release any siblings that completed just before a deferred
          # callback exception is re-raised.
          release_scheduler_waiters(state)
        end
      end
    rescue => e
      release_scheduler_waiters(state)
      release_scheduler_error(state, e)
    ensure
      state[:driver_running] = false
      ensure_scheduler_driver(state) if state[:error].nil? && !state[:pending].empty?
    end
  end

  if Fiber.respond_to?(:schedule)
    Fiber.schedule(&runner)
  else
    Fiber.new(blocking: false, &runner).resume
  end
end

.fail_scheduler_waiter(waiter, error) ⇒ Object



39
40
41
42
43
44
# File 'lib/curl.rb', line 39

def self.fail_scheduler_waiter(waiter, error)
  return if waiter[:error]

  waiter[:error] = error
  wake_scheduler_waiter(waiter)
end

.get(url, params = {}, &block) ⇒ Object



222
223
224
# File 'lib/curl.rb', line 222

def self.get(url, params={}, &block)
  http :GET, urlalize(url, params), nil, nil, &block
end

.gssnegotiate?Boolean

Returns true if the installed libcurl supports HTTP GSS-Negotiate. For libcurl versions < 7.10.6, always returns false.

Returns:

  • (Boolean)


93
94
95
96
97
98
99
100
# File 'ext/curb.c', line 93

static VALUE ruby_curl_gssnegotiate_q(VALUE mod) {
#ifdef HAVE_CURL_VERSION_GSSNEGOTIATE
  curl_version_info_data *ver = curl_version_info(CURLVERSION_NOW);
  return((ver->features & CURL_VERSION_GSSNEGOTIATE) ? Qtrue : Qfalse);
#else
  return Qfalse;
#endif
}

.head(url, params = {}, &block) ⇒ Object



242
243
244
# File 'lib/curl.rb', line 242

def self.head(url, params={}, &block)
  http :HEAD, urlalize(url, params), nil, nil, &block
end

.http(verb, url, post_body = nil, put_data = nil, &block) ⇒ Object



203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
# File 'lib/curl.rb', line 203

def self.http(verb, url, post_body=nil, put_data=nil, &block)
  if Thread.current[:curb_curl_yielding]
    handle = Curl::Easy.new # we can't reuse this
  else
    handle = Thread.current[:curb_curl] ||= Curl::Easy.new
    handle.reset
  end
  handle.url = url
  handle.post_body = post_body if post_body
  handle.put_data = put_data if put_data
  if block_given?
    Thread.current[:curb_curl_yielding] = true
    yield handle
    Thread.current[:curb_curl_yielding] = false
  end
  handle.http(verb)
  handle
end

.http2?Boolean

Returns true if the installed libcurl was built with support for HTTP2. For libcurl versions < 7.33.0, always returns false.

Returns:

  • (Boolean)


231
232
233
234
235
236
237
238
# File 'ext/curb.c', line 231

static VALUE ruby_curl_http2_q(VALUE mod) {
#ifdef HAVE_CURL_VERSION_HTTP2
  curl_version_info_data *ver = curl_version_info(CURLVERSION_NOW);
  return((ver->features & CURL_VERSION_HTTP2) ? Qtrue : Qfalse);
#else
  return Qfalse;
#endif
}

.idn?Boolean

Returns true if the installed libcurl was built with support for IDNA, domain names with international letters. For libcurl versions < 7.12.0, always returns false.

Returns:

  • (Boolean)


180
181
182
183
184
185
186
187
# File 'ext/curb.c', line 180

static VALUE ruby_curl_idn_q(VALUE mod) {
#ifdef HAVE_CURL_VERSION_IDN
  curl_version_info_data *ver = curl_version_info(CURLVERSION_NOW);
  return((ver->features & CURL_VERSION_IDN) ? Qtrue : Qfalse);
#else
  return Qfalse;
#endif
}

.ipv6?Boolean

Returns true if the installed libcurl supports IPv6.

Returns:

  • (Boolean)


21
22
23
24
# File 'ext/curb.c', line 21

static VALUE ruby_curl_ipv6_q(VALUE mod) {
  curl_version_info_data *ver = curl_version_info(CURLVERSION_NOW);
  return((ver->features & CURL_VERSION_IPV6) ? Qtrue : Qfalse);
}

.kerberos4?Boolean

Returns true if the installed libcurl supports Kerberos4 authentication with FTP connections.

Returns:

  • (Boolean)


33
34
35
36
# File 'ext/curb.c', line 33

static VALUE ruby_curl_kerberos4_q(VALUE mod) {
  curl_version_info_data *ver = curl_version_info(CURLVERSION_NOW);
  return((ver->features & CURL_VERSION_KERBEROS4) ? Qtrue : Qfalse);
}

.largefile?Boolean

Returns true if the installed libcurl was built with support for large files. For libcurl versions < 7.11.1, always returns false.

Returns:

  • (Boolean)


163
164
165
166
167
168
169
170
# File 'ext/curb.c', line 163

static VALUE ruby_curl_largefile_q(VALUE mod) {
#ifdef HAVE_CURL_VERSION_LARGEFILE
  curl_version_info_data *ver = curl_version_info(CURLVERSION_NOW);
  return((ver->features & CURL_VERSION_LARGEFILE) ? Qtrue : Qfalse);
#else
  return Qfalse;
#endif
}

.libz?Boolean

Returns true if the installed libcurl supports HTTP deflate using libz. For libcurl versions < 7.10, always returns false.

Returns:

  • (Boolean)


61
62
63
64
65
66
67
68
# File 'ext/curb.c', line 61

static VALUE ruby_curl_libz_q(VALUE mod) {
#ifdef HAVE_CURL_VERSION_LIBZ
  curl_version_info_data *ver = curl_version_info(CURLVERSION_NOW);
  return((ver->features & CURL_VERSION_LIBZ) ? Qtrue : Qfalse);
#else
  return Qfalse;
#endif
}

.ntlm?Boolean

Returns true if the installed libcurl supports HTTP NTLM. For libcurl versions < 7.10.6, always returns false.

Returns:

  • (Boolean)


77
78
79
80
81
82
83
84
# File 'ext/curb.c', line 77

static VALUE ruby_curl_ntlm_q(VALUE mod) {
#ifdef HAVE_CURL_VERSION_NTLM
  curl_version_info_data *ver = curl_version_info(CURLVERSION_NOW);
  return((ver->features & CURL_VERSION_NTLM) ? Qtrue : Qfalse);
#else
  return Qfalse;
#endif
}

.options(url, params = {}, &block) ⇒ Object



246
247
248
# File 'lib/curl.rb', line 246

def self.options(url, params={}, &block)
  http :OPTIONS, urlalize(url, params), nil, nil, &block
end

.patch(url, params = {}, &block) ⇒ Object



238
239
240
# File 'lib/curl.rb', line 238

def self.patch(url, params={}, &block)
  http :PATCH, url, postalize(params), nil, &block
end

.perform_with_scheduler(easy) ⇒ Object



92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
# File 'lib/curl.rb', line 92

def self.perform_with_scheduler(easy)
  state = scheduler_state
  waiter = {completed: false, done: false, error: nil, fiber: nil, scheduler: Fiber.scheduler}
  state[:waiters][easy.object_id] = waiter
  previous_complete = easy.on_complete do |completed_easy|
    previous_complete.call(completed_easy) if previous_complete
    waiter[:completed] = true
  end

  state[:pending] << easy
  ensure_scheduler_driver(state)

  until waiter[:done]
    raise waiter[:error] if waiter[:error]
    raise state[:error] if state[:error]
    block_scheduler_waiter(waiter)
  end

  while state[:driver_running] && state[:pending].empty? &&
        state[:waiters].length == 1 && state[:waiters].key?(easy.object_id)
    scheduler_yield
  end

  true
ensure
  state[:waiters].delete(easy.object_id) if defined?(state) && state[:waiters]
  if defined?(previous_complete)
    if previous_complete
      easy.on_complete(&previous_complete)
    else
      easy.on_complete
    end
  end
end

.post(url, params = {}, &block) ⇒ Object



226
227
228
# File 'lib/curl.rb', line 226

def self.post(url, params={}, &block)
  http :POST, url, postalize(params), nil, &block
end

.postalize(params = {}) ⇒ Object



260
261
262
# File 'lib/curl.rb', line 260

def self.postalize(params={})
  params.respond_to?(:map) ? URI.encode_www_form(params) : (params.respond_to?(:to_s) ? params.to_s : params)
end

.put(url, params = {}, &block) ⇒ Object



230
231
232
# File 'lib/curl.rb', line 230

def self.put(url, params={}, &block)
  http :PUT, url, nil, postalize(params), &block
end

.release_scheduler_error(state, error) ⇒ Object



46
47
48
49
50
51
52
53
54
55
# File 'lib/curl.rb', line 46

def self.release_scheduler_error(state, error)
  source_waiter = state[:waiters][deferred_exception_source_id(state)]

  if source_waiter
    fail_scheduler_waiter(source_waiter, error)
  else
    state[:error] = error
    state[:waiters].each_value { |waiter| wake_scheduler_waiter(waiter) }
  end
end

.release_scheduler_waiters(state) ⇒ Object



82
83
84
85
86
87
88
89
90
# File 'lib/curl.rb', line 82

def self.release_scheduler_waiters(state)
  source_id = deferred_exception_source_id(state)

  state[:waiters].each do |easy_id, waiter|
    next if source_id == easy_id

    complete_scheduler_waiter(waiter) if waiter[:completed]
  end
end

.resetObject



264
265
266
# File 'lib/curl.rb', line 264

def self.reset
  Thread.current[:curb_curl] = Curl::Easy.new
end

.scheduler_active?Boolean

Returns:

  • (Boolean)


9
10
11
# File 'lib/curl.rb', line 9

def self.scheduler_active?
  Fiber.respond_to?(:scheduler) && !Fiber.scheduler.nil?
end

.scheduler_stateObject



127
128
129
130
131
132
133
134
135
136
137
138
139
# File 'lib/curl.rb', line 127

def self.scheduler_state
  Thread.current.thread_variable_get(:curb_scheduler_state) || begin
    state = {
      multi: Curl::Multi.new,
      pending: [],
      driver_running: false,
      error: nil,
      waiters: {},
    }
    Thread.current.thread_variable_set(:curb_scheduler_state, state)
    state
  end
end

.scheduler_waiter_blocking_supported?Boolean

Returns:

  • (Boolean)


19
20
21
22
# File 'lib/curl.rb', line 19

def self.scheduler_waiter_blocking_supported?
  scheduler = Fiber.scheduler
  scheduler && scheduler.respond_to?(:block) && scheduler.respond_to?(:unblock)
end

.scheduler_yieldObject



72
73
74
75
76
77
78
79
80
# File 'lib/curl.rb', line 72

def self.scheduler_yield
  scheduler = Fiber.scheduler

  if scheduler&.respond_to?(:kernel_sleep)
    scheduler.kernel_sleep(0)
  else
    sleep 0
  end
end

.spnego?Boolean

Returns true if the installed libcurl was built with support for SPNEGO authentication (Simple and Protected GSS-API Negotiation Mechanism, defined in RFC 2478). For libcurl versions < 7.10.8, always returns false.

Returns:

  • (Boolean)


145
146
147
148
149
150
151
152
153
154
# File 'ext/curb.c', line 145

static VALUE ruby_curl_spnego_q(VALUE mod) {
  curl_version_info_data *ver = curl_version_info(CURLVERSION_NOW);
#ifdef HAVE_CURL_VERSION_SPNEGO
  if (ver->features & CURL_VERSION_SPNEGO) return Qtrue;
#endif
#ifdef HAVE_CURL_VERSION_GSSNEGOTIATE
  if (ver->features & CURL_VERSION_GSSNEGOTIATE) return Qtrue;
#endif
  return Qfalse;
}

.ssl?Boolean

Returns true if the installed libcurl supports SSL connections. For libcurl versions < 7.10, always returns false.

Returns:

  • (Boolean)


45
46
47
48
49
50
51
52
# File 'ext/curb.c', line 45

static VALUE ruby_curl_ssl_q(VALUE mod) {
#ifdef HAVE_CURL_VERSION_SSL
  curl_version_info_data *ver = curl_version_info(CURLVERSION_NOW);
  return((ver->features & CURL_VERSION_SSL) ? Qtrue : Qfalse);
#else
  return Qfalse;
#endif
}

.sspi?Boolean

Returns true if the installed libcurl was built with support for SSPI. This is only available on Windows and makes libcurl use Windows-provided functions for NTLM authentication. It also allows libcurl to use the current user and the current user’s password without the app having to pass them on. For libcurl versions < 7.13.2, always returns false.

Returns:

  • (Boolean)


199
200
201
202
203
204
205
206
# File 'ext/curb.c', line 199

static VALUE ruby_curl_sspi_q(VALUE mod) {
#ifdef HAVE_CURL_VERSION_SSPI
  curl_version_info_data *ver = curl_version_info(CURLVERSION_NOW);
  return((ver->features & CURL_VERSION_SSPI) ? Qtrue : Qfalse);
#else
  return Qfalse;
#endif
}

.urlalize(url, params = {}) ⇒ Object



250
251
252
253
254
255
256
257
258
# File 'lib/curl.rb', line 250

def self.urlalize(url, params={})
  uri = URI(url)
  # early return if we didn't specify any extra params
  return uri.to_s if (params || {}).empty?

  params_query = URI.encode_www_form(params || {})
  uri.query = [uri.query.to_s, params_query].reject(&:empty?).join('&')
  uri.to_s
end

.wake_scheduler_waiter(waiter) ⇒ Object



24
25
26
27
28
29
30
# File 'lib/curl.rb', line 24

def self.wake_scheduler_waiter(waiter)
  fiber = waiter[:fiber]
  scheduler = waiter[:scheduler]
  return unless fiber&.alive? && scheduler&.respond_to?(:unblock)

  scheduler.unblock(waiter, fiber)
end