class Vines::Stream::Http::Request

Constants

BUF_SIZE
CONTENT_TYPES
IF_MODIFIED
MODIFIED
MOVED
NOT_FOUND
NOT_MODIFIED
OPTIONS
TEXT_PLAIN

Attributes

body[R]
headers[R]
method[R]
path[R]
query[R]
stream[R]
url[R]

Public Class Methods

new(stream, parser, body) click to toggle source
# File lib/vines/stream/http/request.rb, line 28
def initialize(stream, parser, body)
  @stream, @body = stream, body
  @headers  = parser.headers
  @method   = parser.http_method
  @path     = parser.request_path
  @url      = parser.request_url
  @query    = parser.query_string
  @received = Time.now
end

Public Instance Methods

age() click to toggle source

Return the number of seconds since this request was received.

# File lib/vines/stream/http/request.rb, line 39
def age
  Time.now - @received
end
options?() click to toggle source

Return true if the request method is OPTIONS, signaling a CORS preflight check.

# File lib/vines/stream/http/request.rb, line 86
def options?
  @method == OPTIONS
end
reply(node, content_type) click to toggle source

Send an HTTP 200 OK response wrapping the XMPP node content back to the client.

# File lib/vines/stream/http/request.rb, line 72
def reply(node, content_type)
  body = node.to_s
  header = [
    "HTTP/1.1 200 OK",
    "Access-Control-Allow-Origin: *",
    "Content-Type: #{content_type}",
    "Content-Length: #{body.bytesize}",
    vroute_cookie
  ].compact.join("\r\n")
  @stream.stream_write([header, body].join("\r\n\r\n"))
end
reply_to_options() click to toggle source

Send a 200 OK response, allowing any origin domain to connect to the server, in response to CORS preflight OPTIONS requests. This allows any web application using strophe.js to connect to our BOSH port.

# File lib/vines/stream/http/request.rb, line 93
def reply_to_options
  allow = @headers['Access-Control-Request-Headers']
  headers = [
    "Access-Control-Allow-Origin: *",
    "Access-Control-Allow-Methods: POST, GET, OPTIONS",
    "Access-Control-Allow-Headers: #{allow}",
    "Access-Control-Max-Age: #{60 * 60 * 24 * 30}"
  ]
  send_status(200, 'OK', headers)
end
reply_with_file(dir) click to toggle source

Write the requested file to the client out of the given document root directory. Take care to prevent directory traversal attacks with paths like ../../../etc/passwd. Use the If-Modified-Since request header to implement caching.

# File lib/vines/stream/http/request.rb, line 47
def reply_with_file(dir)
  path = File.expand_path(File.join(dir, @path))

  # redirect requests missing a slash so relative links work
  if File.directory?(path) && !@path.end_with?('/')
    send_status(301, MOVED, "Location: #{redirect_uri}")
    return
  end

  path = File.join(path, 'index.html') if File.directory?(path)

  if path.start_with?(dir) && File.exist?(path)
    modified?(path) ? send_file(path) : send_status(304, NOT_MODIFIED)
  else
    missing = File.join(dir, '404.html')
    if File.exist?(missing)
      send_file(missing, 404, NOT_FOUND)
    else
      send_status(404, NOT_FOUND)
    end
  end
end

Private Instance Methods

content_type(path) click to toggle source
# File lib/vines/stream/http/request.rb, line 156
def content_type(path)
  ext = File.extname(path).sub('.', '')
  CONTENT_TYPES[ext] || TEXT_PLAIN
end
modified?(path) click to toggle source

Return true if the file has been modified since the client last requested it with the If-Modified-Since header.

# File lib/vines/stream/http/request.rb, line 120
def modified?(path)
  @headers[IF_MODIFIED] != mtime(path)
end
mtime(path) click to toggle source
# File lib/vines/stream/http/request.rb, line 124
def mtime(path)
  File.mtime(path).utc.strftime(MODIFIED)
end
redirect_uri() click to toggle source

Attempt to rebuild the full request URI from the Host header. If it wasn't sent by the client, just return the relative path that was requested. The Location response header must contain the fully qualified URI, but most browsers will accept relative paths as well.

# File lib/vines/stream/http/request.rb, line 110
def redirect_uri
  host = headers['Host']
  uri = "#{path}/"
  uri = "#{uri}?#{query}" unless (query || '').empty?
  uri = "http://#{host}#{uri}" if host
  uri
end
send_file(path, status=200, message='OK') click to toggle source

Stream the contents of the file to the client in a 200 OK response. Send a Last-Modified response header so clients can send us an If-Modified-Since request header for caching.

# File lib/vines/stream/http/request.rb, line 140
def send_file(path, status=200, message='OK')
  header = [
    "HTTP/1.1 #{status} #{message}",
    "Content-Type: #{content_type(path)}",
    "Content-Length: #{File.size(path)}",
    "Last-Modified: #{mtime(path)}"
  ].join("\r\n")
  @stream.stream_write("#{header}\r\n\r\n")

  File.open(path) do |file|
    while (buf = file.read(BUF_SIZE)) != nil
      @stream.stream_write(buf)
    end
  end
end
send_status(status, message, *headers) click to toggle source
# File lib/vines/stream/http/request.rb, line 128
def send_status(status, message, *headers)
  header = [
    "HTTP/1.1 #{status} #{message}",
    "Content-Length: 0",
    *headers
  ].join("\r\n")
  @stream.stream_write("#{header}\r\n\r\n")
end