Class: Iodine::Http::Http2

Inherits:
Protocol show all
Defined in:
lib/iodine/http/hpack.rb,
lib/iodine/http/http2.rb

Defined Under Namespace

Classes: HPACK

Instance Attribute Summary

Attributes inherited from Protocol

#io, #locker, #options

Class Method Summary collapse

Instance Method Summary collapse

Methods inherited from Protocol

#call, #close, #closed?, each, #id, #initialize, #on_close, #read, #set_timeout, #ssl?, #timeout?, #write

Constructor Details

This class inherits a constructor from Iodine::Protocol

Class Method Details

.handshake(request, io, data) ⇒ Object

clear text handshake



119
120
121
122
123
124
125
126
# File 'lib/iodine/http/http2.rb', line 119

def self.handshake request, io, data
  return false unless request['upgrade'.freeze] =~ /h2c/.freeze && request['http2-settings'.freeze]
  io.write "HTTP/1.1 101 Switching Protocols\r\nConnection: Upgrade\r\nUpgrade: h2c\r\n\r\n".freeze
  http_2 = self.new(io, request)
  unless data.eof?
    http_2.on_message data.read
  end
end

.pre_handshake(io, data) ⇒ Object

preknowledge handshake



128
129
130
131
132
# File 'lib/iodine/http/http2.rb', line 128

def self.pre_handshake io, data
  return false unless data[0..23] == "PRI * HTTP\/2.0\r\n\r\nSM\r\n\r\n".freeze
  self.new(io).on_message data
  true
end

Instance Method Details

#go_away(error_code) ⇒ Object



106
107
108
109
110
111
# File 'lib/iodine/http/http2.rb', line 106

def go_away error_code
  return false if @io.closed?
  @frame_locker.synchronize { emit_frame [@last_stream, error_code].pack('N*'), 0, 7 }
  close
  # Iodine.info "HTTP/2 connection closed with code #{error_code}"
end

#on_message(data) ⇒ Object



56
57
58
59
60
# File 'lib/iodine/http/http2.rb', line 56

def on_message data
  data = ::StringIO.new data
  parse_preface data unless @connected
  true while parse_frame data
end

#on_openObject



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
# File 'lib/iodine/http/http2.rb', line 4

def on_open
  # not fully fanctional.
  ::Iodine.warn "Http/2 requested - support is still experimental."

  # update the timeout to 15 seconds (ping will be sent whenever timeout is reached).
  set_timeout 15

  # Header compression is stateful
  @hpack = ::Iodine::Http::Http2::HPACK.new

  # the header-stream cache
  @header_buffer = String.new
  @header_end_stream = false
  @header_sid = nil
  @frame_locker = Mutex.new

  # frame parser starting posotion
  @frame = {}

  # Open stream managment
  @open_streams = {}

  # connection is only established after the preface was sent
  @connected = false

  # the last stream to be processed (For the GOAWAY frame)
  @last_stream = 0
  @refuese_new = false
  # @complete_stream = 0

  # the settings state
  @settings = DEFAULT_SETTING.dup

  # send connection preface (Section 3.5) consisting of a (can be empty) SETTINGS frame (Section 6.5).
  #
  # should prepare to accept a client connection preface which starts with:
  # 0x505249202a20485454502f322e300d0a0d0a534d0d0a0d0a
  # == PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n
  # + SETTINGS frame

  # The @options variable contains the original Http1 request, if exists.
  return unless @options
  ::Iodine.warn "Http/2: upgrade handshake settings not implemented. upgrade request:\n#{@options}"
  @last_stream = @options[:stream_id] = 1
  @options[:io] = self
  # deal with the request['http2-settings'] - NO ACK
  # HTTP2-Settings: <base64url encoding of HTTP/2 SETTINGS payload>

  # dispatch the original request
  ::Iodine.run @options, &(::Iodine::Http::Http2.dispatch)
  @options = nil
end

#on_shutdownObject

Gracefully close HTTP/2 when possible



114
115
116
# File 'lib/iodine/http/http2.rb', line 114

def on_shutdown
  go_away NO_ERROR
end

#pingObject



93
94
95
# File 'lib/iodine/http/http2.rb', line 93

def ping
  @frame_locker.synchronize { emit_frame "pniodine".freeze, 0, 6 }
end

#push(request) ⇒ Object



97
98
99
100
101
102
103
104
# File 'lib/iodine/http/http2.rb', line 97

def push request
  return false if @settings[SETTINGS_ENABLE_PUSH] == 0
  @last_push ||= 0
  # emit push promise
  emit_payload @hpack.encode(path: request[:path], method: request[:method], scheme: request[:scheme], authority: request[:authority]), (request[:sid] = (@last_push += 2)), 5, 4
  # queue for app dispatch
  Iodine.run( request, &::Iodine::Http::Http2.dispatch)
end

#send_response(response) ⇒ Object



62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
# File 'lib/iodine/http/http2.rb', line 62

def send_response response
  return false if response.headers.frozen?
  body = response.extract_body
  request = response.request
  return body && body.close unless send_headers response, request
  return log_finished(response) && body && body.close if request.head?
  if body
    until body.eof?
      response.bytes_written += emit_payload(body.read(@settings[SETTINGS_MAX_FRAME_SIZE], Thread.current[:write_buffer]), request[:sid], 0, (body.eof? ? 1 : 0))
    end
    body.close
  else
    emit_payload(''.freeze, request[:sid], 0, 1)
  end
  log_finished response
end

#stream_response(response, finish = false) ⇒ Object



79
80
81
82
83
84
85
86
87
88
89
90
91
# File 'lib/iodine/http/http2.rb', line 79

def stream_response response, finish = false
  request = response.request
  body = response.extract_body
  send_headers response, request
  return body && body.close if request.head?
  if body
    response.bytes_written += emit_payload body, request[:sid], 0,(finish ? 1 : 0)
    body.close
  elsif finish
    emit_payload(''.freeze, request[:sid], 0, 1)
  end
  log_finished response if finish
end