Module: Curl

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

Overview

expose shortcut methods

Defined Under Namespace

Modules: Err Classes: DownloadTargetExistsError, Easy, Multi, PostField, SafeDownloadOutput, SafetyConfig, Upload

Constant Summary collapse

DOWNLOAD_OPTION_KEYS =
[:download_dir, :overwrite].freeze
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



458
459
460
461
462
463
464
465
466
467
468
469
470
471
# File 'lib/curl.rb', line 458

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



433
434
435
436
437
438
# File 'lib/curl.rb', line 433

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



414
415
416
417
418
# File 'lib/curl.rb', line 414

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



649
650
651
# File 'lib/curl.rb', line 649

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

.download_filename_from_url(url) ⇒ Object



62
63
64
65
66
67
68
69
70
71
# File 'lib/curl/download.rb', line 62

def download_filename_from_url(url)
  path = begin
    URI.parse(url.to_s).path
  rescue URI::InvalidURIError
    url.to_s.split(/\?/, 2).first
  end

  basename = File.basename(path.to_s)
  validate_download_filename!(basename)
end

.download_options_hash?(value) ⇒ Boolean

Returns:

  • (Boolean)


11
12
13
# File 'lib/curl/download.rb', line 11

def download_options_hash?(value)
  value.is_a?(Hash) && value.keys.all? { |key| DOWNLOAD_OPTION_KEYS.include?(key) }
end

.drain_scheduler_pending(state) ⇒ Object



590
591
592
593
594
595
596
597
598
599
600
601
602
# File 'lib/curl.rb', line 590

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



542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
# File 'lib/curl.rb', line 542

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



440
441
442
443
444
445
# File 'lib/curl.rb', line 440

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

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

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



624
625
626
# File 'lib/curl.rb', line 624

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



665
666
667
# File 'lib/curl.rb', line 665

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

.http(verb, url, post_body = nil, put_data = nil) {|handle| ... } ⇒ Object

Yields:

  • (handle)


604
605
606
607
608
609
610
611
612
# File 'lib/curl.rb', line 604

def self.http(verb, url, post_body=nil, put_data=nil, &block)
  handle = Curl::Easy.new
  handle.url = url
  handle.post_body = post_body if post_body
  handle.put_data = put_data if put_data
  yield handle if block_given?
  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
}

.normalize_download_arguments(filename, options) ⇒ Object



15
16
17
18
19
20
21
22
# File 'lib/curl/download.rb', line 15

def normalize_download_arguments(filename, options)
  if download_options_hash?(filename) && options.empty?
    options = filename
    filename = nil
  end

  [filename, options || {}]
end

.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
}

.open_safe_download_output(path, overwrite: false) ⇒ Object



98
99
100
# File 'lib/curl/download.rb', line 98

def open_safe_download_output(path, overwrite: false)
  SafeDownloadOutput.new(path, overwrite: overwrite)
end

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



674
675
676
# File 'lib/curl.rb', line 674

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

.parse_download_options(options) ⇒ Object

Raises:

  • (ArgumentError)


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

def parse_download_options(options)
  raise ArgumentError, "download options must be a Hash" unless options.is_a?(Hash)

  options = options.dup
  download_dir = options.delete(:download_dir)
  overwrite = options.key?(:overwrite) ? !!options.delete(:overwrite) : false
  raise ArgumentError, "unsupported download option(s): #{options.keys.join(', ')}" unless options.empty?

  [download_dir, overwrite]
end

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



657
658
659
# File 'lib/curl.rb', line 657

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

.perform_with_scheduler(easy) ⇒ Object



493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
# File 'lib/curl.rb', line 493

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



633
634
635
# File 'lib/curl.rb', line 633

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

.postalize(params = {}) ⇒ Object



693
694
695
# File 'lib/curl.rb', line 693

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

.prepare_download_output(url, filename = nil, options = {}) ⇒ Object



55
56
57
58
59
60
# File 'lib/curl/download.rb', line 55

def prepare_download_output(url, filename = nil, options = {})
  path, io, safe_output, overwrite = resolve_download_output(url, filename, options)
  return [path, io, safe_output] unless safe_output

  [path, open_safe_download_output(path, overwrite: overwrite), true]
end

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



641
642
643
# File 'lib/curl.rb', line 641

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

.release_scheduler_error(state, error) ⇒ Object



447
448
449
450
451
452
453
454
455
456
# File 'lib/curl.rb', line 447

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



483
484
485
486
487
488
489
490
491
# File 'lib/curl.rb', line 483

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



724
725
726
# File 'lib/curl.rb', line 724

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

.resolve_download_output(url, filename = nil, options = {}) ⇒ Object



35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
# File 'lib/curl/download.rb', line 35

def resolve_download_output(url, filename = nil, options = {})
  filename, options = normalize_download_arguments(filename, options)
  download_dir, overwrite = parse_download_options(options)

  if filename.is_a?(IO)
    filename.binmode if filename.respond_to?(:binmode)
    return [nil, filename, false, overwrite]
  end

  path = if download_dir
    safe_download_path(url, download_dir, filename: filename)
  elsif filename.nil?
    safe_download_path(url)
  else
    filename.to_s
  end

  [path, nil, true, overwrite]
end

.safe! {|config| ... } ⇒ Object

Yields:

  • (config)


176
177
178
179
180
181
# File 'lib/curl.rb', line 176

def self.safe!
  config = SafetyConfig.new
  yield config if block_given?
  @safety_config = config
  bump_safety_generation!
end

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



653
654
655
# File 'lib/curl.rb', line 653

def self.safe_delete(url, params={}, options={}, &block)
  safe_http :DELETE, url, postalize(params), nil, options, &block
end

.safe_download_path(url, destination_dir = Dir.pwd, filename: nil) ⇒ Object

Raises:

  • (ArgumentError)


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

def safe_download_path(url, destination_dir = Dir.pwd, filename: nil)
  dir = File.expand_path(destination_dir.to_s)
  raise ArgumentError, "download destination directory does not exist: #{destination_dir}" unless File.directory?(dir)

  File.join(dir, filename.nil? ? download_filename_from_url(url) : validate_download_filename!(filename))
end

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



628
629
630
631
# File 'lib/curl.rb', line 628

def self.safe_get(url, params={}, options={}, &block)
  params, options = split_safe_http_params_options(params, options)
  safe_http :GET, urlalize(url, params), nil, nil, options, &block
end

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



669
670
671
672
# File 'lib/curl.rb', line 669

def self.safe_head(url, params={}, options={}, &block)
  params, options = split_safe_http_params_options(params, options)
  safe_http :HEAD, urlalize(url, params), nil, nil, options, &block
end

.safe_http(verb, url, post_body = nil, put_data = nil, options = {}, &block) ⇒ Object



614
615
616
617
618
619
620
621
622
# File 'lib/curl.rb', line 614

def self.safe_http(verb, url, post_body=nil, put_data=nil, options={}, &block)
  options = safe_http_options(options)

  http(verb, url, post_body, put_data) do |handle|
    yield handle if block
    handle.safe_http!
    handle.max_body_bytes = options[:max_body_bytes] if options.key?(:max_body_bytes)
  end
end

.safe_http_option_hash?(value) ⇒ Boolean

Returns:

  • (Boolean)


716
717
718
# File 'lib/curl.rb', line 716

def self.safe_http_option_hash?(value)
  value.is_a?(Hash) && !value.empty? && (value.keys - safe_http_option_keys).empty?
end

.safe_http_option_keysObject



720
721
722
# File 'lib/curl.rb', line 720

def self.safe_http_option_keys
  [:max_body_bytes]
end

.safe_http_options(options) ⇒ Object

Raises:

  • (ArgumentError)


697
698
699
700
701
702
703
704
705
706
# File 'lib/curl.rb', line 697

def self.safe_http_options(options)
  options ||= {}
  raise ArgumentError, "safe HTTP options must be a Hash" unless options.is_a?(Hash)

  options = options.dup
  unsupported = options.keys - safe_http_option_keys
  raise ArgumentError, "unsupported safe HTTP option(s): #{unsupported.join(', ')}" unless unsupported.empty?

  options
end

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



678
679
680
681
# File 'lib/curl.rb', line 678

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

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



661
662
663
# File 'lib/curl.rb', line 661

def self.safe_patch(url, params={}, options={}, &block)
  safe_http :PATCH, url, postalize(params), nil, options, &block
end

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



637
638
639
# File 'lib/curl.rb', line 637

def self.safe_post(url, params={}, options={}, &block)
  safe_http :POST, url, postalize(params), nil, options, &block
end

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



645
646
647
# File 'lib/curl.rb', line 645

def self.safe_put(url, params={}, options={}, &block)
  safe_http :PUT, url, nil, postalize(params), options, &block
end

.scheduler_active?Boolean

Returns:

  • (Boolean)


410
411
412
# File 'lib/curl.rb', line 410

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

.scheduler_stateObject



528
529
530
531
532
533
534
535
536
537
538
539
540
# File 'lib/curl.rb', line 528

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)


420
421
422
423
# File 'lib/curl.rb', line 420

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

.scheduler_yieldObject



473
474
475
476
477
478
479
480
481
# File 'lib/curl.rb', line 473

def self.scheduler_yield
  scheduler = Fiber.scheduler

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

.split_safe_http_params_options(params, options) ⇒ Object



708
709
710
711
712
713
714
# File 'lib/curl.rb', line 708

def self.split_safe_http_params_options(params, options)
  if options == {} && safe_http_option_hash?(params)
    [{}, params]
  else
    [params, options]
  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



683
684
685
686
687
688
689
690
691
# File 'lib/curl.rb', line 683

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

.validate_download_filename!(filename) ⇒ Object

Raises:

  • (ArgumentError)


80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
# File 'lib/curl/download.rb', line 80

def validate_download_filename!(filename)
  filename = filename.to_s
  invalid = filename.empty? ||
            filename == '.' ||
            filename == '..' ||
            filename == File::SEPARATOR ||
            filename.start_with?('.') ||
            filename.include?("\0") ||
            filename.include?(File::SEPARATOR) ||
            filename.include?('\\') ||
            filename.match?(/\A[A-Za-z]:/) ||
            (File::ALT_SEPARATOR && filename.include?(File::ALT_SEPARATOR))

  raise ArgumentError, "unsafe download filename derived from URL: #{filename.inspect}" if invalid

  filename
end

.wake_scheduler_waiter(waiter) ⇒ Object



425
426
427
428
429
430
431
# File 'lib/curl.rb', line 425

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