Class: H2::Server::Stream::EventSource

Inherits:
Object
  • Object
show all
Includes:
HeaderStringifier
Defined in:
lib/h2/server/stream/event_source.rb

Constant Summary collapse

DATA_TEMPL =
"data: %s\n\n"
EVENT_TEMPL =
"event: %s\n#{DATA_TEMPL}"
SSE_HEADER =
{
  STATUS_KEY => '200',
  CONTENT_TYPE_KEY => EVENT_SOURCE_CONTENT_TYPE
}

Instance Method Summary collapse

Constructor Details

#initialize(stream:, headers: {}) ⇒ H2::Server::Stream::EventSource

build and return EventSource instance, ready for pushing out data or named events. checks accept header in the request, then responds with valid headers for beginning an SSE stream

Parameters:

  • stream: (H2::Server::Stream)

    the Stream instance

  • headers: (Hash) (defaults to: {})

    optional headers to add to the intial response



23
24
25
26
27
28
29
30
31
32
33
34
# File 'lib/h2/server/stream/event_source.rb', line 23

def initialize stream:, headers: {}
  @closed  = false
  @stream  = stream
  @parser  = @stream.stream
  @headers = headers
  @gzip    = false
  @deflate = false

  check_accept_header
  check_accept_encoding
  init_response
end

Instance Method Details

#check_accept_encodingObject

checks the request for accept-encoding headers and processes data & events accordingly



49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
# File 'lib/h2/server/stream/event_source.rb', line 49

def check_accept_encoding
  if accept = @stream.request.headers[ACCEPT_ENCODING_KEY]
    accept.split(',').map(&:strip).each do |encoding|
      case encoding
      when GZIP_ENCODING
        if @stream.connection.server.options[:gzip]
          @gzip = true
          @headers[CONTENT_ENCODING_KEY] = GZIP_ENCODING
          break
        end

      # "deflate" has issues: https://zlib.net/zlib_faq.html#faq39
      #
      when DEFLATE_ENCODING
        if @stream.connection.server.options[:deflate]
          @deflate = true
          @headers[CONTENT_ENCODING_KEY] = DEFLATE_ENCODING
          break
        end

      end
    end
  end
end

#check_accept_headerObject

checks accept header in the request and raises a StreamError if not valid for SSE



39
40
41
42
43
44
# File 'lib/h2/server/stream/event_source.rb', line 39

def check_accept_header
  accept = @stream.request.headers['accept']
  unless accept == SSE_HEADER[CONTENT_TYPE_KEY]
    raise StreamError, "invalid header accept: #{accept}"
  end
end

#closeObject

emit a final frame on this stream with end_stream flag



108
109
110
111
# File 'lib/h2/server/stream/event_source.rb', line 108

def close
  @parser.data ''
  @closed = true
end

#closed?Boolean

Returns true if this stream is closed.

Returns:

  • (Boolean)

    true if this stream is closed



115
116
117
# File 'lib/h2/server/stream/event_source.rb', line 115

def closed?
  @closed
end

#data(str) ⇒ Object

send out a message with the given data

this would be handled by ‘es.onmessage((msg)=>{})`

Parameters:

  • data (String)

    associated with this event



101
102
103
104
# File 'lib/h2/server/stream/event_source.rb', line 101

def data str
  d = encode_content(DATA_TEMPL % str)
  @parser.data d, end_stream: false
end

#event(name:, data:) ⇒ Object

send out a named event with the given data

this would be handled by ‘es.addEventListener(’name’, (msg)=>{})‘

Parameters:

  • name: (String)

    the name of the event

  • data: (String)

    data associated with this event



90
91
92
93
# File 'lib/h2/server/stream/event_source.rb', line 90

def event name:, data:
  e = encode_content(EVENT_TEMPL % [name, data])
  @parser.data e, end_stream: false
end

#init_responseObject

responds with SSE headers on this stream



76
77
78
79
80
81
# File 'lib/h2/server/stream/event_source.rb', line 76

def init_response
  headers = SSE_HEADER.merge @headers
  @parser.headers stringify_headers(headers)
rescue ::HTTP2::Error::StreamClosed
  @stream.log :warn, "stream closed early by client"
end