Project

General

Profile

Feature #15553 ยป patch2.diff

get rid of Timeout from ext/socket/lib/socket.rb. If getaddrinfo_a() is not available, timeout is ignored. - Glass_saga (Masaki Matsushita), 03/24/2019 05:39 AM

View differences:

ext/socket/extconf.rb
444 444
  test_func = "socket(0,0,0)"
445 445
  have_library("nsl", 't_open("", 0, (struct t_info *)NULL)', headers) # SunOS
446 446
  have_library("socket", "socket(0,0,0)", headers) # SunOS
447
  have_library("anl", 'getaddrinfo_a', headers)
447 448
end
448 449

  
449 450
if have_func(test_func, headers)
......
505 506
  unless have_func("gethostname((char *)0, 0)", headers)
506 507
    have_func("uname((struct utsname *)NULL)", headers)
507 508
  end
509
  have_func("getaddrinfo_a", headers)
508 510

  
509 511
  ipv6 = false
510 512
  default_ipv6 = /haiku/ !~ RUBY_PLATFORM
ext/socket/lib/socket.rb
223 223
  #   #   #<Addrinfo: [::1]:80 TCP (:80)>
224 224
  #   #   #<Addrinfo: [::1]:80 UDP (:80)>
225 225
  #
226
  def self.foreach(nodename, service, family=nil, socktype=nil, protocol=nil, flags=nil, &block)
227
    Addrinfo.getaddrinfo(nodename, service, family, socktype, protocol, flags).each(&block)
226
  def self.foreach(nodename, service, family=nil, socktype=nil, protocol=nil, flags=nil, timeout: nil, &block)
227
    Addrinfo.getaddrinfo(nodename, service, family, socktype, protocol, flags, timeout: timeout).each(&block)
228 228
  end
229 229
end
230 230

  
......
606 606
  # _opts_ may have following options:
607 607
  #
608 608
  # [:connect_timeout] specify the timeout in seconds.
609
  # [:resolv_timeout] specify the name resolution timeout in seconds.
609 610
  #
610 611
  # If a block is given, the block is called with the socket.
611 612
  # The value of the block is returned.
......
619 620
  #     puts sock.read
620 621
  #   }
621 622
  #
622
  def self.tcp(host, port, local_host = nil, local_port = nil, connect_timeout: nil) # :yield: socket
623
  def self.tcp(host, port, local_host = nil, local_port = nil, connect_timeout: nil, resolv_timeout: nil) # :yield: socket
623 624
    last_error = nil
624 625
    ret = nil
625 626

  
......
628 629
      local_addr_list = Addrinfo.getaddrinfo(local_host, local_port, nil, :STREAM, nil)
629 630
    end
630 631

  
631
    Addrinfo.foreach(host, port, nil, :STREAM) {|ai|
632
    Addrinfo.foreach(host, port, nil, :STREAM, timeout: resolv_timeout) {|ai|
632 633
      if local_addr_list
633 634
        local_addr = local_addr_list.find {|local_ai| local_ai.afamily == ai.afamily }
634 635
        next unless local_addr
ext/socket/raddrinfo.c
199 199
}
200 200
#endif
201 201

  
202
#ifdef HAVE_GETADDRINFO_A
203
struct gai_suspend_arg
204
{
205
    struct gaicb *req;
206
    struct timespec *timeout;
207
};
208

  
209
static void *
210
nogvl_gai_suspend(void *arg)
211
{
212
    int ret;
213
    struct gai_suspend_arg *ptr = arg;
214
    struct gaicb const *wait_reqs[1];
215

  
216
    wait_reqs[0] = ptr->req;
217
    ret = gai_suspend(wait_reqs, 1, ptr->timeout);
218

  
219
    return (void *)(VALUE)ret;
220
}
221
#endif
222

  
202 223
static int
203 224
numeric_getaddrinfo(const char *node, const char *service,
204 225
        const struct addrinfo *hints,
......
318 339
    return ret;
319 340
}
320 341

  
342
#ifdef HAVE_GETADDRINFO_A
343
int
344
rb_getaddrinfo_a(const char *node, const char *service,
345
               const struct addrinfo *hints,
346
               struct rb_addrinfo **res, struct timespec *timeout)
347
{
348
    struct addrinfo *ai;
349
    int ret;
350
    int allocated_by_malloc = 0;
351

  
352
    ret = numeric_getaddrinfo(node, service, hints, &ai);
353
    if (ret == 0)
354
        allocated_by_malloc = 1;
355
    else {
356
	struct gai_suspend_arg arg;
357
	struct gaicb *reqs[1];
358
	struct gaicb req;
359

  
360
	req.ar_name = node;
361
	req.ar_service = service;
362
	req.ar_request = hints;
363

  
364
	reqs[0] = &req;
365
	ret = getaddrinfo_a(GAI_NOWAIT, reqs, 1, NULL);
366
	if (ret) return ret;
367

  
368
	arg.req = &req;
369
	arg.timeout = timeout;
370

  
371
	ret = (int)(VALUE)rb_thread_call_without_gvl(nogvl_gai_suspend, &arg, RUBY_UBF_IO, 0);
372

  
373
	if (ret) {
374
	    /* on Ubuntu 18.04 (or other systems), gai_suspend(3) returns EAI_SYSTEM/ENOENT on timeout */
375
	    if (ret == EAI_SYSTEM && errno == ENOENT) {
376
		return EAI_AGAIN;
377
	    } else {
378
		return ret;
379
	    }
380
	}
381

  
382
	ret = gai_error(reqs[0]);
383
	ai = reqs[0]->ar_result;
384
    }
385

  
386
    if (ret == 0) {
387
        *res = (struct rb_addrinfo *)xmalloc(sizeof(struct rb_addrinfo));
388
        (*res)->allocated_by_malloc = allocated_by_malloc;
389
        (*res)->ai = ai;
390
    }
391
    return ret;
392
}
393
#endif
394

  
321 395
void
322 396
rb_freeaddrinfo(struct rb_addrinfo *ai)
323 397
{
......
530 604
    return res;
531 605
}
532 606

  
607
#ifdef HAVE_GETADDRINFO_A
608
static struct rb_addrinfo*
609
rsock_getaddrinfo_a(VALUE host, VALUE port, struct addrinfo *hints, int socktype_hack, VALUE timeout)
610
{
611
    struct rb_addrinfo* res = NULL;
612
    char *hostp, *portp;
613
    int error;
614
    char hbuf[NI_MAXHOST], pbuf[NI_MAXSERV];
615
    int additional_flags = 0;
616

  
617
    hostp = host_str(host, hbuf, sizeof(hbuf), &additional_flags);
618
    portp = port_str(port, pbuf, sizeof(pbuf), &additional_flags);
619

  
620
    if (socktype_hack && hints->ai_socktype == 0 && str_is_number(portp)) {
621
       hints->ai_socktype = SOCK_DGRAM;
622
    }
623
    hints->ai_flags |= additional_flags;
624

  
625
    if (NIL_P(timeout)) {
626
	error = rb_getaddrinfo(hostp, portp, hints, &res);
627
    } else {
628
	struct timespec _timeout = rb_time_timespec_interval(timeout);
629
	error = rb_getaddrinfo_a(hostp, portp, hints, &res, &_timeout);
630
    }
631

  
632
    if (error) {
633
        if (hostp && hostp[strlen(hostp)-1] == '\n') {
634
            rb_raise(rb_eSocket, "newline at the end of hostname");
635
        }
636
        rsock_raise_socket_error("getaddrinfo_a", error);
637
    }
638

  
639
    return res;
640
}
641
#endif
642

  
533 643
int
534 644
rsock_fd_family(int fd)
535 645
{
......
811 921
static struct rb_addrinfo *
812 922
call_getaddrinfo(VALUE node, VALUE service,
813 923
                 VALUE family, VALUE socktype, VALUE protocol, VALUE flags,
814
                 int socktype_hack)
924
                 int socktype_hack, VALUE timeout)
815 925
{
816 926
    struct addrinfo hints;
817 927
    struct rb_addrinfo *res;
......
828 938
    if (!NIL_P(flags)) {
829 939
	hints.ai_flags = NUM2INT(flags);
830 940
    }
941

  
942
#ifdef HAVE_GETADDRINFO_A
943
    if (NIL_P(timeout)) {
944
	res = rsock_getaddrinfo(node, service, &hints, socktype_hack);
945
    } else {
946
	res = rsock_getaddrinfo_a(node, service, &hints, socktype_hack, timeout);
947
    }
948
#else
831 949
    res = rsock_getaddrinfo(node, service, &hints, socktype_hack);
950
#endif
832 951

  
833 952
    if (res == NULL)
834 953
	rb_raise(rb_eSocket, "host not found");
......
842 961
                          VALUE family, VALUE socktype, VALUE protocol, VALUE flags,
843 962
                          VALUE inspectnode, VALUE inspectservice)
844 963
{
845
    struct rb_addrinfo *res = call_getaddrinfo(node, service, family, socktype, protocol, flags, 1);
964
    struct rb_addrinfo *res = call_getaddrinfo(node, service, family, socktype, protocol, flags, 1, Qnil);
846 965
    VALUE canonname;
847 966
    VALUE inspectname = rb_str_equal(node, inspectnode) ? Qnil : make_inspectname(inspectnode, inspectservice, res->ai);
848 967

  
......
912 1031
    VALUE canonname;
913 1032
    VALUE inspectname;
914 1033

  
915
    struct rb_addrinfo *res = call_getaddrinfo(node, service, family, socktype, protocol, flags, 0);
1034
    struct rb_addrinfo *res = call_getaddrinfo(node, service, family, socktype, protocol, flags, 0, Qnil);
916 1035

  
917 1036
    inspectname = make_inspectname(node, service, res->ai);
918 1037

  
......
932 1051
}
933 1052

  
934 1053
static VALUE
935
addrinfo_list_new(VALUE node, VALUE service, VALUE family, VALUE socktype, VALUE protocol, VALUE flags)
1054
addrinfo_list_new(VALUE node, VALUE service, VALUE family, VALUE socktype, VALUE protocol, VALUE flags, VALUE timeout)
936 1055
{
937 1056
    VALUE ret;
938 1057
    struct addrinfo *r;
939 1058
    VALUE inspectname;
940 1059

  
941
    struct rb_addrinfo *res = call_getaddrinfo(node, service, family, socktype, protocol, flags, 0);
1060
    struct rb_addrinfo *res = call_getaddrinfo(node, service, family, socktype, protocol, flags, 0, timeout);
942 1061

  
943 1062
    inspectname = make_inspectname(node, service, res->ai);
944 1063

  
......
1691 1810
#endif
1692 1811
        res = call_getaddrinfo(rb_ary_entry(pair, 0), rb_ary_entry(pair, 1),
1693 1812
                               INT2NUM(pfamily), INT2NUM(socktype), INT2NUM(protocol),
1694
                               INT2NUM(flags), 1);
1813
                               INT2NUM(flags), 1, Qnil);
1695 1814

  
1696 1815
        len = res->ai->ai_addrlen;
1697 1816
        memcpy(&ss, res->ai->ai_addr, res->ai->ai_addrlen);
......
2329 2448
}
2330 2449
#endif
2331 2450

  
2451
static ID id_timeout;
2452

  
2332 2453
/*
2333 2454
 * call-seq:
2334 2455
 *   Addrinfo.getaddrinfo(nodename, service, family, socktype, protocol, flags) => [addrinfo, ...]
......
2375 2496
static VALUE
2376 2497
addrinfo_s_getaddrinfo(int argc, VALUE *argv, VALUE self)
2377 2498
{
2378
    VALUE node, service, family, socktype, protocol, flags;
2499
    VALUE node, service, family, socktype, protocol, flags, opts, timeout;
2500

  
2501
    rb_scan_args(argc, argv, "24:", &node, &service, &family, &socktype,
2502
		 &protocol, &flags, &opts);
2503
    rb_get_kwargs(opts, &id_timeout, 0, 1, &timeout);
2504
    if (timeout == Qundef) {
2505
	timeout = Qnil;
2506
    }
2379 2507

  
2380
    rb_scan_args(argc, argv, "24", &node, &service, &family, &socktype, &protocol, &flags);
2381
    return addrinfo_list_new(node, service, family, socktype, protocol, flags);
2508
    return addrinfo_list_new(node, service, family, socktype, protocol, flags, timeout);
2382 2509
}
2383 2510

  
2384 2511
/*
......
2563 2690
     * The Addrinfo class maps <tt>struct addrinfo</tt> to ruby.  This
2564 2691
     * structure identifies an Internet host and a service.
2565 2692
     */
2693
    id_timeout = rb_intern("timeout");
2694

  
2566 2695
    rb_cAddrinfo = rb_define_class("Addrinfo", rb_cData);
2567 2696
    rb_define_alloc_func(rb_cAddrinfo, addrinfo_s_allocate);
2568 2697
    rb_define_method(rb_cAddrinfo, "initialize", addrinfo_initialize, -1);
include/ruby/intern.h
933 933
struct timeval rb_time_interval(VALUE num);
934 934
struct timeval rb_time_timeval(VALUE time);
935 935
struct timespec rb_time_timespec(VALUE time);
936
struct timespec rb_time_timespec_interval(VALUE num);
936 937
VALUE rb_time_utc_offset(VALUE time);
937 938
/* variable.c */
938 939
VALUE rb_mod_name(VALUE);
test/socket/test_addrinfo.rb
688 688
      assert_equal(ai1.canonname, ai2.canonname)
689 689
    end
690 690

  
691
    def test_addrinfo_timeout
692
      ai = Addrinfo.getaddrinfo("localhost.localdomain", "http", Socket::PF_INET, Socket::SOCK_STREAM, timeout: 1).fetch(0)
693
      assert_equal(6, ai.protocol)
694
      assert_equal(80, ai.ip_port)
695
    end
691 696
  end
692 697
end
time.c
2671 2671
    return time_timespec(time, FALSE);
2672 2672
}
2673 2673

  
2674
struct timespec
2675
rb_time_timespec_interval(VALUE num)
2676
{
2677
    return time_timespec(num, TRUE);
2678
}
2679

  
2674 2680
/*
2675 2681
 *  call-seq:
2676 2682
 *     Time.now -> time