2005-11-25
2005-11-25 17:52:00 by Henrik Grubbström (Grubba) <grubba@grubba.org>
-
496c841f5fd42d294676b637f61484683947c88e
(224 lines)
(+125/-99)
[
Show
| Annotate
]
Branch: 5.2
The RAM cache now supports byte ranges.
Rev: server/protocols/http.pike:1.482
2:
// Modified by Francesco Chemolli to add throttling capabilities.
// Copyright © 1996 - 2004, Roxen IS.
- constant cvs_version = "$Id: http.pike,v 1.481 2005/11/25 16:14:42 grubba Exp $";
+ constant cvs_version = "$Id: http.pike,v 1.482 2005/11/25 17:52:00 grubba Exp $";
// #define REQUEST_DEBUG
#define MAGIC_ERROR
284:
}
- void send (string|object what, int|void len)
+ void send(string|object what, int|void len)
{
if( len>0 && port_obj && port_obj->minimum_byterate )
call_out( end, len / port_obj->minimum_byterate );
1509:
array range_info = ({});
string type;
string stored_data = "";
- void create(mapping _file, mapping heads, array _ranges,
- array(string)|string t, object id)
+ void create(mapping _file, array _ranges, array(string)|string t, object id)
{
file = _file->file;
len = _file->len;
1642:
return ranges;
}
+
+ // Handle byte ranges.
+ // NOTE: Modifies both arguments destructively.
+ void handle_byte_ranges(mapping(string:mixed) file,
+ mapping(string:array(string)|string) variant_heads)
+ {
+ if(misc->range && file->len && (method == "GET") &&
+ (file->error == 200) && (objectp(file->file) || file->data))
+ // Plain and simple file and a Range header. Let's play.
+ // Also we only bother with 200-requests. Anything else should be
+ // nicely and completely ignored.
+ // Also this is only used for GET requests.
+ {
+ // split the range header. If no valid ranges are found, ignore it.
+ // If one is found, send that range. If many are found we need to
+ // use a wrapper and send a multi-part message.
+ array ranges = parse_range_header(file->len);
+ if(ranges) // No incorrect syntax...
+ {
+ misc->no_proto_cache = 1;
+ if(sizeof(ranges)) // And we have valid ranges as well.
+ {
+ m_delete(variant_heads, "Content-Length");
+
+ file->error = 206; // 206 Partial Content
+ if(sizeof(ranges) == 1)
+ {
+ variant_heads["Content-Range"] = sprintf("bytes %d-%d/%d",
+ @ranges[0],
+ file->len);
+ if (objectp(file->file)) {
+ file->file->seek(ranges[0][0]);
+ } else {
+ file->data = file->data[ranges[0][0]..ranges[0][1]];
+ }
+ if(ranges[0][1] == (file->len - 1) &&
+ GLOBVAR(RestoreConnLogFull))
+ // Log continuations (ie REST in FTP), 'range XXX-'
+ // using the entire length of the file, not just the
+ // "sent" part. Ie add the "start" byte location when logging
+ misc->_log_cheat_addition = ranges[0][0];
+ file->len = ranges[0][1] - ranges[0][0]+1;
+ } else {
+ // Multiple ranges. Multipart reply and stuff needed.
+
+ array(string)|string content_type =
+ variant_heads["Content-Type"] || "application/octet-stream";
+
+ if(request_headers["request-range"]) {
+ // Compat with old Netscape.
+ variant_heads["Content-Type"] =
+ "multipart/x-byteranges; boundary=" BOUND;
+ } else {
+ variant_heads["Content-Type"] =
+ "multipart/byteranges; boundary=" BOUND;
+ }
+
+ if (objectp(file->file)) {
+ // We do this by replacing the file object with a wrapper.
+ // Nice and handy.
+ file->file = MultiRangeWrapper(file, ranges,
+ content_type, this_object());
+ } else {
+ array(string) res = allocate(sizeof(ranges)*3+1);
+ mapping(string:string) part_heads = ([
+ "Content-Type":content_type,
+ ]);
+ int j;
+ foreach(ranges; int i; array(int) range) {
+ res[j++] = "\r\n--" BOUND "\r\n";
+ part_heads["Content-Range"] =
+ sprintf("bytes %d-%d/%d", @range, file->len);
+ res[j++] = Roxen.make_http_headers(part_heads);
+ res[j++] = data[range[0]..range[1]];
+ }
+ res[j++] = "\r\n--" BOUND "\r\n";
+ file->len = sizeof(file->data = res * "");
+ variant_heads["Content-Length"] = (string)file->len;
+ }
+ }
+ } else {
+ // Got the header, but the specified ranges were out of bounds.
+ // Reply with a 416 Requested Range not satisfiable.
+ file->error = 416;
+ variant_heads["Content-Range"] = "*/"+file->len;
+ file->file = file->data = file->type = file->len = 0;
+ }
+ }
+ }
+ }
+
// Tell the client that it can start sending some more data
void ready_to_receive()
{
1978:
file->file = 0;
} else {
#ifdef RAM_CACHE
- if( (misc->cacheable > 0) && (file->data || file->file) &&
- (prot != "HTTP/0.9") && !misc->no_proto_cache)
+ if( (misc->cacheable > 0) && !misc->no_proto_cache)
{
if( file->len>0 && // known length.
((file->len + sizeof(head_string)) <
2020: Inside #if defined(RAM_CACHE)
}
}
#endif
- if(misc->range && file->len && (method == "GET") &&
- (file->error == 200) && (objectp(file->file) || file->data))
- // Plain and simple file and a Range header. Let's play.
- // Also we only bother with 200-requests. Anything else should be
- // nicely and completely ignored.
- // Also this is only used for GET requests.
- {
- // split the range header. If no valid ranges are found, ignore it.
- // If one is found, send that range. If many are found we need to
- // use a wrapper and send a multi-part message.
- array ranges = parse_range_header(file->len);
- if(ranges) // No incorrect syntax...
- {
- misc->no_proto_cache = 1;
- if(sizeof(ranges)) // And we have valid ranges as well.
- {
- m_delete(variant_heads, "Content-Length");
-
- file->error = 206; // 206 Partial Content
- if(sizeof(ranges) == 1)
- {
- variant_heads["Content-Range"] = sprintf("bytes %d-%d/%d",
- @ranges[0],
- file->len);
- if (objectp(file->file)) {
- file->file->seek(ranges[0][0]);
- } else {
- file->data = file->data[ranges[0][0]..ranges[0][1]];
+ if (misc->range) {
+ // Handle byte ranges.
+ // NOTE: Modifies both arguments destructively.
+ handle_byte_ranges(file, variant_heads);
}
- if(ranges[0][1] == (file->len - 1) &&
- GLOBVAR(RestoreConnLogFull))
- // Log continuations (ie REST in FTP), 'range XXX-'
- // using the entire length of the file, not just the
- // "sent" part. Ie add the "start" byte location when logging
- misc->_log_cheat_addition = ranges[0][0];
- file->len = ranges[0][1] - ranges[0][0]+1;
- } else {
- // Multiple ranges. Multipart reply and stuff needed.
-
- array(string)|string content_type =
- variant_heads["Content-Type"] || "application/octet-stream";
-
- if(request_headers["request-range"]) {
- // Compat with old Netscape.
- variant_heads["Content-Type"] =
- "multipart/x-byteranges; boundary=" BOUND;
- } else {
- variant_heads["Content-Type"] =
- "multipart/byteranges; boundary=" BOUND;
+
}
- if (objectp(file->file)) {
- // We do this by replacing the file object with a wrapper.
- // Nice and handy.
- file->file = MultiRangeWrapper(file, heads, ranges,
- content_type, this_object());
- } else {
- array(string) res = allocate(sizeof(ranges)*3+1);
- mapping(string:string) part_heads = ([
- "Content-Type":content_type,
- ]);
- int j;
- foreach(ranges; int i; array(int) range) {
- res[j++] = "\r\n--" BOUND "\r\n";
- part_heads["Content-Range"] =
- sprintf("bytes %d-%d/%d", @range, file->len);
- res[j++] = Roxen.make_http_headers(part_heads);
- res[j++] = data[range[0]..range[1]];
- }
- res[j++] = "\r\n--" BOUND "\r\n";
- file->len = sizeof(file->data = res * "");
- variant_heads["Content-Length"] = (string)file->len;
- }
- }
- } else {
- // Got the header, but the specified ranges were out of bounds.
- // Reply with a 416 Requested Range not satisfiable.
- file->error = 416;
- variant_heads["Content-Range"] = "*/"+file->len;
- file->file = file->data = file->type = file->len = 0;
- }
- }
- }
- }
-
+
variant_string = Roxen.make_http_headers(variant_heads);
full_headers = prot + " " + file->error + head_string + variant_string;
- REQUEST_WERR(sprintf("HTTP: full_headers: %O\n"
- "HTTP: file: %O\n",
- full_headers, file));
+
low_send_result(full_headers, file->data, file->len, file->file);
}
else {
// RAW or HTTP/0.9 mode.
- // No headers!
+
if(!file->type) file->type="text/plain";
-
+
+ // No headers!
low_send_result("", file->data, file->len, file->file);
}
2496: Inside #if defined(RAM_CACHE)
if (method == "HEAD") {
d = "";
}
- string full_headers = prot + " " + code + file->hs +
- Roxen.make_http_headers(([
+ mapping(string:string) variant_heads = ([
"Date":Roxen.http_date(predef::time(1)),
"Content-Length":(string)len,
"Content-Type":file->type,
"Connection":misc->connection ||
([ "HTTP/1.1":"keep-alive" ])[prot] || "close",
- ]));
+ ]);
+ if (misc->range) {
+ // Handle byte ranges.
+ // NOTE: Modifies both arguments destructively.
-
+ // Make sure we don't mess with the RAM cache.
+ file += ([]);
+ file->error = code;
+ file->data = d;
+ file->len = len;
+
+ handle_byte_ranges(file, variant_heads);
+
+ d = file->data;
+ code = file->error;
+ }
+ string full_headers = prot + " " + code + file->hs +
+ Roxen.make_http_headers(variant_heads);
+
MY_TRACE_LEAVE ("Using entry from ram cache");
cache_status["protcache"] = 1;