File: headers.rb

package info (click to toggle)
ruby-websocket-driver 0.8.0-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 204 kB
  • sloc: ruby: 1,236; java: 46; ansic: 25; makefile: 3
file content (112 lines) | stat: -rw-r--r-- 2,803 bytes parent folder | download
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
module WebSocket
  module HTTP

    module Headers
      MAX_LINE_LENGTH = 4096
      CR = 0x0D
      LF = 0x0A

      # RFC 2616 grammar rules:
      #
      #       CHAR           = <any US-ASCII character (octets 0 - 127)>
      #
      #       CTL            = <any US-ASCII control character
      #                        (octets 0 - 31) and DEL (127)>
      #
      #       SP             = <US-ASCII SP, space (32)>
      #
      #       HT             = <US-ASCII HT, horizontal-tab (9)>
      #
      #       token          = 1*<any CHAR except CTLs or separators>
      #
      #       separators     = "(" | ")" | "<" | ">" | "@"
      #                      | "," | ";" | ":" | "\" | <">
      #                      | "/" | "[" | "]" | "?" | "="
      #                      | "{" | "}" | SP | HT
      #
      # Or, as redefined in RFC 7230:
      #
      #       token          = 1*tchar
      #
      #       tchar          = "!" / "#" / "$" / "%" / "&" / "'" / "*"
      #                      / "+" / "-" / "." / "^" / "_" / "`" / "|" / "~"
      #                      / DIGIT / ALPHA
      #                      ; any VCHAR, except delimiters

      HEADER_LINE = /^([!#\$%&'\*\+\-\.\^_`\|~0-9a-z]+):\s*([\x20-\x7e]*?)\s*$/i

      attr_reader :headers

      def initialize
        @buffer  = []
        @env     = {}
        @headers = {}
        @stage   = 0
      end

      def complete?
        @stage == 2
      end

      def error?
        @stage == -1
      end

      def parse(chunk)
        chunk.each_byte do |octet|
          if octet == LF and @stage < 2
            @buffer.pop if @buffer.last == CR
            if @buffer.empty?
              complete if @stage == 1
            else
              result = case @stage
                       when 0 then start_line(string_buffer)
                       when 1 then header_line(string_buffer)
                       end

              if result
                @stage = 1
              else
                error
              end
            end
            @buffer = []
          else
            @buffer << octet if @stage >= 0
            error if @stage < 2 and @buffer.size > MAX_LINE_LENGTH
          end
        end
        @env['rack.input'] = StringIO.new(string_buffer)
      end

    private

      def complete
        @stage = 2
      end

      def error
        @stage = -1
      end

      def header_line(line)
        return false unless parsed = line.scan(HEADER_LINE).first

        key   = HTTP.normalize_header(parsed[0])
        value = parsed[1].strip

        if @headers.has_key?(key)
          @headers[key] << ', ' << value
        else
          @headers[key] = value
        end
        true
      end

      def string_buffer
        @buffer.pack('C*')
      end
    end

  end
end