~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/HttpServer.py

  • Committer: Canonical.com Patch Queue Manager
  • Date: 2006-12-13 19:15:34 UTC
  • mfrom: (2183.1.1 Aaron's integration)
  • Revision ID: pqm@pqm.ubuntu.com-20061213191534-c948e178bdeb5d36
Make test HTTP server's range handling more spec-compliant (Vincent Ladeuil)

Show diffs side-by-side

added added

removed removed

Lines of Context:
88
88
    _tail_regexp = re.compile(r'^-(?P<tail>\d+)$')
89
89
 
90
90
    def parse_ranges(self, ranges_header):
91
 
        """Parse the range header value and returns ranges and tail"""
 
91
        """Parse the range header value and returns ranges and tail.
 
92
 
 
93
        RFC2616 14.35 says that syntactically invalid range
 
94
        specifiers MUST be ignored. In that case, we return 0 for
 
95
        tail and [] for ranges.
 
96
        """
92
97
        tail = 0
93
98
        ranges = []
94
 
        assert ranges_header.startswith('bytes=')
 
99
        if not ranges_header.startswith('bytes='):
 
100
            # Syntactically invalid header
 
101
            return 0, []
 
102
 
95
103
        ranges_header = ranges_header[len('bytes='):]
96
104
        for range_str in ranges_header.split(','):
 
105
            # FIXME: RFC2616 says end is optional and default to file_size
97
106
            range_match = self._range_regexp.match(range_str)
98
107
            if range_match is not None:
99
 
                ranges.append((int(range_match.group('start')),
100
 
                               int(range_match.group('end'))))
 
108
                start = int(range_match.group('start'))
 
109
                end = int(range_match.group('end'))
 
110
                if start > end:
 
111
                    # Syntactically invalid range
 
112
                    return 0, []
 
113
                ranges.append((start, end))
101
114
            else:
102
115
                tail_match = self._tail_regexp.match(range_str)
103
116
                if tail_match is not None:
104
117
                    tail = int(tail_match.group('tail'))
 
118
                else:
 
119
                    # Syntactically invalid range
 
120
                    return 0, []
105
121
        return tail, ranges
106
122
 
107
123
    def send_range_content(self, file, start, length):
167
183
        if tail != 0:
168
184
            ranges.append((file_size - tail, file_size))
169
185
 
170
 
        ranges_valid = True
 
186
        self._satisfiable_ranges = True
171
187
        if len(ranges) == 0:
172
 
            ranges_valid = False
 
188
            self._satisfiable_ranges = False
173
189
        else:
174
 
            for (start, end) in ranges:
175
 
                if start >= file_size or end >= file_size:
176
 
                    ranges_valid = False
177
 
                    break
178
 
        if not ranges_valid:
179
 
            # RFC2616 14.35 says that invalid Range headers must
180
 
            # be ignored. If they are, the whole file should be
181
 
            # returned as though no Range header was present. If
182
 
            # they aren't, the server should return a 416 error.
183
 
            # FIXME: per 14.35, ranges are only invalid if start > end.
184
 
            # end values should be truncated to file_size -1 if they exceed it.
185
 
            # only start values >= file_size should produce a 416.
 
190
            def check_range(range_specifier):
 
191
                start, end = range_specifier
 
192
                # RFC2616 14.35, ranges are invalid if start >= file_size
 
193
                if start >= file_size:
 
194
                    self._satisfiable_ranges = False # Side-effect !
 
195
                    return 0, 0
 
196
                # RFC2616 14.35, end values should be truncated
 
197
                # to file_size -1 if they exceed it
 
198
                end = min(end, file_size - 1)
 
199
                return start, end
 
200
 
 
201
            ranges = map(check_range, ranges)
 
202
 
 
203
        if not self._satisfiable_ranges:
 
204
            # RFC2616 14.16 and 14.35 says that when a server
 
205
            # encounters unsatisfiable range specifiers, it
 
206
            # SHOULD return a 416.
186
207
            file.close()
 
208
            # FIXME: We SHOULD send a Content-Range header too,
 
209
            # but the implementation of send_error does not
 
210
            # allows that. So far.
187
211
            self.send_error(416, "Requested range not satisfiable")
188
212
            return
189
213