Project

General

Profile

Feature #13729 ยป webrick_sni_support.patch

Tietew (Toru Iwase), 07/07/2017 08:43 AM

View differences:

lib/webrick/https.rb
10 10
# $IPR: https.rb,v 1.15 2003/07/22 19:20:42 gotoyuzo Exp $
11 11

  
12 12
require 'webrick/ssl'
13
require 'webrick/httpserver'
13 14

  
14 15
module WEBrick
15 16
  module Config
......
84 85

  
85 86
    # :startdoc:
86 87
  end
88

  
89
  ##
90
  #--
91
  # Fake WEBrick::HTTPRequest for lookup_server
92

  
93
  class SNIRequest
94

  
95
    ##
96
    # The SNI hostname
97

  
98
    attr_reader :host
99

  
100
    ##
101
    # The socket address of the server
102

  
103
    attr_reader :addr
104

  
105
    ##
106
    # The port this request is for
107

  
108
    attr_reader :port
109

  
110
    ##
111
    # Creates a new SNIRequest.
112

  
113
    def initialize(sslsocket, hostname)
114
      @host = hostname
115
      @addr = sslsocket.addr
116
      @port = @addr[1]
117
    end
118
  end
119

  
120

  
121
  ##
122
  #--
123
  # Adds SSL functionality to WEBrick::HTTPServer
124

  
125
  class HTTPServer < ::WEBrick::GenericServer
126
    ##
127
    # ServerNameIndication callback
128

  
129
    def ssl_servername_callback(sslsocket, hostname = nil)
130
      req = SNIRequest.new(sslsocket, hostname)
131
      server = lookup_server(req)
132
      server ? server.ssl_context : nil
133
    end
134
  end
87 135
end
lib/webrick/ssl.rb
48 48
    #   Number of CA certificates to walk when verifying a certificate chain
49 49
    # :SSLVerifyCallback    ::
50 50
    #   Custom certificate verification callback
51
    # :SSLServerNameCallback::
52
    #   Custom servername indication callback
51 53
    # :SSLTimeout           ::
52 54
    #   Maximum session lifetime
53 55
    # :SSLOptions           ::
......
193 195
      ctx.verify_mode = config[:SSLVerifyClient]
194 196
      ctx.verify_depth = config[:SSLVerifyDepth]
195 197
      ctx.verify_callback = config[:SSLVerifyCallback]
198
      ctx.servername_cb = config[:SSLServerNameCallback] || proc { |args| ssl_servername_callback(*args) }
196 199
      ctx.timeout = config[:SSLTimeout]
197 200
      ctx.options = config[:SSLOptions]
198 201
      ctx.ciphers = config[:SSLCiphers]
199 202
      ctx
200 203
    end
204

  
205
    ##
206
    # ServerNameIndication callback
207

  
208
    def ssl_servername_callback(sslsocket, hostname = nil)
209
      # default
210
    end
211

  
201 212
  end
202 213
end
test/webrick/test_https.rb
1
# frozen_string_literal: false
2
require "test/unit"
3
require "net/http"
4
require "webrick"
5
require "webrick/https"
6
require "webrick/utils"
7
require_relative "utils"
8

  
9
class TestWEBrickHTTPS < Test::Unit::TestCase
10
  empty_log = Object.new
11
  def empty_log.<<(str)
12
    assert_equal('', str)
13
    self
14
  end
15
  NoLog = WEBrick::Log.new(empty_log, WEBrick::BasicLog::WARN)
16

  
17
  class HTTPSNITest < ::Net::HTTP
18
    attr_accessor :sni_hostname
19

  
20
    def ssl_socket_connect(s, timeout)
21
      s.hostname = sni_hostname
22
      super
23
    end
24
  end
25

  
26
  def teardown
27
    WEBrick::Utils::TimeoutHandler.terminate
28
    super
29
  end
30

  
31
  def https_get(addr, port, hostname, path)
32
    http = HTTPSNITest.new(addr, port)
33
    http.use_ssl = true
34
    http.verify_mode = OpenSSL::SSL::VERIFY_NONE
35
    http.sni_hostname = hostname
36
    req = Net::HTTP::Get.new(path)
37
    req["Host"] = "#{hostname}:#{port}"
38
    http.request(req).body
39
  end
40

  
41
  def test_sni
42
    config = {
43
      :ServerName => "localhost",
44
      :SSLEnable => true,
45
      :SSLCertName => "/CN=locahost",
46
    }
47
    TestWEBrick.start_httpserver(config){|server, addr, port, log|
48
      server.mount_proc("/") {|req, res| res.body = "master" }
49

  
50
      vhost_config1 = {
51
        :ServerName => "vhost1",
52
        :Port => port,
53
        :DoNotListen => true,
54
        :Logger => NoLog,
55
        :AccessLog => [],
56
        :SSLEnable => true,
57
        :SSLCertName => "/CN=vhost1",
58
      }
59
      vhost1 = WEBrick::HTTPServer.new(vhost_config1)
60
      vhost1.mount_proc("/") {|req, res| res.body = "vhost1" }
61
      server.virtual_host(vhost1)
62

  
63
      vhost_config2 = {
64
        :ServerName => "vhost2",
65
        :ServerAlias => ["vhost2alias"],
66
        :Port => port,
67
        :DoNotListen => true,
68
        :Logger => NoLog,
69
        :AccessLog => [],
70
        :SSLEnable => true,
71
        :SSLCertName => "/CN=vhost2",
72
      }
73
      vhost2 = WEBrick::HTTPServer.new(vhost_config2)
74
      vhost2.mount_proc("/") {|req, res| res.body = "vhost2" }
75
      server.virtual_host(vhost2)
76

  
77
      assert_equal("master", https_get(addr, port, "localhost", "/localhost"))
78
      assert_equal("master", https_get(addr, port, "unknown", "/unknown"))
79
      assert_equal("vhost1", https_get(addr, port, "vhost1", "/vhost1"))
80
      assert_equal("vhost2", https_get(addr, port, "vhost2", "/vhost2"))
81
      assert_equal("vhost2", https_get(addr, port, "vhost2alias", "/vhost2alias"))
82
    }
83
  end
84
end