aboutsummaryrefslogtreecommitdiffstats
path: root/test/plum/client/test_client.rb
blob: 13aa82ad24a3a8302a9a14fa2b4704e44214effd (plain)
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
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
require "test_helper"

using Plum::BinaryString
class ClientTest < Minitest::Test
  def test_request_sync
    server_thread = start_tls_server
    client = Client.start("127.0.0.1", LISTEN_PORT, https: true, verify_mode: OpenSSL::SSL::VERIFY_NONE)
    res1 = client.put!("/", "aaa", headers: { "header" => "ccc" })
    assert_equal("PUTcccaaa", res1.body)
    client.close
  ensure
    server_thread.join if server_thread
  end

  def test_request_async
    res2 = nil
    client = nil
    server_thread = start_tls_server
    Client.start("127.0.0.1", LISTEN_PORT, https: true, verify_mode: OpenSSL::SSL::VERIFY_NONE) { |c|
      client = c
      res1 = client.request({ ":path" => "/", ":method" => "GET", ":scheme" => "https", "header" => "ccc" }, nil) { |res1|
        assert(res1.headers)
      }
      assert_nil(res1.headers)

      res2 = client.get("/", headers: { "header" => "ccc" })
      assert_nil(res2.headers)
    }
    assert(res2.headers)
    assert_equal("GETccc", res2.body)
  ensure
    server_thread.join if server_thread
  end

  def test_verify
    client = nil
    server_thread = start_tls_server
    assert_raises(OpenSSL::SSL::SSLError) {
      client = Client.start("127.0.0.1", LISTEN_PORT, https: true, verify_mode: OpenSSL::SSL::VERIFY_PEER)
    }
  ensure
    server_thread.join if server_thread
  end

  def test_raise_error_sync
    client = nil
    server_thread = start_tls_server
    Client.start("127.0.0.1", LISTEN_PORT, https: true, verify_mode: OpenSSL::SSL::VERIFY_NONE) { |c|
      client = c
      assert_raises(LocalConnectionError) {
        client.get!("/connection_error")
      }
    }
  ensure
    server_thread.join if server_thread
  end

  def test_raise_error_async_seq_resume
    server_thread = start_tls_server
    client = Client.start("127.0.0.1", LISTEN_PORT, https: true, verify_mode: OpenSSL::SSL::VERIFY_NONE)
    res = client.get("/error_in_data")
    assert_raises(LocalConnectionError) {
      client.resume(res)
    }
    client.close
  ensure
    server_thread.join if server_thread
  end

  def test_raise_error_async_block
    client = nil
    server_thread = start_tls_server
    assert_raises(LocalConnectionError) {
      Client.start("127.0.0.1", LISTEN_PORT, https: true, verify_mode: OpenSSL::SSL::VERIFY_NONE) { |c|
        client = c
        client.get("/connection_error") { |res| flunk "success??" }
      } # resume
    }
  ensure
    server_thread.join if server_thread
  end

  def test_session_socket_http2_https
    sock = StringSocket.new
    client = Client.start(sock, nil, https: true)
    assert(client.session.class == ClientSession)
  end

  def test_session_socket_http2_http
    sock = StringSocket.new("HTTP/1.1 100\r\n\r\n")
    client = Client.start(sock, nil, https: false)
    assert(client.session.class == UpgradeClientSession)
  end

  private
  def start_tls_server(&block)
    ctx = OpenSSL::SSL::SSLContext.new
    ctx.alpn_select_cb = -> protocols { "h2" }
    ctx.cert = TLS_CERT
    ctx.key = TLS_KEY
    tcp_server = TCPServer.new("127.0.0.1", LISTEN_PORT)
    ssl_server = OpenSSL::SSL::SSLServer.new(tcp_server, ctx)

    server_thread = Thread.new {
      plum = nil
      begin
        Timeout.timeout(1) {
          sock = ssl_server.accept
          plum = ServerConnection.new(sock.method(:write))

          plum.on(:stream) { |stream|
            headers = data = nil
            stream.on(:headers) { |h|
              headers = h.to_h }
            stream.on(:data) { |d|
              data = d }
            stream.on(:end_stream) {
              case headers[":path"]
              when "/connection_error"
                plum.goaway(:protocol_error)
              when "/error_in_data"
                stream.send_headers({ ":status" => 200 }, end_stream: false)
                stream.send_data("a", end_stream: false)
                raise ExampleError, "example error"
              else
                stream.send_headers({ ":status" => 200 }, end_stream: false)
                stream.send_data(headers.to_h[":method"] + headers.to_h["header"].to_s + data.to_s, end_stream: true)
              end } }

          yield plum if block_given?

          begin
            while !sock.closed? && !sock.eof?
              plum << sock.readpartial(1024)
            end
          rescue Errno::ECONNABORTED
            # Ignore, this happens only when running on mingw64(?)
          end
        }
      rescue OpenSSL::SSL::SSLError
      rescue Timeout::Error
        flunk "server timeout"
      rescue ExampleError => e
        plum.goaway(:internal_error) if plum
      ensure
        tcp_server.close
      end
    }
  end
end