Class: HTTP_Spew::Request

Inherits:
Object
  • Object
show all
Includes:
Headers
Defined in:
lib/http_spew/request.rb

Overview

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

Direct Known Subclasses

HitNRun

Instance Attribute Summary collapse

Instance Method Summary collapse

Methods included from Headers

env_to_headers, request_uri

Constructor Details

#initialize(env, input, sock, allow = nil) ⇒ Request

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



27
28
29
30
31
32
33
34
35
# File 'lib/http_spew/request.rb', line 27

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

Instance Attribute Details

#errorObject

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



12
13
14
# File 'lib/http_spew/request.rb', line 12

def error
  @error
end

#responseObject (readonly)

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



15
16
17
# File 'lib/http_spew/request.rb', line 15

def response
  @response
end

#to_ioObject (readonly)

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



8
9
10
# File 'lib/http_spew/request.rb', line 8

def to_io
  @to_io
end

Instance Method Details

#closeObject

Called by Rack servers after writing a response to a client



129
130
131
132
# File 'lib/http_spew/request.rb', line 129

def close
  @to_io.close
  @input = nil
end

#eachObject

Called by Rack servers to write the response to a client



107
108
109
110
111
112
113
114
115
116
117
118
# File 'lib/http_spew/request.rb', line 107

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

#read_responseObject

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.



84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
# File 'lib/http_spew/request.rb', line 84

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

#resumeObject

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



39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
# File 'lib/http_spew/request.rb', line 39

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) ⇒ Object

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



60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
# File 'lib/http_spew/request.rb', line 60

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) ⇒ Object



134
135
136
137
138
139
140
141
# File 'lib/http_spew/request.rb', line 134

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

#too_big!Object

:nodoc:

Raises:



102
103
104
# File 'lib/http_spew/request.rb', line 102

def too_big! # :nodoc:
  raise HTTP_Spew::RequestError.new(self), "response headers too large", []
end