Class: Tep::Request

Inherits:
Object
  • Object
show all
Defined in:
lib/tep/request.rb

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initializeRequest

Returns a new instance of Request.



15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
# File 'lib/tep/request.rb', line 15

def initialize
  @verb         = ""
  @path         = ""
  @raw_path     = ""
  @http_version = "HTTP/1.0"
  @params       = Tep.str_hash   # path captures + query + form merged
  @query        = Tep.str_hash   # raw query string only
  @req_headers  = Tep.str_hash   # downcased header names; renamed
                                 # from `headers` to avoid sharing
                                 # an ivar slot with Response (spinel
                                 # mis-codegens polymorphic ivar
                                 # writes when two classes share an
                                 # ivar name).
  @cookies      = Tep.str_hash   # parsed from Cookie: header
  @session      = Session.new    # signed cookie store
  @raw_body     = ""             # same reasoning as req_headers
  @remote_host  = ""
  @passed       = false          # `pass` flag: skip to the next matching route
  @ivars        = Tep.str_hash   # per-request bag for `@name = ...`
                                 # set by handlers and `before` filters,
                                 # read by templates as `ivars[k]`. The
                                 # Sinatra-compat translator rewrites
                                 # `@x = v` -> `req.ivars["x"] = (v).to_s`
                                 # in handler bodies and `@x` -> `ivars["x"]`
                                 # inside ERB chunks.
  @identity     = Tep::Identity.anonymous
end

Instance Attribute Details

#cookiesObject

Returns the value of attribute cookies.



5
6
7
# File 'lib/tep/request.rb', line 5

def cookies
  @cookies
end

#http_versionObject

Returns the value of attribute http_version.



4
5
6
# File 'lib/tep/request.rb', line 4

def http_version
  @http_version
end

#identityObject

Set by the auth-filter (Tep::AuthFilter, run before the user’s before-filter – see Tep::App#auth_filter). Always populated: Tep::Identity.anonymous when no provider matched, otherwise the matched provider’s Identity. Handlers and filters can rely on req.identity being non-nil.



13
14
15
# File 'lib/tep/request.rb', line 13

def identity
  @identity
end

#ivarsObject

Returns the value of attribute ivars.



7
8
9
# File 'lib/tep/request.rb', line 7

def ivars
  @ivars
end

#paramsObject

Returns the value of attribute params.



5
6
7
# File 'lib/tep/request.rb', line 5

def params
  @params
end

#passedObject

Returns the value of attribute passed.



43
44
45
# File 'lib/tep/request.rb', line 43

def passed
  @passed
end

#pathObject

Returns the value of attribute path.



4
5
6
# File 'lib/tep/request.rb', line 4

def path
  @path
end

#queryObject

Returns the value of attribute query.



5
6
7
# File 'lib/tep/request.rb', line 5

def query
  @query
end

#raw_bodyObject

Returns the value of attribute raw_body.



5
6
7
# File 'lib/tep/request.rb', line 5

def raw_body
  @raw_body
end

#raw_pathObject

Returns the value of attribute raw_path.



4
5
6
# File 'lib/tep/request.rb', line 4

def raw_path
  @raw_path
end

#remote_hostObject

Returns the value of attribute remote_host.



6
7
8
# File 'lib/tep/request.rb', line 6

def remote_host
  @remote_host
end

#req_headersObject

Returns the value of attribute req_headers.



5
6
7
# File 'lib/tep/request.rb', line 5

def req_headers
  @req_headers
end

#sessionObject

Returns the value of attribute session.



5
6
7
# File 'lib/tep/request.rb', line 5

def session
  @session
end

#verbObject

Returns the value of attribute verb.



4
5
6
# File 'lib/tep/request.rb', line 4

def verb
  @verb
end

Instance Method Details

#acceptObject



93
# File 'lib/tep/request.rb', line 93

def accept;        @req_headers["accept"];      end

#bodyObject



52
# File 'lib/tep/request.rb', line 52

def body;    @raw_body;    end

#consume_body(client_fd) ⇒ Object

Pull any remaining body bytes from ‘client_fd` up to the advertised Content-Length, then merge form / multipart fields into @params. Used by Tep::Server (prefork, blocking fds) – under the prefork model recv() blocks naturally until bytes arrive, so `sphttp_drain_body` (a tight blocking-recv loop) is the right primitive.

Tep::Server::Scheduled uses ‘consume_body_via_scheduler` below instead, because its client fd is non-blocking + a blocking recv would starve the whole worker.

No-op on bodyless requests. Form parsing handles ‘application/x-www-form-urlencoded`; multipart handles `multipart/form-data` (text fields only; file uploads skipped). Other content types leave @raw_body intact for handlers that want to consume it directly.



128
129
130
131
132
133
134
135
136
137
# File 'lib/tep/request.rb', line 128

def consume_body(client_fd)
  cl = content_length
  already = @raw_body.length
  if cl > already
    rest = Sock.sphttp_drain_body(client_fd, cl - already)
    @raw_body = @raw_body + rest
  end
  parse_form_body
  0
end

#consume_body_via_scheduler(client_fd) ⇒ Object

Scheduler-friendly body drain. Loops on ‘Sock.sphttp_recv_some` + `Tep::Scheduler.io_wait` so other fibers keep running while we wait for body bytes. Per-recv timeout caps the wait at 5s – a client that opened the request but never sent the body gets dropped instead of hanging the fiber forever.

Returns @raw_body.length after the drain. Body parsing (form / multipart -> @params) happens at the end via parse_form_body, same shape as consume_body.



149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
# File 'lib/tep/request.rb', line 149

def consume_body_via_scheduler(client_fd)
  cl = content_length
  while @raw_body.length < cl
    ready = Tep::Scheduler.io_wait(client_fd, Tep::Scheduler::READ, 5)
    if ready == 0
      break   # timeout -- client never finished sending
    end
    chunk = Sock.sphttp_recv_some(client_fd, cl - @raw_body.length)
    if chunk.length == 0
      # Over TLS an empty read can be a partial record (SSL_read
      # WANT_READ/WANT_WRITE) rather than a peer close -- re-park on
      # the indicated direction and retry instead of truncating the
      # body. Plaintext EOF/error (status 3/-1) still breaks.
      st = Sock.sphttp_io_status
      if st == 1
        next
      end
      if st == 2
        Tep::Scheduler.io_wait(client_fd, Tep::Scheduler::WRITE, 5)
        next
      end
      break   # peer closed mid-body
    end
    @raw_body = @raw_body + chunk
  end
  parse_form_body
  0
end

#content_lengthObject



68
69
70
# File 'lib/tep/request.rb', line 68

def content_length
  @req_headers["content-length"].to_i
end

#content_typeObject



94
# File 'lib/tep/request.rb', line 94

def content_type;  @req_headers["content-type"]; end

#form?Boolean

Returns:

  • (Boolean)


72
73
74
# File 'lib/tep/request.rb', line 72

def form?
  @req_headers["content-type"].downcase.start_with?("application/x-www-form-urlencoded")
end

#headersObject

Sinatra-compat read aliases. Writers stay on the renamed slots (req_headers, raw_body) – a ‘req.headers = v` from user code goes through these getters, but assignment back into the request via this method name is intentionally not provided (the framework doesn’t expect handlers to mutate the request).



51
# File 'lib/tep/request.rb', line 51

def headers; @req_headers; end

#hostObject

—- Rack::Request-style accessors (reads only, no .ip yet) —- These are convenience getters over headers we already parse; ‘.ip` would need a sphttp_accept_with_peer C helper before it can land cleanly, so it’s deferred.



89
# File 'lib/tep/request.rb', line 89

def host;          @req_headers["host"];        end

#keep_alive?Boolean

Spinel’s Hash returns “” for missing string keys, not nil – so an empty Connection header looks the same as no header at all. We treat both as “use HTTP/1.1 default behaviour”.

Returns:

  • (Boolean)


57
58
59
60
61
62
63
64
65
66
# File 'lib/tep/request.rb', line 57

def keep_alive?
  lc = @req_headers["connection"].downcase
  if lc == "close"
    return false
  end
  if lc == "keep-alive"
    return true
  end
  @http_version == "HTTP/1.1"
end

#multipart?Boolean

True when the request body is a multipart/form-data submission (browsers use this for any form built via ‘new FormData(…)` or carrying file inputs). Tep::Multipart.parse handles the text fields; file-upload parts are skipped in v1.

Returns:

  • (Boolean)


80
81
82
# File 'lib/tep/request.rb', line 80

def multipart?
  @req_headers["content-type"].downcase.start_with?("multipart/form-data")
end

#parse_form_bodyObject

Shared form / multipart -> @params merge. Both server-side body-drain paths call this once their drain step has filled



181
182
183
184
185
186
187
188
189
190
191
192
# File 'lib/tep/request.rb', line 181

def parse_form_body
  if form?
    Url.parse_query(@raw_body).each do |k, v|
      @params[k] = v
    end
  elsif multipart?
    Tep::Multipart.parse(@raw_body, @req_headers["content-type"]).each do |k, v|
      @params[k] = v
    end
  end
  0
end

#refererObject



91
# File 'lib/tep/request.rb', line 91

def referer;       @req_headers["referer"];     end

#referrerObject

spelling alias



92
# File 'lib/tep/request.rb', line 92

def referrer;      @req_headers["referer"];     end

#schemeObject

tep doesn’t terminate TLS itself; both flags reflect “is this connection encrypted from the client’s view?” via the ‘X-Forwarded-Proto: https` header that any reasonable reverse proxy sets.



100
101
102
103
104
105
106
# File 'lib/tep/request.rb', line 100

def scheme
  proto = @req_headers["x-forwarded-proto"]
  if proto.length > 0
    return proto.downcase
  end
  "http"
end

#set_passedObject



44
# File 'lib/tep/request.rb', line 44

def set_passed; @passed = true; end

#ssl?Boolean

Returns:

  • (Boolean)


108
109
110
# File 'lib/tep/request.rb', line 108

def ssl?
  scheme == "https"
end

#user_agentObject



90
# File 'lib/tep/request.rb', line 90

def user_agent;    @req_headers["user-agent"];  end