yahns.git  about / heads / tags
sleepy, multi-threaded, non-blocking application server for Ruby
blob ffa4b3e9f9f1e68adec3c0077a5c6285d17a98f3 2228 bytes (raw)
$ git show opt-case:lib/yahns/openssl_client.rb	# shows this blob on the CLI

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
 
# Copyright (C) 2014, all contributors <yahns-public@yhbt.net>
# License: GPLv3 or later (see COPYING for details)

require_relative 'sendfile_compat'

# this is to be included into a Kgio::Socket-derived class
# this requires Ruby 2.1 and later for "exception: false"
module Yahns::OpenSSLClient # :nodoc:
  include Yahns::SendfileCompat

  def self.included(cls)
    # Forward these methods to OpenSSL::SSL::SSLSocket so hijackers
    # can rely on stdlib methods instead of ugly kgio stuff that
    # we hope to phase out.
    # This is a bit weird, since OpenSSL::SSL::SSLSocket wraps
    # our actual socket, too, so we must take care to not blindly
    # use method_missing and cause infinite recursion
    %w(sync= read write readpartial write_nonblock read_nonblock
       print printf puts gets readlines readline getc
       readchar ungetc eof eof? << flush
       sysread syswrite).map!(&:to_sym).each do |m|
      cls.__send__(:define_method, m) { |*a| @ssl.__send__(m, *a) }
    end

    # block captures, ugh, but nobody really uses them
    %w(each each_line each_byte).map!(&:to_sym).each do |m|
      cls.__send__(:define_method, m) { |*a, &b| @ssl.__send__(m, *a, &b) }
    end
  end

  # this is special, called during IO initialization in Ruby
  def sync
    defined?(@ssl) ? @ssl.sync : super
  end

  def yahns_init_ssl(ssl_ctx)
    @need_accept = true
    @ssl = OpenSSL::SSL::SSLSocket.new(self, ssl_ctx)
  end

  def kgio_trywrite(buf)
    rv = @ssl.write_nonblock(buf, exception: false)
    Integer === rv and
      rv = buf.bytesize == rv ? nil : buf.byteslice(rv, buf.bytesize)
    rv
  end

  def kgio_syssend(buf, flags)
    kgio_trywrite(buf)
  end

  def kgio_tryread(len, buf)
    if @need_accept
      # most protocols require read before write, so we start the negotiation
      # process here:
      begin
        @ssl.accept_nonblock
      rescue IO::WaitReadable
        return :wait_readable
      rescue IO::WaitWritable
        return :wait_writable
      rescue OpenSSL::SSL::SSLError
        return nil
      end
      @need_accept = false
    end
    @ssl.read_nonblock(len, buf, exception: false)
  end

  def close
    @ssl.close # flushes SSLSocket
    super # IO#close
  end
end

git clone git://yhbt.net/yahns.git
git clone https://yhbt.net/yahns.git