Class: Rackful::Request

Inherits:
Rack::Request show all
Defined in:
lib/rackful/request.rb

Overview

Subclass of Rack::Request, augmented for Rackful requests.

Constant Summary

Instance Attribute Summary (collapse)

Instance Method Summary (collapse)

Constructor Details

- (Request) initialize(*args)

Returns a new instance of Request



10
11
12
# File 'lib/rackful/request.rb', line 10

def initialize *args
  super( *args )
end

Instance Attribute Details

- (void) env (readonly) Originally defined in class Rack::Request

The environment of the request.

Instance Method Details

- (Hash{media_type => quality}) accept

Deprecated.

Use #q_values instead

Hash of acceptable media types and their qualities.

This method parses the HTTP/1.1 Accept: header. If no acceptable media types are provided, an empty Hash is returned.

Returns:

  • (Hash{media_type => quality})


196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
# File 'lib/rackful/request.rb', line 196

def accept
  env['rackful.accept'] ||= begin
    Hash[
      env['HTTP_ACCEPT'].to_s.split(',').collect do
        |entry|
        type, *options = entry.delete(' ').split(';')
        quality = 1
        options.each { |e|
          quality = e[2..-1].to_f if e.start_with? 'q='
        }
        [type, quality]
      end
    ]
  rescue
    {}
  end
end

- assert_if_headers(resource)

TODO:

Implement support for the If-Range: header.

This method returns an undefined value.

Assert all If-* request headers.

Raises:

See Also:



70
71
72
73
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
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
# File 'lib/rackful/request.rb', line 70

def assert_if_headers resource
  #raise HTTP501NotImplemented, 'If-Range: request header is not supported.' \
  #  if env.key? 'HTTP_IF_RANGE'
  empty = resource.empty?
  etag =
    if ! empty && resource.respond_to?(:get_etag)
      resource.get_etag
    else
      nil
    end
  last_modified =
    if ! empty && resource.respond_to?(:get_last_modified)
      resource.get_last_modified
    else
      nil
    end
  cond = {
    :match => self.if_match,
    :none_match => self.if_none_match,
    :modified_since => self.if_modified_since,
    :unmodified_since => self.if_unmodified_since
  }
  allow_weak = ['GET', 'HEAD'].include? self.request_method
  if empty
    if cond[:match]
      raise HTTP412PreconditionFailed, 'If-Match'
    elsif cond[:unmodified_since]
      raise HTTP412PreconditionFailed, 'If-Unmodified-Since'
    elsif cond[:modified_since]
      raise HTTP404NotFound
    end
  else
    if cond[:none_match] && self.validate_etag( etag, cond[:none_match] )
      if allow_weak
        raise HTTP304NotModified
      else
        raise HTTP412PreconditionFailed, 'If-None-Match'
      end
    elsif cond[:match] && ! self.validate_etag( etag, cond[:match] )
      raise HTTP412PreconditionFailed, 'If-Match'
    elsif cond[:unmodified_since]
      if ! last_modified || cond[:unmodified_since] < last_modified[0]
        raise HTTP412PreconditionFailed, 'If-Unmodified-Since'
      elsif last_modified && ! last_modified[1] && ! allow_weak &&
            cond[:unmodified_since] == last_modified[0]
        raise HTTP412PreconditionFailed, 'If-Unmodified-Since'
      end
    elsif cond[:modified_since]
      if ! last_modified || cond[:modified_since] >= last_modified[0]
        raise HTTP304NotModified
      elsif last_modified && ! last_modified[1] && !allow_weak &&
            cond[:modified_since] == last_modified[0]
        raise HTTP412PreconditionFailed, 'If-Modified-Since'
      end
    end
  end
end

- (URI::Generic) canonical_uri

Similar to the HTTP/1.1 Content-Location: header. Contains the canonical url of the requested resource, which may differ from Rack::Request#url.

If parameter full_path is provided, than this is used instead of the current request’s full path (which is the path plus optional query string).

Returns:



35
36
37
# File 'lib/rackful/request.rb', line 35

def canonical_uri
  env['rackful.canonical_uri'] ||= URI( self.url ).normalize
end

- (URI::Generic, String) canonical_uri=(uri)

TODO:

Change URI::Generic into URI::HTTP

The canonical url of the requested resource. This may differ from Rack::Request#url.

Parameters:

Returns:



47
48
49
50
51
# File 'lib/rackful/request.rb', line 47

def canonical_uri=( uri )
  env['rackful.canonical_uri'] =
    uri.kind_of?( URI::Generic ) ? URI(uri) : URI( uri ).normalize
  uri
end

- (nil, Array<String>) if_match

Parses the HTTP/1.1 If-Match: header.

Returns:

  • (nil, Array<String>)

Raises:

See Also:



222
223
224
225
226
227
228
229
230
231
232
# File 'lib/rackful/request.rb', line 222

def if_match none = false
  header = env["HTTP_IF_#{ none ? 'NONE_' : '' }MATCH"]
  return nil unless header
  envkey = "rackful.if_#{ none ? 'none_' : '' }match"
  if %r{\A\s*\*\s*\z} === header
    return [ '*' ]
  elsif %r{\A(\s*(W/)?"([^"\\]|\\.)*"\s*,)+\z}m === ( header + ',' )
    return header.scan( %r{(?:W/)?"(?:[^"\\]|\\.)*"}m )
  end
  raise HTTP400BadRequest, "Couldn't parse If-#{ none ? 'None-' : '' }Match: #{header}"
end

- (nil, Time) if_modified_since

Returns:

  • (nil, Time)

See Also:



252
253
254
255
256
257
258
259
260
261
# File 'lib/rackful/request.rb', line 252

def if_modified_since unmodified = false
  header = env["HTTP_IF_#{ unmodified ? 'UN' : '' }MODIFIED_SINCE"]
  return nil unless header
  begin
    header = Time.httpdate( header )
  rescue ArgumentError
    raise HTTP400BadRequest, "Couldn't parse If-#{ unmodified ? 'Unmodified' : 'Modified' }-Since: #{header}"
  end
  header
end

- (nil, Array<String>) if_none_match

Parses the HTTP/1.1 If-None-Match: header.

Returns:

  • (nil, Array<String>)

See Also:



241
242
243
# File 'lib/rackful/request.rb', line 241

def if_none_match
  self.if_match true
end

- (nil, Time) if_unmodified_since

Returns:

  • (nil, Time)

See Also:



269
270
271
# File 'lib/rackful/request.rb', line 269

def if_unmodified_since
  self.if_modified_since true
end

- (Array<Array(type, quality)>) q_values

Shortcut to Rack::Utils.q_values. Well, actually, we reimplemented it because the implementation in Rack::Utils seems incomplete.

Returns:

  • (Array<Array(type, quality)>)

See Also:



133
134
135
136
137
138
139
140
141
142
143
144
145
146
# File 'lib/rackful/request.rb', line 133

def q_values
  # This would be the “shortcut” implementation:
  #env['rackful.q_values'] ||= Rack::Utils.q_values(env['HTTP_ACCEPT'])
  # But here’s a full (and better) implementation:
  env['rackful.q_values'] ||= env['HTTP_ACCEPT'].to_s.split(/\s*,\s*/).map do
    |part|
    value, *parameters = part.split(/\s*;\s*/)
    quality = 1.0
    parameters.each do |p|
      quality = p[2..-1].to_f if p.start_with? 'q='
    end
    [value, quality]
  end
end

- (Resource) resource_at(*args)

Shortcut to Server#resource_at.

(see Server#resource_at)

Returns:

Raises:



22
23
24
# File 'lib/rackful/request.rb', line 22

def resource_at *args
  env['rackful.server'].resource_at( *args )
end

- (Boolean) validate_etag(etag, etags)

Does any of the tags in etags match etag?

Examples:

etag = '"foo"'
etags = [ 'W/"foo"', '"bar"' ]
validate_etag etag, etags
#> true

Parameters:

  • etag (#to_s)
  • etags (#to_a)

Returns:

  • (Boolean)

See Also:



287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
# File 'lib/rackful/request.rb', line 287

def validate_etag etag, etags
  etag = etag.to_s
  match = etags.to_a.detect do
    |tag|
    tag = tag.to_s
    tag == '*' or
    tag == etag or
    'W/' +  tag == etag or
    'W/' + etag ==  tag
  end
  if  match and
      '*' != match and
      'W/' == etag[0,2] || 'W/' == match[0,2] and
      ! [ 'HEAD', 'GET' ].include? self.request_method
    raise HTTP400BadRequest, "Weak validators are only allowed for GET and HEAD requests."
  end
  !!match
end