Class: Wsv::Response

Inherits:
Object
  • Object
show all
Defined in:
lib/wsv/response.rb,
lib/wsv/response/sse_body.rb,
lib/wsv/response/file_body.rb,
lib/wsv/response/sse_builder.rb,
lib/wsv/response/string_body.rb,
lib/wsv/response/file_builder.rb,
lib/wsv/response/text_builder.rb

Defined Under Namespace

Classes: FileBody, FileBuilder, SseBody, SseBuilder, StringBody, TextBuilder

Constant Summary collapse

SERVER_NAME =
"wsv/#{Wsv::VERSION}".freeze
INVALID_HEADER_NAME =
/[\s:]/
INVALID_HEADER_VALUE =
/[\r\n]/

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(status:, headers: {}, body: "") ⇒ Response

Returns a new instance of Response.



21
22
23
24
25
26
# File 'lib/wsv/response.rb', line 21

def initialize(status:, headers: {}, body: "")
  validate_headers(headers)
  @status = status
  @headers = headers
  @body = body.is_a?(String) ? StringBody.new(body) : body
end

Instance Attribute Details

#headersObject (readonly)

Returns the value of attribute headers.



19
20
21
# File 'lib/wsv/response.rb', line 19

def headers
  @headers
end

#statusObject (readonly)

Returns the value of attribute status.



19
20
21
# File 'lib/wsv/response.rb', line 19

def status
  @status
end

Class Method Details

.file(path) ⇒ Object



62
63
64
# File 'lib/wsv/response.rb', line 62

def self.file(path, **)
  FileBuilder.new(path, **).build
end

.not_modifiedObject



70
71
72
# File 'lib/wsv/response.rb', line 70

def self.not_modified
  new(status: 304, headers: { "Cache-Control" => "no-cache" }, body: "")
end

.range_not_satisfiable(file_size, head: false) ⇒ Object



74
75
76
# File 'lib/wsv/response.rb', line 74

def self.range_not_satisfiable(file_size, head: false)
  TextBuilder.new(416, head: head, headers: { "Content-Range" => "bytes */#{file_size}" }).build
end

.redirect(location, head: false) ⇒ Object



66
67
68
# File 'lib/wsv/response.rb', line 66

def self.redirect(location, head: false)
  TextBuilder.new(301, head: head, headers: { "Location" => location }).build
end

.sse(status: 200, headers: {}, &producer) ⇒ Object

Build a Server-Sent Events response. The block receives the client socket and writes (and flushes) SSE frames until it returns.



80
81
82
# File 'lib/wsv/response.rb', line 80

def self.sse(status: 200, headers: {}, &producer)
  SseBuilder.new(status: status, headers: headers, &producer).build
end

.text(status) ⇒ Object



58
59
60
# File 'lib/wsv/response.rb', line 58

def self.text(status, **)
  TextBuilder.new(status, **).build
end

Instance Method Details

#bodyObject



28
29
30
# File 'lib/wsv/response.rb', line 28

def body
  @body.to_s
end

#bytesizeObject



32
33
34
# File 'lib/wsv/response.rb', line 32

def bytesize
  @body.bytesize
end

#reasonObject



36
37
38
# File 'lib/wsv/response.rb', line 36

def reason
  Status.reason(status)
end

#with_headers(extra) ⇒ Object

Returns a new Response with ‘extra` merged into the headers, sharing the same body object so streaming (FileBody) is preserved.



42
43
44
# File 'lib/wsv/response.rb', line 42

def with_headers(extra)
  self.class.new(status: @status, headers: @headers.merge(extra), body: @body)
end

#write_to(io) ⇒ Object



46
47
48
49
50
51
52
53
54
55
56
# File 'lib/wsv/response.rb', line 46

def write_to(io)
  io.write "HTTP/1.1 #{status} #{reason}\r\n"
  io.write "Server: #{SERVER_NAME}\r\n"
  io.write "Connection: close\r\n"
  unless headers.any? { |name, _value| name.to_s.casecmp?("X-Content-Type-Options") }
    io.write "X-Content-Type-Options: nosniff\r\n"
  end
  headers.each { |name, value| io.write "#{name}: #{value}\r\n" }
  io.write "\r\n"
  @body.write_to(io)
end