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



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

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.



138
139
140
141
142
143
144
145
146
147
# File 'lib/tep/request.rb', line 138

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.



159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
# File 'lib/tep/request.rb', line 159

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



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

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

#content_typeObject



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

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

#form?Boolean

Returns:

  • (Boolean)


77
78
79
80
81
# File 'lib/tep/request.rb', line 77

def form?
  ct = @req_headers["content-type"]
  ct = "" if ct.nil?
  ct.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["X"] = 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.



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

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

#keep_alive?Boolean

A missing header reads as nil (CRuby Hash semantics; spinel's unresolved-call gate raises NoMethodError on nil receivers, same as CRuby would). Guard before calling String methods; an empty header and no header both mean "use HTTP/1.1 default behaviour". NOTE: guard with x = "" if x.nil?, not x || "" -- the ||-form mis-compiles at spinel pin f6d5eef (blanks the hit value).

Returns:

  • (Boolean)


60
61
62
63
64
65
66
67
68
69
70
71
# File 'lib/tep/request.rb', line 60

def keep_alive?
  lc = @req_headers["connection"]
  lc = "" if lc.nil?
  lc = lc.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)


87
88
89
90
91
# File 'lib/tep/request.rb', line 87

def multipart?
  ct = @req_headers["content-type"]
  ct = "" if ct.nil?
  ct.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



191
192
193
194
195
196
197
198
199
200
201
202
# File 'lib/tep/request.rb', line 191

def parse_form_body
  if form?
    SpinelKit::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



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

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

#referrerObject

spelling alias



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

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.



109
110
111
112
113
114
115
116
# File 'lib/tep/request.rb', line 109

def scheme
  proto = @req_headers["x-forwarded-proto"]
  proto = "" if proto.nil?
  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)


118
119
120
# File 'lib/tep/request.rb', line 118

def ssl?
  scheme == "https"
end

#user_agentObject



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

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