Class: IntervalResponse::Sequence

Inherits:
Object
  • Object
show all
Defined in:
lib/interval_response/sequence.rb

Overview

An interval sequence represents a linear sequence of non-overlapping, joined intervals. For example, an HTTP response which consists of multiple edge included segments, or a timeline with clips joined together. Every interval contains a segment - an arbitrary object which responds to ‘#size` at time of adding to the IntervalSequence.

Defined Under Namespace

Classes: Interval

Constant Summary collapse

MULTIPART_GENRATOR_FINGERPRINT =
'boo'

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(*segments) ⇒ Sequence



14
15
16
17
18
# File 'lib/interval_response/sequence.rb', line 14

def initialize(*segments)
  @intervals = []
  @size = 0
  segments.each { |s| self << s }
end

Instance Attribute Details

#sizeObject (readonly)

Returns the value of attribute size.



12
13
14
# File 'lib/interval_response/sequence.rb', line 12

def size
  @size
end

Instance Method Details

#<<(segment) ⇒ Object



20
21
22
23
24
25
26
27
# File 'lib/interval_response/sequence.rb', line 20

def <<(segment)
  segment_size_or_bytesize = segment.respond_to?(:bytesize) ? segment.bytesize : segment.size
  return self if segment_size_or_bytesize == 0

  add_segment(segment, size: segment_size_or_bytesize)
  self
  self
end

#add_segment(segment, size:) ⇒ Object



29
30
31
32
# File 'lib/interval_response/sequence.rb', line 29

def add_segment(segment, size:)
  @intervals << Interval.new(segment, size, @size, @intervals.length)
  @size += size
end

#each_in_range(from_range_in_resource) ⇒ Object



34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
# File 'lib/interval_response/sequence.rb', line 34

def each_in_range(from_range_in_resource)
  # Skip empty ranges
  requested_range_size = (from_range_in_resource.end - from_range_in_resource.begin) + 1
  return if requested_range_size < 1

  # ...and if the range misses our intervals completely
  included_intervals = intervals_within_range(from_range_in_resource)

  # And normal case - walk through included intervals
  included_intervals.each do |interval|
    int_start = interval.offset
    int_end = interval.offset + interval.size - 1
    req_start = from_range_in_resource.begin
    req_end = from_range_in_resource.end
    range_within_interval = (max(int_start, req_start) - int_start)..(min(int_end, req_end) - int_start)

    # Allow Sequences to be composed together
    if interval.segment.respond_to?(:each_in_range)
      interval.segment.each_in_range(range_within_interval) do |sub_segment, sub_range|
        yield(sub_segment, sub_range)
      end
    else
      yield(interval.segment, range_within_interval)
    end
  end
end

#empty?Boolean



61
62
63
# File 'lib/interval_response/sequence.rb', line 61

def empty?
  @size == 0
end

#etagObject

For IE resumes to work, a strong ETag must be set in the response, and a strong comparison must be performed on it.

ETags have meaning with Range: requests, because when a client requests a range it will send the ETag back in the If-Range header. That header tells the server that “I want to have the ranges as emitted by the response representation that has output this etag”. This is done so that there is a guarantee that the same resource being requested has the same resource length (off of which the ranges get computed), and the ranges can be safely combined by the client. In practice this means that the ETag must contain some “version handle” which stays unchanged as long as the code responsible for generating the response does not change. In our case the response can change due to the following things:

  • The lengths of the segments change

  • The contents of the segments changes

  • Code that outputs the ranges themselves changes, and outputs different offsets of differently-sized resources. A resource can be differently sized since the MIME multiplart-byte-range response can have its boundary or per-part headers change, which affects the size of the MIME part headers. Even though the boundary is not a part of the resource itself, the sizes of the part headers do contribute to the envelope size - that should stay the same as long as the ETag holds.

It is important that the returned ETag is a strong ETag (not prefixed with ‘W/’) and must be enclosed in double-quotes.

See for more blogs.msdn.microsoft.com/ieinternals/2011/06/03/download-resumption-in-internet-explorer/



91
92
93
94
95
96
# File 'lib/interval_response/sequence.rb', line 91

def etag
  d = Digest::SHA1.new
  d << IntervalResponse::VERSION
  d << Marshal.dump(@intervals.map(&:size))
  '"%s"' % d.hexdigest
end