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; }