File: client76.rb

package info (click to toggle)
ruby-websocket 1.2.11-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 412 kB
  • sloc: ruby: 2,667; makefile: 4
file content (105 lines) | stat: -rw-r--r-- 2,995 bytes parent folder | download | duplicates (2)
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
# frozen_string_literal: true

require 'digest/md5'

module WebSocket
  module Handshake
    module Handler
      class Client76 < Client75
        # @see WebSocket::Handshake::Base#valid?
        def valid?
          super && verify_challenge && verify_protocol
        end

        private

        # @see WebSocket::Handshake::Base#reserved_leftover_lines
        def reserved_leftover_lines
          1
        end

        # @see WebSocket::Handshake::Handler::Base#handshake_keys
        def handshake_keys
          keys = super
          keys << ['Sec-WebSocket-Key1', key1]
          keys << ['Sec-WebSocket-Key2', key2]
          keys
        end

        # @see WebSocket::Handshake::Handler::Base#finishing_line
        def finishing_line
          key3
        end

        # Sec-WebSocket-Key1 value
        # @return [String] key
        def key1
          @key1 ||= generate_key(:key1)
        end

        # Sec-WebSocket-Key2 value
        # @return [String] key
        def key2
          @key2 ||= generate_key(:key2)
        end

        # Value of third key, sent in body
        # @return [String] key
        def key3
          @key3 ||= generate_key3
        end

        # Expected challenge that should be sent by server
        # @return [String] challenge
        def challenge
          return @challenge if defined?(@challenge)
          key1 && key2
          sum = [@key1_number].pack('N*') +
                [@key2_number].pack('N*') +
                key3

          @challenge = Digest::MD5.digest(sum).strip
        end

        # Verify if challenge sent by server match generated one
        # @return [Boolena] True if challenge matches, false otherwise(sets appropriate error)
        def verify_challenge
          raise WebSocket::Error::Handshake::InvalidAuthentication unless @handshake.leftovers == challenge
          true
        end

        NOISE_CHARS = ("\x21".."\x2f").to_a + ("\x3a".."\x7e").to_a

        # Generate Sec-WebSocket-Key1 and Sec-WebSocket-Key2
        # @param key [String] name of key. Will be used to set number variable needed later. Valid values: key1, key2
        # @return [String] generated key
        def generate_key(key)
          spaces = rand(1..12)
          max = 0xffffffff / spaces
          number = rand(max + 1)
          instance_variable_set("@#{key}_number", number)
          key = (number * spaces).to_s
          rand(1..12).times do
            char = NOISE_CHARS[rand(NOISE_CHARS.size)]
            pos = rand(key.size + 1)
            key[pos...pos] = char
          end
          spaces.times do
            pos = 1 + rand(key.size - 1)
            key[pos...pos] = ' '
          end
          key
        end

        # Generate third key
        def generate_key3
          [rand(0x100000000)].pack('N') + [rand(0x100000000)].pack('N')
        end

        def provided_protocols
          Array(@handshake.headers['sec-websocket-protocol'].to_s.strip)
        end
      end
    end
  end
end