Class: Apache::Request

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

Overview

Request extends the Apache::Request object C-extension. It's main purpose to provide additional convenience functions which are not part of the core Apache request structure. From the programmer's perspctive in Ruby, the C-extension and the Ruby implemenation appear as a single class.

So this Ruby extension mainly consists of helpful functions to perform many common tasks such as:

  • Get file, request, and domain information.

  • Access query and/or POST paramters

  • Manage (set/get/delete) cookies

  • Peform common HTTP tasks using a high-level interface -- redirect, page expiry, etc.

  • Provide low-level access to HTTP headers (in and out)

  • Provide a source to send content back to the client, all at once or incrementally (chunked).

The Request class is instantiated within the framework for each request (inside of a Turnstile instance within in mod_rsp's handler method).

Instance Attribute Summary (collapse)

Instance Method Summary (collapse)

Instance Attribute Details

- (Object) out

Returns the value of attribute out



28
29
30
# File 'lib/core/r4a/request.rb', line 28

def out
  @out
end

Instance Method Details

- (Object) bdg



129
130
131
# File 'lib/core/r4a/request.rb', line 129

def bdg()
  return binding
end

- (Object) boundary

Returns the multi-part boundary given in the Content-Type header, if exists.



302
303
304
305
306
307
308
# File 'lib/core/r4a/request.rb', line 302

def boundary()
  ct = self.headers_in['Content-Type']
  
  if ct != nil
    return '--' + ct.split('boundary=')[1]
  end
end

- (Object) clearCookie(name)

Clears a cookie. Just sets a cookie with an expiry in the past, which should cause the browser should then clear it out.



202
203
204
# File 'lib/core/r4a/request.rb', line 202

def clearCookie(name)
  setCookie(name, '', -1)
end

Get a cookie by the name of name



159
160
161
162
163
# File 'lib/core/r4a/request.rb', line 159

def cookie(name)
  if self.cookies().has_key?(name)
    return self.cookies()[name]
  end
end

- (Object) cookies



137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
# File 'lib/core/r4a/request.rb', line 137

def cookies()
  
  return @cookies if @cookies

  @cookies  = {}
  text      = self.headers_in['Cookie']

  if text != nil
    text.split(';').each do |cookie|
      k,v = cookie.split('=')
      if v == nil
        @cookies[k.strip] = nil
      else
        @cookies[k.strip] = v.strip
      end
    end
  end

  return @cookies
end

- (Object) copyErrorHeaders

Transfer headers to err_headers



388
389
390
391
392
# File 'lib/core/r4a/request.rb', line 388

def copyErrorHeaders()
  self.headers_out.each do |k, v|
    self.err_headers_out.add(k,v)
  end
end

- (Object) dir



114
115
116
117
118
119
120
121
122
# File 'lib/core/r4a/request.rb', line 114

def dir()
  ospath = self.cgi['SCRIPT_NAME']
  ospath = ospath[0..ospath.rindex('/')]

  # Shave the ending / if there
  ospath = ospath[0..-2] || '' if ospath[-1] == '/'

  return ospath
end

- (Object) documentRoot



110
111
112
# File 'lib/core/r4a/request.rb', line 110

def documentRoot()
  return self.cgi['DOCUMENT_ROOT']
end

- (Object) domainName



74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
# File 'lib/core/r4a/request.rb', line 74

def domainName()
  return @domainName if @domainName

  if hostName().index('.') != nil
    # Decompose domain into list
    domainParts = @hostName.split('.')

    # Reconstitute last two parts for domain name
    @domainName = domainParts[-2..-1].join('.')

    # If there is more than 2 elements, use the 1st as the hostName
    if domainParts.size > 2
      @hostName = domainParts[-3]
    end
  else
    @domainName = @hostName
  end

  # If there is a port number, trim it
  if @domainName.index(':') != nil
    @domainName = @domainName[0..@domainName.index(':')-1]
  end
  
  return @domainName
end

- (Object) dontCache

Set the expiration time to now so the page is not cached.



373
374
375
376
# File 'lib/core/r4a/request.rb', line 373

def dontCache()
  self.headers_out['Expires'] = Time.now().httpdate()
  self.headers_out['Cache-Control']  = 'no-cache'
end

- (Object) expirationDate(days, minutes = 0)

Compute and format a RFC 1123 expiration date days days in the future.



395
396
397
398
399
400
# File 'lib/core/r4a/request.rb', line 395

def expirationDate(days, minutes=0)
  secs_per_day = 86400
  
  t = Time.now() + days*secs_per_day + minutes*60
  return t.httpdate()
end

- (Object) expires(t)

Redirect the client to url.



368
369
370
# File 'lib/core/r4a/request.rb', line 368

def expires(t)
  self.headers_out['Expires'] = t.rfc2822()
end

- (Object) file



124
125
126
127
# File 'lib/core/r4a/request.rb', line 124

def file()
  ospath = self.cgi['SCRIPT_NAME']
  file = ospath[ospath.rindex('/') + 1 .. -1]    
end

- (Object) flush

Forces content through Apache back to client.



379
380
381
# File 'lib/core/r4a/request.rb', line 379

def flush()
  self.rflush()
end

- (Boolean) hasValue?(name, value = nil)

Searches for a variable with the name of name in params, then @queries, and then cgi. Returns true for the first find, false otherwise.

Returns:

  • (Boolean)


213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
# File 'lib/core/r4a/request.rb', line 213

def hasValue?(name, value=nil)

  if self.params.has_key?(name)

    # Special Case: If a specific value is provided, we need to check for its
    # existence. It's not enough that just they key exists. There may be
    # multiple values for a given key and the caller wants to know of the
    # specific key AND value exist.
    if value != nil
      # Iterate through all elements with key=name
      self.params.each do |k,v|
        if k == name and v == value
          return true
        end
      end
      
      return false
    end

    return true
  end

  if self.queries.has_key?(name)
    return true
  end

  if self.cgi.has_key?(name)
    return true
  end

  return false
end

- (Object) hostName

First look to see if there is a HOST value set in the HTTP headers. If there is one, we use that. If there isn't, then we fall back on the value specified by the virtual host entry in @serverName. This is the canonical server name. The reason we do it this way is because of ServerAlias's in the webserver. If there are ServerAlias's defined in which other domain names are accepted for a site, then the URL in the the client's browser will not match with the canonical server name. This means that setting cookies WILL NOT WORK because browsers will not accept cookies that when the cookie's domain name does not match the domain name in the URL. So, to get around this, we accept the domain name provided by the client, and make that the domain name we set cookies by. Keep in mind that if a client comes back to this same page through another aliased domain name then THE OLD COOKIE WILL NOT BE FOUND, as it has a different domain name. The best way to keep things simple and clean is to set up ServerAliases to redirect to the canonical domain name. In either case, the code here will ensure that cookies work properly (as possible).



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

def hostName()
  
  @hostName ||= nil

  return @hostName if @hostName

  if self.cgi.has_key?('HTTP_HOST')
    @hostName = self.cgi['HTTP_HOST']
  else
    @hostName = serverName()
  end
  
  return @hostName
end

- (Object) jujifruit

Basic extension test. Add method jujifruit(). This proves that the C API request struct can be extended in Ruby.



32
33
34
# File 'lib/core/r4a/request.rb', line 32

def jujifruit()
  return 'yumyum'
end

- (Object) method



133
134
135
# File 'lib/core/r4a/request.rb', line 133

def method()
  self.cgi['REQUEST_METHOD']
end

- (Object) redirect(url)

Redirect the client to url.

Raises:



323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
# File 'lib/core/r4a/request.rb', line 323

def redirect(url)

  # Set the Apache req.status
  self.set_status(302)

  # Also set it in the headers, just in case
  self.headers_out['Status']   = "302"
  self.headers_out['Location'] = url

  # TODO: This following comment seems to be invalid due to unit testing
  # results. Need to confirm what the proper behavior is. This applied to
  # method below as well.

  # This is required to get Set-Cookie out. Since we are redirecting, we are
  # sending a 302 response. Apache will only send headers_out for 200 response
  # codes. For everything else it uses err_headers_out. Therefore, we have to
  # manually copy our headers into err_headers_out using copyErrorHeaders().

  # copyErrorHeaders()

  # This will terminate subsequent request processing
  raise R4A::Redirect.new(url)
end

- (Object) referrer



100
101
102
103
104
105
106
107
108
# File 'lib/core/r4a/request.rb', line 100

def referrer()
  return @referrer if @referrer 

  if self.cgi.has_key?('HTTP_REFERER')
    @referrer = self.cgi['HTTP_REFERER']
  end

  return @referrer
end

- (Object) serverName



36
37
38
# File 'lib/core/r4a/request.rb', line 36

def serverName()
  return self.cgi['SERVER_NAME']
end

- (Object) setCookie(name, value, days = 0, minutes = 0, path = nil)

Sets a cookie. Parameters are as follows:

name

Name of cookie

value

Value of cookie

days

Days from today to expiration date

minutes

Minutes to add from today to expire

path

Relative path (to document root)

The expiration date is determined by days+minutes. If days = -1 and minutes = -1, then it sets the expiry to 2038 Rather than use this convention, use clearCookie() in code.



177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
# File 'lib/core/r4a/request.rb', line 177

def setCookie( name, value,
               days=0, minutes=0,
               path=nil )
    
  if path == nil
    path = '/'
  end

  cookieString = "%s=%s;path=%s;domain=.%s" 
  cookieString = cookieString % [name, value, path, domainName()]
  
  if days != 0 or minutes != 0
    if days==-1 and minutes==-1
      cookieString += ";Expires=Sun, 17-Jan-2038 19:14:07 -0600"
    else
      cookieString += ";Expires=%s" % expirationDate(days, minutes)
    end
  end

  # Append this cookie to the array
  self.headers_out.add('Set-Cookie', cookieString)
end

- (Object) setStatus(code)

Set the HTTP status code



348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
# File 'lib/core/r4a/request.rb', line 348

def setStatus(code)

  # Set the Apache req.status
  self.set_status(code.to_i)

  # Also set it in the headers, just in case
  self.headers_out['Status']   = code.to_s

  # Apache will only send headers_out for 200 response codes. For everything
  # else it uses err_headers_out. Therefore, we have to manually copy our
  # headers into err_headers_out using copyErrorHeaders().

  # TODO: This does not appear to be true according to unit testing. See
  # testreq.cpp (TestReq::errorHeadersTest()).

  # Temporarily commenting this out as it seems to *duplicate* headers.
  # copyErrorHeaders()
end

- (Object) terminate

Terminates subsequent request processing. This causes all further Ruby code to stop and whatever is in the request buffer is flushed and sent back to the client. It is not an error, just the termination of all further processing no matter how far along in the stack it has proceeded. Flow execution returns to the Apache module handler which cleans up and finalizes the request.



318
319
320
# File 'lib/core/r4a/request.rb', line 318

def terminate()
  raise R4A::RequestTermination.new()
end

- (Object) value(name, default = nil)

Searches for a variable with the name of name in @params, then @queries, and then cgi. Returns the value of the first find. If it does not find a match, it retrurns the value of of the default parameter (which itself defaults to nil).

Returns a scalar value.



253
254
255
256
257
258
259
260
# File 'lib/core/r4a/request.rb', line 253

def value(name, default=nil)

  if hasValue?(name) == true
    return values(name)[0]
  end

  return default
end

- (Object) values(name, default = nil)

Searches for a variable with the name of name in @params, then @queries, and then @cgi. Returns the value of the first find. If it does not find a match, it returns the value of of the default parameter (which itself defaults to nil).

Returns an Array value.



269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
# File 'lib/core/r4a/request.rb', line 269

def values(name, default=nil)

  # Post
  if self.params.has_key?(name)
    results = []
    # Collect all elements with key=name
    self.params.each do |k,v|
      if k == name
        results.push v
      end
    end

    return results
  end

  # Queries
  if self.queries.has_key?(name)
    return [self.queries[name]]
  end

  # Environmental variables
  if self.cgi.has_key?(name)
    return [self.cgi[name]]
  end

  return default
end