dom/downloads/tests/serve_file.sjs

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

     1 // Serves a file with a given mime type and size at an optionally given rate.
     3 function getQuery(request) {
     4   var query = {};
     5   request.queryString.split('&').forEach(function (val) {
     6     var [name, value] = val.split('=');
     7     query[name] = unescape(value);
     8   });
     9   return query;
    10 }
    12 function handleResponse() {
    13   // Is this a rate limited response?
    14   if (this.state.rate > 0) {
    15     // Calculate how many bytes we have left to send.
    16     var bytesToWrite = this.state.totalBytes - this.state.sentBytes;
    18     // Do we have any bytes left to send? If not we'll just fall thru and
    19     // cancel our repeating timer and finalize the response.
    20     if (bytesToWrite > 0) {
    21       // Figure out how many bytes to send, based on the rate limit.
    22       bytesToWrite =
    23         (bytesToWrite > this.state.rate) ? this.state.rate : bytesToWrite;
    25       for (let i = 0; i < bytesToWrite; i++) {
    26         try {
    27           this.response.bodyOutputStream.write("0", 1);
    28         } catch (e) {
    29           // Connection was closed by client.
    30           if (e == Components.results.NS_ERROR_NOT_AVAILABLE) {
    31             // There's no harm in calling this multiple times.
    32             this.response.finish();
    34             // It's possible that our timer wasn't cancelled in time
    35             // and we'll be called again.
    36             if (this.timer) {
    37               this.timer.cancel();
    38               this.timer = null;
    39             }
    41             return;
    42           }
    43         }
    44       }
    46       // Update the number of bytes we've sent to the client.
    47       this.state.sentBytes += bytesToWrite;
    49       // Wait until the next call to do anything else.
    50       return;
    51     }
    52   }
    53   else {
    54     // Not rate limited, write it all out.
    55     for (let i = 0; i < this.state.totalBytes; i++) {
    56       this.response.write("0");
    57     }
    58   }
    60   // Finalize the response.
    61   this.response.finish();
    63   // All done sending, go ahead and cancel our repeating timer.
    64   this.timer.cancel();
    66   // Clear the timer.
    67   this.timer = null;
    68 }
    70 function handleRequest(request, response) {
    71   var query = getQuery(request);
    73   // sending at a specific rate requires our response to be asynchronous so
    74   // we handle all requests asynchronously. See handleResponse().
    75   response.processAsync();
    77   // Default status when responding.
    78   var version = "1.1";
    79   var statusCode = 200;
    80   var description = "OK";
    82   // Default values for content type, size and rate.
    83   var contentType = "text/plain";
    84   var contentRange = null;
    85   var size = 1024;
    86   var rate = 0;
    88   // optional content type to be used by our response.
    89   if ("contentType" in query) {
    90     contentType = query["contentType"];
    91   }
    93   // optional size (in bytes) for generated file.
    94   if ("size" in query) {
    95     size = parseInt(query["size"]);
    96   }
    98   // optional range request check.
    99   if (request.hasHeader("range")) {
   100     version = "1.1";
   101     statusCode = 206;
   102     description = "Partial Content";
   104     // We'll only support simple range byte style requests.
   105     var [offset, total] = request.getHeader("range").slice("bytes=".length).split("-");
   106     // Enforce valid Number values.
   107     offset = parseInt(offset);
   108     offset = isNaN(offset) ? 0 : offset;
   109     // Same.
   110     total = parseInt(total);
   111     total = isNaN(total) ? 0 : total;
   113     // We'll need to original total size as part of the Content-Range header
   114     // value in our response.
   115     var originalSize = size;
   117     // If we have a total size requested, we must make sure to send that number
   118     // of bytes only (minus the start offset).
   119     if (total && total < size) {
   120       size = total - offset;
   121     } else if (offset) {
   122       // Looks like we just have a byte offset to deal with.
   123       size = size - offset;
   124     }
   126     // We specifically need to add a Content-Range header to all responses for
   127     // requests that include a range request header.
   128     contentRange = "bytes " + offset + "-" + (size - 1) + "/" + originalSize;
   129   }
   131   // optional rate (in bytes/s) at which to send the file.
   132   if ("rate" in query) {
   133     rate = parseInt(query["rate"]);
   134   }
   136   // The context for the responseHandler.
   137   var context = {
   138     response: response,
   139     state: {
   140       contentType: contentType,
   141       totalBytes: size,
   142       sentBytes: 0,
   143       rate: rate
   144     },
   145     timer: null
   146   };
   148   // The notify implementation for the timer.
   149   context.notify = handleResponse.bind(context);
   151   context.timer =
   152     Components.classes["@mozilla.org/timer;1"]
   153               .createInstance(Components.interfaces.nsITimer);
   155   // generate the content.
   156   response.setStatusLine(version, statusCode, description);
   157   response.setHeader("Content-Type", contentType, false);
   158   if (contentRange) {
   159     response.setHeader("Content-Range", contentRange, false);
   160   }
   161   response.setHeader("Content-Length", size.toString(), false);
   163   // initialize the timer and start writing out the response.
   164   context.timer.initWithCallback(
   165     context,
   166     1000,
   167     Components.interfaces.nsITimer.TYPE_REPEATING_SLACK
   168   );
   170 }

mercurial