1require "test/unit" 2require "net/http" 3require "webrick" 4require "webrick/httpproxy" 5begin 6 require "webrick/ssl" 7 require "net/https" 8 require File.expand_path("../openssl/utils.rb", File.dirname(__FILE__)) 9rescue LoadError 10 # test_connect will be skipped 11end 12require File.expand_path("utils.rb", File.dirname(__FILE__)) 13 14class TestWEBrickHTTPProxy < Test::Unit::TestCase 15 def test_fake_proxy 16 assert_nil(WEBrick::FakeProxyURI.scheme) 17 assert_nil(WEBrick::FakeProxyURI.host) 18 assert_nil(WEBrick::FakeProxyURI.port) 19 assert_nil(WEBrick::FakeProxyURI.path) 20 assert_nil(WEBrick::FakeProxyURI.userinfo) 21 assert_raise(NoMethodError){ WEBrick::FakeProxyURI.foo } 22 end 23 24 def test_proxy 25 # Testing GET or POST to the proxy server 26 # Note that the proxy server works as the origin server. 27 # +------+ 28 # V | 29 # client -------> proxy ---+ 30 # GET / POST GET / POST 31 # 32 proxy_handler_called = request_handler_called = 0 33 config = { 34 :ServerName => "localhost.localdomain", 35 :ProxyContentHandler => Proc.new{|req, res| proxy_handler_called += 1 }, 36 :RequestCallback => Proc.new{|req, res| request_handler_called += 1 } 37 } 38 TestWEBrick.start_httpproxy(config){|server, addr, port, log| 39 server.mount_proc("/"){|req, res| 40 res.body = "#{req.request_method} #{req.path} #{req.body}" 41 } 42 http = Net::HTTP.new(addr, port, addr, port) 43 44 req = Net::HTTP::Get.new("/") 45 http.request(req){|res| 46 assert_equal("1.1 localhost.localdomain:#{port}", res["via"], log.call) 47 assert_equal("GET / ", res.body, log.call) 48 } 49 assert_equal(1, proxy_handler_called, log.call) 50 assert_equal(2, request_handler_called, log.call) 51 52 req = Net::HTTP::Head.new("/") 53 http.request(req){|res| 54 assert_equal("1.1 localhost.localdomain:#{port}", res["via"], log.call) 55 assert_nil(res.body, log.call) 56 } 57 assert_equal(2, proxy_handler_called, log.call) 58 assert_equal(4, request_handler_called, log.call) 59 60 req = Net::HTTP::Post.new("/") 61 req.body = "post-data" 62 http.request(req){|res| 63 assert_equal("1.1 localhost.localdomain:#{port}", res["via"], log.call) 64 assert_equal("POST / post-data", res.body, log.call) 65 } 66 assert_equal(3, proxy_handler_called, log.call) 67 assert_equal(6, request_handler_called, log.call) 68 } 69 end 70 71 def test_no_proxy 72 # Testing GET or POST to the proxy server without proxy request. 73 # 74 # client -------> proxy 75 # GET / POST 76 # 77 proxy_handler_called = request_handler_called = 0 78 config = { 79 :ServerName => "localhost.localdomain", 80 :ProxyContentHandler => Proc.new{|req, res| proxy_handler_called += 1 }, 81 :RequestCallback => Proc.new{|req, res| request_handler_called += 1 } 82 } 83 TestWEBrick.start_httpproxy(config){|server, addr, port, log| 84 server.mount_proc("/"){|req, res| 85 res.body = "#{req.request_method} #{req.path} #{req.body}" 86 } 87 http = Net::HTTP.new(addr, port) 88 89 req = Net::HTTP::Get.new("/") 90 http.request(req){|res| 91 assert_nil(res["via"], log.call) 92 assert_equal("GET / ", res.body, log.call) 93 } 94 assert_equal(0, proxy_handler_called, log.call) 95 assert_equal(1, request_handler_called, log.call) 96 97 req = Net::HTTP::Head.new("/") 98 http.request(req){|res| 99 assert_nil(res["via"], log.call) 100 assert_nil(res.body, log.call) 101 } 102 assert_equal(0, proxy_handler_called, log.call) 103 assert_equal(2, request_handler_called, log.call) 104 105 req = Net::HTTP::Post.new("/") 106 req.body = "post-data" 107 http.request(req){|res| 108 assert_nil(res["via"], log.call) 109 assert_equal("POST / post-data", res.body, log.call) 110 } 111 assert_equal(0, proxy_handler_called, log.call) 112 assert_equal(3, request_handler_called, log.call) 113 } 114 end 115 116 def make_certificate(key, cn) 117 subject = OpenSSL::X509::Name.parse("/DC=org/DC=ruby-lang/CN=#{cn}") 118 exts = [ 119 ["keyUsage", "keyEncipherment,digitalSignature", true], 120 ] 121 cert = OpenSSL::TestUtils.issue_cert( 122 subject, key, 1, Time.now, Time.now + 3600, exts, 123 nil, nil, OpenSSL::Digest::SHA1.new 124 ) 125 return cert 126 end 127 128 def test_connect 129 # Testing CONNECT to proxy server 130 # 131 # client -----------> proxy -----------> https 132 # 1. CONNECT establish TCP 133 # 2. ---- establish SSL session ---> 134 # 3. ------- GET or POST ----------> 135 # 136 key = OpenSSL::TestUtils::TEST_KEY_RSA1024 137 cert = make_certificate(key, "127.0.0.1") 138 s_config = { 139 :SSLEnable =>true, 140 :ServerName => "localhost", 141 :SSLCertificate => cert, 142 :SSLPrivateKey => key, 143 } 144 config = { 145 :ServerName => "localhost.localdomain", 146 :RequestCallback => Proc.new{|req, res| 147 assert_equal("CONNECT", req.request_method) 148 }, 149 } 150 TestWEBrick.start_httpserver(s_config){|s_server, s_addr, s_port, s_log| 151 s_server.mount_proc("/"){|req, res| 152 res.body = "SSL #{req.request_method} #{req.path} #{req.body}" 153 } 154 TestWEBrick.start_httpproxy(config){|server, addr, port, log| 155 http = Net::HTTP.new("127.0.0.1", s_port, addr, port) 156 http.use_ssl = true 157 http.verify_callback = Proc.new do |preverify_ok, store_ctx| 158 store_ctx.current_cert.to_der == cert.to_der 159 end 160 161 req = Net::HTTP::Get.new("/") 162 http.request(req){|res| 163 assert_equal("SSL GET / ", res.body, s_log.call + log.call) 164 } 165 166 req = Net::HTTP::Post.new("/") 167 req.body = "post-data" 168 http.request(req){|res| 169 assert_equal("SSL POST / post-data", res.body, s_log.call + log.call) 170 } 171 } 172 } 173 end if defined?(OpenSSL) 174 175 def test_upstream_proxy 176 # Testing GET or POST through the upstream proxy server 177 # Note that the upstream proxy server works as the origin server. 178 # +------+ 179 # V | 180 # client -------> proxy -------> proxy ---+ 181 # GET / POST GET / POST GET / POST 182 # 183 up_proxy_handler_called = up_request_handler_called = 0 184 proxy_handler_called = request_handler_called = 0 185 up_config = { 186 :ServerName => "localhost.localdomain", 187 :ProxyContentHandler => Proc.new{|req, res| up_proxy_handler_called += 1}, 188 :RequestCallback => Proc.new{|req, res| up_request_handler_called += 1} 189 } 190 TestWEBrick.start_httpproxy(up_config){|up_server, up_addr, up_port, up_log| 191 up_server.mount_proc("/"){|req, res| 192 res.body = "#{req.request_method} #{req.path} #{req.body}" 193 } 194 config = { 195 :ServerName => "localhost.localdomain", 196 :ProxyURI => URI.parse("http://localhost:#{up_port}"), 197 :ProxyContentHandler => Proc.new{|req, res| proxy_handler_called += 1}, 198 :RequestCallback => Proc.new{|req, res| request_handler_called += 1}, 199 } 200 TestWEBrick.start_httpproxy(config){|server, addr, port, log| 201 http = Net::HTTP.new(up_addr, up_port, addr, port) 202 203 req = Net::HTTP::Get.new("/") 204 http.request(req){|res| 205 skip res.message unless res.code == '200' 206 via = res["via"].split(/,\s+/) 207 assert(via.include?("1.1 localhost.localdomain:#{up_port}"), up_log.call + log.call) 208 assert(via.include?("1.1 localhost.localdomain:#{port}"), up_log.call + log.call) 209 assert_equal("GET / ", res.body) 210 } 211 assert_equal(1, up_proxy_handler_called, up_log.call + log.call) 212 assert_equal(2, up_request_handler_called, up_log.call + log.call) 213 assert_equal(1, proxy_handler_called, up_log.call + log.call) 214 assert_equal(1, request_handler_called, up_log.call + log.call) 215 216 req = Net::HTTP::Head.new("/") 217 http.request(req){|res| 218 via = res["via"].split(/,\s+/) 219 assert(via.include?("1.1 localhost.localdomain:#{up_port}"), up_log.call + log.call) 220 assert(via.include?("1.1 localhost.localdomain:#{port}"), up_log.call + log.call) 221 assert_nil(res.body, up_log.call + log.call) 222 } 223 assert_equal(2, up_proxy_handler_called, up_log.call + log.call) 224 assert_equal(4, up_request_handler_called, up_log.call + log.call) 225 assert_equal(2, proxy_handler_called, up_log.call + log.call) 226 assert_equal(2, request_handler_called, up_log.call + log.call) 227 228 req = Net::HTTP::Post.new("/") 229 req.body = "post-data" 230 http.request(req){|res| 231 via = res["via"].split(/,\s+/) 232 assert(via.include?("1.1 localhost.localdomain:#{up_port}"), up_log.call + log.call) 233 assert(via.include?("1.1 localhost.localdomain:#{port}"), up_log.call + log.call) 234 assert_equal("POST / post-data", res.body, up_log.call + log.call) 235 } 236 assert_equal(3, up_proxy_handler_called, up_log.call + log.call) 237 assert_equal(6, up_request_handler_called, up_log.call + log.call) 238 assert_equal(3, proxy_handler_called, up_log.call + log.call) 239 assert_equal(3, request_handler_called, up_log.call + log.call) 240 241 if defined?(OpenSSL) 242 # Testing CONNECT to the upstream proxy server 243 # 244 # client -------> proxy -------> proxy -------> https 245 # 1. CONNECT CONNECT establish TCP 246 # 2. -------- establish SSL session ------> 247 # 3. ---------- GET or POST --------------> 248 # 249 key = OpenSSL::TestUtils::TEST_KEY_RSA1024 250 cert = make_certificate(key, "127.0.0.1") 251 s_config = { 252 :SSLEnable =>true, 253 :ServerName => "localhost", 254 :SSLCertificate => cert, 255 :SSLPrivateKey => key, 256 } 257 TestWEBrick.start_httpserver(s_config){|s_server, s_addr, s_port, s_log| 258 s_server.mount_proc("/"){|req2, res| 259 res.body = "SSL #{req2.request_method} #{req2.path} #{req2.body}" 260 } 261 http = Net::HTTP.new("127.0.0.1", s_port, addr, port, up_log.call + log.call + s_log.call) 262 http.use_ssl = true 263 http.verify_callback = Proc.new do |preverify_ok, store_ctx| 264 store_ctx.current_cert.to_der == cert.to_der 265 end 266 267 req2 = Net::HTTP::Get.new("/") 268 http.request(req2){|res| 269 assert_equal("SSL GET / ", res.body, up_log.call + log.call + s_log.call) 270 } 271 272 req2 = Net::HTTP::Post.new("/") 273 req2.body = "post-data" 274 http.request(req2){|res| 275 assert_equal("SSL POST / post-data", res.body, up_log.call + log.call + s_log.call) 276 } 277 } 278 end 279 } 280 } 281 end 282end 283