Module: Qeweney::ResponseMethods

Included in:
Request
Defined in:
lib/qeweney/response.rb

Constant Summary collapse

WEBSOCKET_GUID =
'258EAFA5-E914-47DA-95CA-C5AB0DC85B11'

Instance Method Summary collapse

Instance Method Details

#file_full_path(path, opts) ⇒ Object



121
122
123
124
125
126
127
# File 'lib/qeweney/response.rb', line 121

def file_full_path(path, opts)
  if (base_path = opts[:base_path])
    File.join(opts[:base_path], path)
  else
    path
  end
end

#redirect(url, status = Status::FOUND) ⇒ Object



49
50
51
# File 'lib/qeweney/response.rb', line 49

def redirect(url, status = Status::FOUND)
  respond(nil, ':status' => status, 'Location' => url)
end

#redirect_to_host(new_host, status = Status::FOUND) ⇒ Object



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

def redirect_to_host(new_host, status = Status::FOUND)
  secure_uri = "//#{new_host}#{uri}"
  redirect(secure_uri, status)
end

#redirect_to_https(status = Status::MOVED_PERMANENTLY) ⇒ Object



53
54
55
56
# File 'lib/qeweney/response.rb', line 53

def redirect_to_https(status = Status::MOVED_PERMANENTLY)
  secure_uri = "https://#{host}#{uri}"
  redirect(secure_uri, status)
end

#respond_with_static_file(path, etag, last_modified, opts) ⇒ Object



85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
# File 'lib/qeweney/response.rb', line 85

def respond_with_static_file(path, etag, last_modified, opts)
  cache_headers = {
    'etag' => etag,
    'last-modified' => last_modified,
  }
  File.open(path, 'r') do |f|
    if opts[:headers]
      opts[:headers].merge!(cache_headers)
    else
      opts[:headers] = cache_headers
    end

    # accept_encoding should return encodings in client's order of preference
    accept_encoding.each do |encoding|
      case encoding
      when 'deflate'
        return serve_io_deflate(f, opts)
      when 'gzip'
        return serve_io_gzip(f, opts)
      end
    end
    serve_io(f, opts)
  end
end

#serve_file(path, opts = {}) ⇒ Object



63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
# File 'lib/qeweney/response.rb', line 63

def serve_file(path, opts = {})
  full_path = file_full_path(path, opts)
  stat = File.stat(full_path)
  etag = StaticFileCaching.file_stat_to_etag(stat)
  last_modified = StaticFileCaching.file_stat_to_last_modified(stat)

  if validate_static_file_cache(etag, last_modified)
    return respond(nil, {
      ':status' => Status::NOT_MODIFIED,
      'etag' => etag
    })
  end

  mime_type = Qeweney::MimeTypes[File.extname(path)]
  opts[:stat] = stat
  (opts[:headers] ||= {})['Content-Type'] ||= mime_type if mime_type

  respond_with_static_file(full_path, etag, last_modified, opts)
rescue Errno::ENOENT => e
  respond(nil, ':status' => Status::NOT_FOUND)
end

#serve_io(io, opts) ⇒ Object



129
130
131
# File 'lib/qeweney/response.rb', line 129

def serve_io(io, opts)
  respond(io.read, opts[:headers] || {})
end

#serve_io_deflate(io, opts) ⇒ Object



133
134
135
136
137
138
139
140
141
# File 'lib/qeweney/response.rb', line 133

def serve_io_deflate(io, opts)
  deflate = Zlib::Deflate.new
  headers = opts[:headers].merge(
    'content-encoding' => 'deflate',
    'vary' => 'Accept-Encoding'
  )

  respond(deflate.deflate(io.read, Zlib::FINISH), headers)
end

#serve_io_gzip(io, opts) ⇒ Object



143
144
145
146
147
148
149
150
151
152
153
154
# File 'lib/qeweney/response.rb', line 143

def serve_io_gzip(io, opts)
  buf = StringIO.new
  z = Zlib::GzipWriter.new(buf)
  z << io.read
  z.flush
  z.close
  headers = opts[:headers].merge(
    'content-encoding' => 'gzip',
    'vary' => 'Accept-Encoding'
  )
  respond(buf.string, headers)
end

#serve_rack(app) ⇒ Object



156
157
158
159
160
161
162
163
164
# File 'lib/qeweney/response.rb', line 156

def serve_rack(app)
  response = app.(Qeweney.rack_env_from_request(self))
  headers = (response[1] || {}).merge(':status' => response[0])
  respond(response[2].join, headers)

  # TODO: send separate chunks for multi-part body
  # TODO: add support for streaming body
  # TODO: add support for websocket
end

#upgrade(protocol, custom_headers = nil) ⇒ Object



25
26
27
28
29
30
31
32
33
34
# File 'lib/qeweney/response.rb', line 25

def upgrade(protocol, custom_headers = nil)
  upgrade_headers = {
    ':status' => Status::SWITCHING_PROTOCOLS,
    'Upgrade' => protocol,
    'Connection' => 'upgrade'
  }
  upgrade_headers.merge!(custom_headers) if custom_headers

  respond(nil, upgrade_headers)
end

#upgrade_to_websocket(custom_headers = nil) ⇒ Object



38
39
40
41
42
43
44
45
46
47
# File 'lib/qeweney/response.rb', line 38

def upgrade_to_websocket(custom_headers = nil)
  key = "#{headers['sec-websocket-key']}#{WEBSOCKET_GUID}"
  upgrade_headers = {
    'Sec-WebSocket-Accept' => Digest::SHA1.base64digest(key)
  }
  upgrade_headers.merge!(custom_headers) if custom_headers
  upgrade('websocket', upgrade_headers)

  adapter.websocket_connection(self)
end

#validate_static_file_cache(etag, last_modified) ⇒ Object



110
111
112
113
114
115
116
117
118
119
# File 'lib/qeweney/response.rb', line 110

def validate_static_file_cache(etag, last_modified)
  if (none_match = headers['if-none-match'])
    return true if none_match == etag
  end
  if (modified_since = headers['if-modified-since'])
    return true if modified_since == last_modified
  end

  false
end