class HTTP_Spew::Request

This is the base class actually capable of making a normal HTTP request

Attributes

error[R]

Stores any exception that was raised in another thread (e.g. ContentMD5 or InputSpray write drivers).

response[R]

Stores the Rack response (a 3-element Array) on success

to_io[R]

May be called by IO.select or for use with IO#wait_*able

Public Class Methods

new(env, input, sock, allow = nil) click to toggle source

Creates a new Request based on a Rack env and input object and prepares it for writing to sock. input supercedes env since it may be an alternate IO object (such as one filtered through HTTP_Spew::ContentMD5.

sock may be the String representing an address created with Socket.pack_sockaddr_un or Socket.pack_sockaddr_in, or it may be an actual Socket object

# File lib/http_spew/request.rb, line 26
def initialize(env, input, sock, allow = nil)
  @to_io = BasicSocket === sock ? sock : start_sock(sock)
  if Hash === env
    @buf, @input = env_to_headers(env, input)
  else
    @buf, @input = env, input
  end
  @allow = allow
end

Public Instance Methods

close() click to toggle source

Called by Rack servers after writing a response to a client

# File lib/http_spew/request.rb, line 128
def close
  @to_io.close
  @input = nil
end
each() { |buf| ... } click to toggle source

Called by Rack servers to write the response to a client

# File lib/http_spew/request.rb, line 106
def each
  buf = ""
  case @to_io.read_nonblock(0x4000, buf, exception: false)
  when :wait_readable
    @to_io.wait_readable
  when nil
    buf.clear
    return
  else
    yield buf
  end while true
end
error=(exception) click to toggle source

Used internally by various HTTP_Spew elements to report errors across different Threads and Fibers

# File lib/http_spew/request.rb, line 121
def error=(exception)
  @input.respond_to?(:error=) and @input.error = exception
  close
  @error = exception
end
read_response() click to toggle source

returns a 3-element Rack response array on completion returns :wait_readable or :wait_writable if busy Users do not need to call this directly, resume will return the result of this.

# File lib/http_spew/request.rb, line 83
def read_response
  buf = @to_io.recv_nonblock(0x4000, Socket::MSG_PEEK, exception: false) or
    raise HttpSpew::EOF, "upstream server closed connection", []
  String === buf or return buf

  # Kcar::Parser#headers shortens +buf+ for us
  hdr_len = buf.size
  r = Kcar::Parser.new.headers({}, buf) or too_big!
  if @allow && ! @allow.include?(r[0].to_i)
    raise HTTP_Spew::UnexpectedResponse,
          "#{r[0].to_i} not in #{@allow.inspect}", []
  end

  # discard the header data from the socket buffer
  (hdr_len -= buf.size) > 0 and @to_io.read(hdr_len, buf)
  @response = r << self
end
resume() click to toggle source

returns a 3-element Rack response array on completion returns :wait_readable or :wait_writable if busy

# File lib/http_spew/request.rb, line 38
def resume
  if @buf
    case w = @to_io.write_nonblock(@buf, exception: false)
    when :wait_writable, :wait_readable
      return w
    else # Integer
      len = @buf.size
      if w == len
        @buf = @input ? @input.read(0x4000, @buf) : nil
      else
        tmp = @buf.byteslice(w, len - w)
        @buf.clear
        @buf = tmp # loop retry, socket buffer could've expanded
      end
    end while @buf
  end
  read_response
end
run(timeout) click to toggle source

returns a 3-element Rack response array on successful completion returns an Exception if one was raised

# File lib/http_spew/request.rb, line 59
def run(timeout)
  t0 = Process.clock_gettime(Process::CLOCK_MONOTONIC)
  buf, @buf = @buf, nil # make inspect nicer
  @to_io.write(buf)
  if @input
    @to_io.write(buf) while @input.read(0x4000, buf)
  end
  buf.clear
  timeout -= (Process.clock_gettime(Process::CLOCK_MONOTONIC) - t0)
  while :wait_readable == (rv = read_response) && timeout >= 0.0
    t0 = Process.clock_gettime(Process::CLOCK_MONOTONIC)
    @to_io.wait_readable(timeout) if timeout > 0.0
    timeout -= (Process.clock_gettime(Process::CLOCK_MONOTONIC) - t0)
  end
  rv
  rescue => e
    @input.respond_to?(:close) and @input.close rescue nil
    self.error = e
end
start_sock(ai) click to toggle source
# File lib/http_spew/request.rb, line 133
def start_sock(ai)
  ai = Addrinfo.new(ai) unless Addrinfo === ai
  sock = Socket.new(ai.afamily, :SOCK_STREAM)
  case sock.connect_nonblock(ai, exception: false)
  when 0, :wait_writable
  end
  sock
end