Commit 2b06b74c authored by kinaba@chromium.org's avatar kinaba@chromium.org

Expose upload progress from URLFetcher.

Uploading counterpart of http://crrev.com/124146. Provide a method for
the users of URLFetcher to obtain upload progress.

Different from the download case, even the internals of URLFetcher
(= URLRequest) doesn't provide callback mechanism for upload progress.
So the current implementation runs a timer and poll the progress status.

BUG=chromium-os:27370
TEST=content_unittests --gtest_filter='*URLFetcher*'

Committed: http://src.chromium.org/viewvc/chrome?view=rev&revision=126589
Reverted: https://src.chromium.org/viewvc/chrome?view=rev&revision=126611
(XP test bots hung at URLFetcher test)

Review URL: https://chromiumcodereview.appspot.com/9618051

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@126850 0039d316-1c4b-4281-b951-d872f2087c98
parent fba65f7b
......@@ -36,6 +36,7 @@
#include "net/url_request/url_request_throttler_manager.h"
static const int kBufferSize = 4096;
static const int kUploadProgressTimerInterval = 100;
class URLFetcherImpl::Core
: public base::RefCountedThreadSafe<URLFetcherImpl::Core>,
......@@ -228,7 +229,9 @@ class URLFetcherImpl::Core
// Drop ownership of any file managed by |file_path_|.
void DisownFile();
// Notify Delegate about the progress of downloading.
// Notify Delegate about the progress of upload/download.
void InformDelegateUploadProgress();
void InformDelegateUploadProgressInDelegateThread(int64 current, int64 total);
void InformDelegateDownloadProgress();
void InformDelegateDownloadProgressInDelegateThread(int64 current,
int64 total);
......@@ -312,7 +315,11 @@ class URLFetcherImpl::Core
// Back-off time delay. 0 by default.
base::TimeDelta backoff_delay_;
// Length of bytes received so far.
// Timer to poll the progress of uploading for POST and PUT requests.
base::RepeatingTimer<Core> upload_progress_checker_timer_;
// Number of bytes sent so far.
int64 current_upload_bytes_;
// Number of bytes received so far.
int64 current_response_bytes_;
// Total expected bytes to receive (-1 if it cannot be determined).
int64 total_response_bytes_;
......@@ -600,6 +607,7 @@ URLFetcherImpl::Core::Core(URLFetcherImpl* fetcher,
response_destination_(STRING),
automatically_retry_on_5xx_(true),
max_retries_(0),
current_upload_bytes_(-1),
current_response_bytes_(0),
total_response_bytes_(-1) {
}
......@@ -891,6 +899,15 @@ void URLFetcherImpl::Core::StartURLRequest() {
request_->AppendBytesToUpload(
upload_content_.data(), static_cast<int>(upload_content_.length()));
}
current_upload_bytes_ = -1;
// TODO(kinaba): http://crbug.com/118103. Implement upload callback in the
// net:: layer and avoid using timer here.
upload_progress_checker_timer_.Start(
FROM_HERE,
base::TimeDelta::FromMilliseconds(kUploadProgressTimerInterval),
this,
&Core::InformDelegateUploadProgress);
break;
case HEAD:
......@@ -971,6 +988,30 @@ void URLFetcherImpl::Core::OnCompletedURLRequest(
}
}
void URLFetcherImpl::Core::InformDelegateUploadProgress() {
DCHECK(io_message_loop_proxy_->BelongsToCurrentThread());
if (request_.get()) {
int64 current = request_->GetUploadProgress();
if (current_upload_bytes_ != current) {
current_upload_bytes_ = current;
int64 total = -1;
if (!is_chunked_upload_)
total = static_cast<int64>(upload_content_.size());
delegate_loop_proxy_->PostTask(
FROM_HERE,
base::Bind(&Core::InformDelegateUploadProgressInDelegateThread,
this, current, total));
}
}
}
void URLFetcherImpl::Core::InformDelegateUploadProgressInDelegateThread(
int64 current, int64 total) {
DCHECK(delegate_loop_proxy_->BelongsToCurrentThread());
if (delegate_)
delegate_->OnURLFetchUploadProgress(fetcher_, current, total);
}
void URLFetcherImpl::Core::InformDelegateDownloadProgress() {
DCHECK(io_message_loop_proxy_->BelongsToCurrentThread());
delegate_loop_proxy_->PostTask(
......@@ -1009,6 +1050,7 @@ void URLFetcherImpl::Core::NotifyMalformedContent() {
}
void URLFetcherImpl::Core::ReleaseRequest() {
upload_progress_checker_timer_.Stop();
request_.reset();
g_registry.Get().RemoveURLFetcherCore(this);
}
......
......@@ -149,6 +149,20 @@ class URLFetcherDownloadProgressCancelTest : public URLFetcherTest {
bool cancelled_;
};
// Version of URLFetcherTest that tests upload progress reports.
class URLFetcherUploadProgressTest : public URLFetcherTest {
public:
virtual void CreateFetcher(const GURL& url);
// content::URLFetcherDelegate
virtual void OnURLFetchUploadProgress(const content::URLFetcher* source,
int64 current, int64 total);
protected:
int64 previous_progress_;
std::string chunk_;
int64 number_of_chunks_added_;
};
// Version of URLFetcherTest that tests headers.
class URLFetcherHeadersTest : public URLFetcherTest {
public:
......@@ -345,6 +359,36 @@ void URLFetcherDownloadProgressCancelTest::OnURLFetchComplete(
io_message_loop_proxy()->PostTask(FROM_HERE, MessageLoop::QuitClosure());
}
void URLFetcherUploadProgressTest::CreateFetcher(const GURL& url) {
fetcher_ = new URLFetcherImpl(url, content::URLFetcher::POST, this);
fetcher_->SetRequestContext(new TestURLRequestContextGetter(
io_message_loop_proxy()));
previous_progress_ = 0;
// Large enough data to require more than one read from UploadDataStream.
chunk_.assign(1<<16, 'a');
// Use chunked upload to wait for a timer event of progress notification.
fetcher_->SetChunkedUpload("application/x-www-form-urlencoded");
fetcher_->Start();
number_of_chunks_added_ = 1;
fetcher_->AppendChunkToUpload(chunk_, false);
}
void URLFetcherUploadProgressTest::OnURLFetchUploadProgress(
const content::URLFetcher* source, int64 current, int64 total) {
// Increasing between 0 and total.
EXPECT_LE(0, current);
EXPECT_GE(static_cast<int64>(chunk_.size()) * number_of_chunks_added_,
current);
EXPECT_LE(previous_progress_, current);
previous_progress_ = current;
EXPECT_EQ(-1, total);
if (number_of_chunks_added_ < 2) {
number_of_chunks_added_ += 1;
fetcher_->AppendChunkToUpload(chunk_, true);
}
}
void URLFetcherHeadersTest::OnURLFetchComplete(
const content::URLFetcher* source) {
std::string header;
......@@ -638,6 +682,21 @@ TEST_F(URLFetcherPostTest, Basic) {
MessageLoop::current()->Run();
}
#if defined(OS_MACOSX)
// SIGSEGV on Mac: http://crbug.com/60426
TEST_F(URLFetcherUploadProgressTest, DISABLED_Basic) {
#else
TEST_F(URLFetcherUploadProgressTest, Basic) {
#endif
net::TestServer test_server(net::TestServer::TYPE_HTTP,
net::TestServer::kLocalhost,
FilePath(kDocRoot));
ASSERT_TRUE(test_server.Start());
CreateFetcher(test_server.GetURL("echo"));
MessageLoop::current()->Run();
}
TEST_F(URLFetcherDownloadProgressTest, Basic) {
net::TestServer test_server(net::TestServer::TYPE_HTTP,
net::TestServer::kLocalhost,
......
......@@ -21,11 +21,17 @@ class CONTENT_EXPORT URLFetcherDelegate {
virtual void OnURLFetchComplete(const URLFetcher* source) = 0;
// This will be called when some part of the response are read. |current|
// denotes the sum of bytes received up to the call, and |total| is the
// denotes the number of bytes received up to the call, and |total| is the
// expected total size of the response (or -1 if not determined).
virtual void OnURLFetchDownloadProgress(const URLFetcher* source,
int64 current, int64 total) {}
// This will be called when uploading of POST or PUT requests proceeded.
// |current| denotes the number of bytes sent so far, and |total| is the
// total size of uploading data (or -1 if chunked upload is enabled).
virtual void OnURLFetchUploadProgress(const URLFetcher* source,
int64 current, int64 total) {}
protected:
virtual ~URLFetcherDelegate() {}
};
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment