Module: IntervalResponse

Defined in:
lib/interval_response.rb,
lib/interval_response/version.rb

Defined Under Namespace

Modules: ToRackResponseTriplet Classes: Empty, Error, Full, Invalid, LazyFile, Multi, Sequence, Single

Constant Summary collapse

ENTIRE_RESOURCE_RANGE =
'bytes=0-'
VERSION =
"0.1.2"

Class Method Summary collapse

Class Method Details

.new(interval_map, http_range_header_value_or_nil, http_if_range_header_or_nil) ⇒ Object



19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
# File 'lib/interval_response.rb', line 19

def self.new(interval_map, http_range_header_value_or_nil, http_if_range_header_or_nil)
  # If the 'If-Range' header is provided but does not match, discard the Range header. It means
  # that the client is requesting a certain representation of the resource and wants a range
  # _within_ that representation, but the representation has since changed and the offsets
  # no longer make sense. In that case we are supposed to answer with a 200 and the full
  # monty.
  if http_if_range_header_or_nil && http_if_range_header_or_nil != interval_map.etag
    Measurometer.increment_counter('interval_response.if_range_mismatch', 1)
    return new(interval_map, ENTIRE_RESOURCE_RANGE, nil)
  end

  if http_if_range_header_or_nil
    Measurometer.increment_counter('interval_response.if_range_match', 1)
  elsif http_range_header_value_or_nil
    Measurometer.increment_counter('interval_response.if_range_not_provided', 1)
  end

  prepare_response(interval_map, http_range_header_value_or_nil, http_if_range_header_or_nil).tap do |res|
    response_type_name_for_metric = res.class.to_s.split('::').last.downcase # Some::Module::Empty => empty
    Measurometer.increment_counter('interval_response.resp_%s' % response_type_name_for_metric, 1)
  end
end

.prepare_response(interval_map, http_range_header_value_or_nil, _http_if_range_header_or_nil) ⇒ Object



42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
# File 'lib/interval_response.rb', line 42

def self.prepare_response(interval_map, http_range_header_value_or_nil, _http_if_range_header_or_nil)
  # Case 1 - response of 0 bytes (empty resource).
  # We don't even have to parse the Range header for this since
  # the response will be the same, always.
  return Empty.new(interval_map) if interval_map.empty?

  # Parse the HTTP Range: header
  range_request_header = http_range_header_value_or_nil || ENTIRE_RESOURCE_RANGE
  http_ranges = Rack::Utils.get_byte_ranges(range_request_header, interval_map.size)

  # Case 2 - Client did send us a Range header, but Rack discarded
  # it because it is invalid and cannot be satisfied
  return Invalid.new(interval_map) if http_range_header_value_or_nil && http_ranges.empty?

  # Case 3 - entire resource
  return Full.new(interval_map) if http_ranges.length == 1 && http_ranges.first == (0..(interval_map.size - 1))

  # Case 4 - one content range
  return Single.new(interval_map, http_ranges) if http_ranges.length == 1

  # Case 5 - MIME multipart with multiple content ranges
  Multi.new(interval_map, http_ranges)
end