class Pakyow::Connection

Represents the connection throughout a request/response lifecycle.

Constants

Endpoint

Attributes

body[R]
error[RW]

Contains the error object when the connection is in a failed state.

headers[R]
id[R]
input_parser[W]

@api private

logger[R]
request[R]

@api private

status[R]
timestamp[R]

Public Class Methods

new(request) click to toggle source
# File lib/pakyow/connection.rb, line 56
def initialize(request)
  @id = SecureRandom.hex(4)
  @timestamp = Time.now
  @status = 200
  @headers = {}
  @request = request
  @body = Async::HTTP::Body::Buffered.wrap(StringIO.new)
  @params = Pakyow::Connection::Params.new
  @logger = Logger.new(:http, started_at: @timestamp, id: @id, output: Pakyow.global_logger, level: Pakyow.config.logger.level)
  @streams = []
end

Public Instance Methods

<<(content)
Alias for: write
authority() click to toggle source
# File lib/pakyow/connection.rb, line 136
def authority
  @request.authority
end
body=(body) click to toggle source
# File lib/pakyow/connection.rb, line 269
def body=(body)
  @body = if body.is_a?(Async::HTTP::Body)
    body
  else
    Async::HTTP::Body::Buffered.wrap(body)
  end
end
close() click to toggle source
# File lib/pakyow/connection.rb, line 282
def close
  @body.close
end
cookies() click to toggle source
# File lib/pakyow/connection.rb, line 242
def cookies
  unless instance_variable_defined?(:@cookies)
    parse_cookies
  end

  @cookies
end
delete_header(key) click to toggle source
# File lib/pakyow/connection.rb, line 94
def delete_header(key)
  @headers.delete(normalize_header(key))
end
endpoint() click to toggle source
# File lib/pakyow/connection.rb, line 188
def endpoint
  unless instance_variable_defined?(:@endpoint)
    @endpoint = Endpoint.new(path, params).freeze
  end

  @endpoint
end
finalize() click to toggle source
# File lib/pakyow/connection.rb, line 320
def finalize
  performing :finalize do
    if request_method == "HEAD"
      if streaming?
        @streams.each(&:stop); @streams = []
      end

      close; @body = Async::HTTP::Body::Buffered.wrap(StringIO.new)
    end

    set_cookies

    if streaming?
      Async::Reactor.run do
        while stream = @streams.shift
          stream.wait
        end

        close
      end
    end

    if instance_variable_defined?(:@response)
      @response
    else
      Async::HTTP::Protocol::Response.new(nil, @status, finalize_headers, @body)
    end
  end
end
format() click to toggle source

Returns the symbolized format of the request.

@example

request.format
=> :html
# File lib/pakyow/connection.rb, line 202
def format
  unless instance_variable_defined?(:@format)
    parse_format
  end

  @format
end
format=(format) click to toggle source

Sets the Content-Type header based on the format.

@example

request.format = :json
request.content_type
=> "application/json"
# File lib/pakyow/connection.rb, line 257
def format=(format)
  if mime = MiniMime.lookup_by_extension(format.to_s)
    set_header("content-type", mime.content_type)
  end

  @format = format
end
fullpath() click to toggle source
# File lib/pakyow/connection.rb, line 180
def fullpath
  @request.path
end
header(key) click to toggle source
# File lib/pakyow/connection.rb, line 80
def header(key)
  @headers[normalize_header(key)]
end
header?(key) click to toggle source
# File lib/pakyow/connection.rb, line 76
def header?(key)
  @headers.key?(normalize_header(key))
end
hijack!() click to toggle source
# File lib/pakyow/connection.rb, line 316
def hijack!
  @request.hijack!
end
hijack?() click to toggle source
# File lib/pakyow/connection.rb, line 312
def hijack?
  @request.hijack?
end
host() click to toggle source
# File lib/pakyow/connection.rb, line 140
def host
  unless instance_variable_defined?(:@host)
    parse_authority
  end

  @host
end
input() click to toggle source
# File lib/pakyow/connection.rb, line 98
def input
  @request.body
end
ip() click to toggle source
# File lib/pakyow/connection.rb, line 184
def ip
  request_header("x-forwarded-for").to_a.first || @request.remote_address.ip_address
end
media_type()
Alias for: type
media_type_params()
Alias for: type_params
method() click to toggle source

Returns the request method (e.g. `:get`).

# File lib/pakyow/connection.rb, line 112
def method
  unless instance_variable_defined?(:@method)
    @method = if request_method == "POST" && params.include?(:"pw-http-method")
      params[:"pw-http-method"].downcase.to_sym
    else
      request_method.downcase.to_sym
    end
  end

  @method
end
params() click to toggle source

Returns an indifferentized params hash.

# File lib/pakyow/connection.rb, line 234
def params
  unless instance_variable_defined?(:@built_params)
    build_params
  end

  @params
end
parsed_input() click to toggle source
# File lib/pakyow/connection.rb, line 102
def parsed_input
  unless instance_variable_defined?(:@parsed_input)
    @parsed_input = nil; @parsed_input = parse_input
  end

  @parsed_input
end
path() click to toggle source
# File lib/pakyow/connection.rb, line 164
def path
  unless instance_variable_defined?(:@path)
    parse_path
  end

  @path
end
port() click to toggle source
# File lib/pakyow/connection.rb, line 148
def port
  unless instance_variable_defined?(:@port)
    parse_authority
  end

  @port
end
query() click to toggle source
# File lib/pakyow/connection.rb, line 172
def query
  unless instance_variable_defined?(:@query)
    parse_path
  end

  @query
end
request_header(key) click to toggle source
# File lib/pakyow/connection.rb, line 72
def request_header(key)
  @request.headers[normalize_header(key)]
end
request_header?(key) click to toggle source
# File lib/pakyow/connection.rb, line 68
def request_header?(key)
  @request.headers.include?(normalize_header(key))
end
request_method() click to toggle source

@api private

# File lib/pakyow/connection.rb, line 351
def request_method
  @request.method
end
request_path() click to toggle source

@api private

# File lib/pakyow/connection.rb, line 356
def request_path
  @request.path
end
scheme() click to toggle source
# File lib/pakyow/connection.rb, line 124
def scheme
  if request_header("https").to_s == "on" || request_header("x-forwarded-ssl").to_s == "on"
    "https"
  elsif value = request_header("x-forwarded-scheme")
    value[0]
  elsif value = request_header("x-forwarded-proto")
    value[0]
  else
    @request.scheme
  end
end
secure?() click to toggle source
# File lib/pakyow/connection.rb, line 228
def secure?
  scheme == "https"
end
set_header(key, value) click to toggle source
# File lib/pakyow/connection.rb, line 84
def set_header(key, value)
  @headers[normalize_header(key)] = normalize_header_value(value)
end
set_headers(headers) click to toggle source
# File lib/pakyow/connection.rb, line 88
def set_headers(headers)
  headers.each do |key, value|
    set_header(normalize_header(key), value)
  end
end
sleep(seconds) click to toggle source
# File lib/pakyow/connection.rb, line 308
def sleep(seconds)
  Async::Task.current.sleep(seconds)
end
status=(status) click to toggle source
# File lib/pakyow/connection.rb, line 265
def status=(status)
  @status = Statuses.code(status)
end
stream(length = nil) { |self| ... } click to toggle source
# File lib/pakyow/connection.rb, line 286
def stream(length = nil)
  unless streaming?
    @body = Async::HTTP::Body::Writable.new(length)
  end

  @streams << Async::Task.current.async { |task|
    Thread.current[:pakyow_logger] = @logger

    begin
      yield self
    rescue => error
      @logger.error(error: error)
    end

    @streams.delete(task)
  }
end
streaming?() click to toggle source
# File lib/pakyow/connection.rb, line 304
def streaming?
  @streams.any?
end
subdomain() click to toggle source
# File lib/pakyow/connection.rb, line 156
def subdomain
  unless instance_variable_defined?(:@subdomain)
    parse_subdomain
  end

  @subdomain
end
type() click to toggle source
# File lib/pakyow/connection.rb, line 210
def type
  unless instance_variable_defined?(:@type)
    parse_content_type
  end

  @type
end
Also aliased as: media_type
type_params() click to toggle source
# File lib/pakyow/connection.rb, line 219
def type_params
  unless instance_variable_defined?(:@parsed_type_params)
    @parsed_type_params = build_type_params
  end

  @parsed_type_params
end
Also aliased as: media_type_params
write(content) click to toggle source
# File lib/pakyow/connection.rb, line 277
def write(content)
  @body.write(content)
end
Also aliased as: <<

Private Instance Methods

build_params() click to toggle source
# File lib/pakyow/connection.rb, line 490
def build_params
  @built_params = true
  @params.parse(query.to_s)
  parsed_input
end
build_type_params() click to toggle source
# File lib/pakyow/connection.rb, line 496
def build_type_params
  unless instance_variable_defined?(:@type_params)
    parse_content_type
  end

  QueryParser.new.tap { |parser|
    parser.parse(@type_params.to_s)
  }.params.deep_indifferentize
end
escape(string) click to toggle source
# File lib/pakyow/connection.rb, line 573
def escape(string)
  CGI.escape(string)
end
finalize_headers() click to toggle source
# File lib/pakyow/connection.rb, line 577
def finalize_headers
  @headers.each_with_object([]) { |(key, value), headers|
    Array.ensure(value).each do |single_value|
      headers << [key, single_value]
    end
  }
end
normalize_header(key) click to toggle source
# File lib/pakyow/connection.rb, line 369
def normalize_header(key)
  key.to_s.downcase.gsub("_", "-")
end
normalize_header_value(value) click to toggle source
# File lib/pakyow/connection.rb, line 373
def normalize_header_value(value)
  case value
  when Array
    value.map(&:to_s)
  else
    value.to_s
  end
end
parse_authority() click to toggle source
# File lib/pakyow/connection.rb, line 526
def parse_authority
  @host, @port = authority.to_s.split(":", 2)
end
parse_content_type() click to toggle source
# File lib/pakyow/connection.rb, line 554
def parse_content_type
  @type, @type_params = request_header("content-type").to_s.split(";", 2).map(&:strip)
end
parse_cookies() click to toggle source
# File lib/pakyow/connection.rb, line 558
def parse_cookies
  @cookies = parse_cookie_header
  @request_cookies = @cookies.dup
end
parse_format() click to toggle source
# File lib/pakyow/connection.rb, line 538
def parse_format
  @format = if path.include?(".")
    path.split(".").last.to_sym
  elsif accept = request_header("accept")
    if accept[0] == "*/*"
      :any
    elsif mime_type = MiniMime.lookup_by_content_type(accept[0])&.extension
      mime_type.to_sym
    else
      nil
    end
  else
    :html
  end
end
parse_input() click to toggle source
# File lib/pakyow/connection.rb, line 506
def parse_input
  if instance_variable_defined?(:@input_parser) && input
    if @input_parser[:rewindable]
      request.body = Async::HTTP::Body::Rewindable.new(request.body)
    end

    @input_parser[:block].call(input, self).tap do
      if input.respond_to?(:rewind)
        input.rewind
      end
    end
  else
    nil
  end
end
parse_path() click to toggle source
# File lib/pakyow/connection.rb, line 522
def parse_path
  @path, @query = @request.path.to_s.split("?", 2)
end
parse_subdomain() click to toggle source
# File lib/pakyow/connection.rb, line 530
def parse_subdomain
  @subdomain = if authority.include?(".")
    authority.split(".", 2)[0]
  else
    nil
  end
end
set_cookies() click to toggle source
# File lib/pakyow/connection.rb, line 386
def set_cookies
  response_cookies = {}

  # Delete cookies with nil/empty values.
  #
  cookies.delete_if do |_, value|
    value.nil? || value.empty?
  end

  # Normalize cookies.
  #
  cookies.keys.each do |key|
    cookies[key] = normalize_cookie(cookies.delete(key))
  end

  # Set cookies that have new values.
  #
  cookies.reject { |key, cookie|
    cookie[:value] == @request_cookies[key]
  }.each do |key, cookie|
    response_cookies[key] = cookie_config.merge(cookie)
  end

  # Remove cookies.
  #
  (@request_cookies.keys - cookies.keys).each do |key|
    response_cookies[key] = DELETE_COOKIE
  end

  # Build the header value.
  #
  # TODO: protect against cookie values being larger than 4096 bytes
  set_header(
    "set-cookie",
    response_cookies.map { |key, cookie|
      String.new("#{escape(key.to_s)}=#{escape(cookie[:value].to_s)}") << cookie_options(cookie)
    }
  )
end
unescape(string) click to toggle source
# File lib/pakyow/connection.rb, line 569
def unescape(string)
  CGI.unescape(string)
end