NS_IMETHODIMP
nsMsgSaveAsListener::OnStopRequest(nsIRequest *request, nsISupports * aCtxt, nsresult aStatus)
{
  if (m_outputStream)
  {
    m_outputStream->Flush();
    m_outputStream->Close();
  }
  return NS_OK;
} 
NS_IMETHODIMP
ServeResourceEvent::Run() {
  bool more = false; // Are there HTTP headers to read after the first line
  nsCString line;    // Contains the current line read from input stream
  nsLineBuffer<char>* buffer = new nsLineBuffer<char>();
  nsresult rv = ReadCRLF(mInput.get(), buffer, line, &more);
  if (NS_FAILED(rv)) { Shutdown(); return rv; }

  // First line contains the HTTP GET request. Extract the URL and obtain
  // the MediaResource for it.
  nsRefPtr<MediaResource> resource = GetMediaResource(line);
  if (!resource) {
    const char* response_404 = "HTTP/1.1 404 Not Found\r\n"
                               "Content-Length: 0\r\n\r\n";
    rv = WriteAll(response_404, strlen(response_404));
    Shutdown();
    return rv;
  }

  // Offset in bytes to start reading from resource.
  // This is zero by default but can be set to another starting value if
  // this HTTP request includes a byte range request header.
  int64_t start = 0;

  // Keep reading lines until we get a zero length line, which is the HTTP
  // protocol's way of signifying the end of headers and start of body, or
  // until we have no more data to read.
  while (more && line.Length() > 0) {
    rv = ReadCRLF(mInput.get(), buffer, line, &more);
    if (NS_FAILED(rv)) { Shutdown(); return rv; }

    // Look for a byte range request header. If there is one, set the
    // media resource offset to start from to that requested. Here we
    // only check for the range request format used by Android rather
    // than implementing all possibilities in the HTTP specification.
    // That is, the range request is of the form:
    //   Range: bytes=nnnn-
    // Were 'nnnn' is an integer number.
    // The end of the range is not checked, instead we return up to
    // the end of the resource and the client is informed of this via
    // the content-range header.
    NS_NAMED_LITERAL_CSTRING(byteRange, "Range: bytes=");
    const char* s = strstr(line.get(), byteRange.get());
    if (s) {
      start = strtoll(s+byteRange.Length(), nullptr, 10);

      // Clamp 'start' to be between 0 and the resource length.
      start = std::max(int64_t(0), std::min(resource->GetLength(), start));
    }
  }

  // HTTP response to use if this is a non byte range request
  const char* response_normal = "HTTP/1.1 200 OK\r\n";

  // HTTP response to use if this is a byte range request
  const char* response_range = "HTTP/1.1 206 Partial Content\r\n";

  // End of HTTP reponse headers is indicated by an empty line.
  const char* response_end = "\r\n";

  // If the request was a byte range request, we need to read from the
  // requested offset. If the resource is non-seekable, or the seek
  // fails, then the start offset is set back to zero. This results in all
  // HTTP response data being as if the byte range request was not made.
  if (start > 0 && !resource->IsTransportSeekable()) {
    start = 0;
  }

  const char* response_line = start > 0 ?
                                response_range :
                                response_normal;
  rv = WriteAll(response_line, strlen(response_line));
  if (NS_FAILED(rv)) { Shutdown(); return NS_OK; }

  // Buffer used for reading from the input stream and writing to
  // the output stream. The buffer size should be big enough for the
  // HTTP response headers sent below. A static_assert ensures
  // this where the buffer is used.
  const int buffer_size = 32768;
  nsAutoArrayPtr<char> b(new char[buffer_size]);

  // If we know the length of the resource, send a Content-Length header.
  int64_t contentlength = resource->GetLength() - start;
  if (contentlength > 0) {
    static_assert (buffer_size > 1024,
                   "buffer_size must be large enough "
                   "to hold response headers");
    snprintf(b, buffer_size, "Content-Length: %" PRId64 "\r\n", contentlength);
    rv = WriteAll(b, strlen(b));
    if (NS_FAILED(rv)) { Shutdown(); return NS_OK; }
  }

  // If the request was a byte range request, respond with a Content-Range
  // header which details the extent of the data returned.
  if (start > 0) {
    static_assert (buffer_size > 1024,
                   "buffer_size must be large enough "
                   "to hold response headers");
    snprintf(b, buffer_size, "Content-Range: "
             "bytes %" PRId64 "-%" PRId64 "/%" PRId64 "\r\n",
             start, resource->GetLength() - 1, resource->GetLength());
    rv = WriteAll(b, strlen(b));
    if (NS_FAILED(rv)) { Shutdown(); return NS_OK; }
  }

  rv = WriteAll(response_end, strlen(response_end));
  if (NS_FAILED(rv)) { Shutdown(); return NS_OK; }

  rv = mOutput->Flush();
  if (NS_FAILED(rv)) { Shutdown(); return NS_OK; }

  // Read data from media resource
  uint32_t bytesRead = 0; // Number of bytes read/written to streams
  rv = resource->ReadAt(start, b, buffer_size, &bytesRead);
  while (NS_SUCCEEDED(rv) && bytesRead != 0) {
    // Keep track of what we think the starting position for the next read
    // is. This is used in subsequent ReadAt calls to ensure we are reading
    // from the correct offset in the case where another thread is reading
    // from th same MediaResource.
    start += bytesRead;

    // Write data obtained from media resource to output stream
    rv = WriteAll(b, bytesRead);
    if (NS_FAILED (rv)) break;

    rv = resource->ReadAt(start, b, 32768, &bytesRead);
  }

  Shutdown();
  return NS_OK;
}