#include "mojo/services/network/url_loader_impl.h"
+#include "base/memory/scoped_vector.h"
+#include "base/message_loop/message_loop.h"
#include "mojo/common/common_type_converters.h"
#include "mojo/services/network/network_context.h"
#include "net/base/io_buffer.h"
#include "net/base/load_flags.h"
+#include "net/base/upload_bytes_element_reader.h"
+#include "net/base/upload_data_stream.h"
#include "net/http/http_response_headers.h"
+#include "net/url_request/redirect_info.h"
namespace mojo {
namespace {
// Generates an URLResponsePtr from the response state of a net::URLRequest.
URLResponsePtr MakeURLResponse(const net::URLRequest* url_request) {
URLResponsePtr response(URLResponse::New());
- response->url = url_request->url().spec();
+ response->url = String::From(url_request->url());
const net::HttpResponseHeaders* headers = url_request->response_headers();
if (headers) {
response->headers.Swap(&header_lines);
}
+ std::string mime_type;
+ url_request->GetMimeType(&mime_type);
+ response->mime_type = mime_type;
+
+ std::string charset;
+ url_request->GetCharset(&charset);
+ response->charset = charset;
+
return response.Pass();
}
+NetworkErrorPtr MakeNetworkError(int error_code) {
+ NetworkErrorPtr error = NetworkError::New();
+ error->code = error_code;
+ error->description = net::ErrorToString(error_code);
+ return error.Pass();
+}
+
+// Reads the request body upload data from a DataPipe.
+class UploadDataPipeElementReader : public net::UploadElementReader {
+ public:
+ UploadDataPipeElementReader(ScopedDataPipeConsumerHandle pipe)
+ : pipe_(pipe.Pass()), num_bytes_(0) {}
+ virtual ~UploadDataPipeElementReader() {}
+
+ // UploadElementReader overrides:
+ virtual int Init(const net::CompletionCallback& callback) OVERRIDE {
+ offset_ = 0;
+ ReadDataRaw(pipe_.get(), NULL, &num_bytes_, MOJO_READ_DATA_FLAG_QUERY);
+ return net::OK;
+ }
+ virtual uint64 GetContentLength() const OVERRIDE {
+ return num_bytes_;
+ }
+ virtual uint64 BytesRemaining() const OVERRIDE {
+ return num_bytes_ - offset_;
+ }
+ virtual bool IsInMemory() const OVERRIDE {
+ return false;
+ }
+ virtual int Read(net::IOBuffer* buf,
+ int buf_length,
+ const net::CompletionCallback& callback) OVERRIDE {
+ uint32_t bytes_read =
+ std::min(BytesRemaining(), static_cast<uint64>(buf_length));
+ if (bytes_read > 0) {
+ ReadDataRaw(pipe_.get(), buf->data(), &bytes_read,
+ MOJO_READ_DATA_FLAG_NONE);
+ }
+
+ offset_ += bytes_read;
+ return bytes_read;
+ }
+
+ private:
+ ScopedDataPipeConsumerHandle pipe_;
+ uint32_t num_bytes_;
+ uint32_t offset_;
+
+ DISALLOW_COPY_AND_ASSIGN(UploadDataPipeElementReader);
+};
+
} // namespace
// Keeps track of a pending two-phase write on a DataPipeProducerHandle.
buffer_(NULL) {
}
- bool BeginWrite(uint32_t* num_bytes) {
+ MojoResult BeginWrite(uint32_t* num_bytes) {
MojoResult result = BeginWriteDataRaw(handle_.get(), &buffer_, num_bytes,
MOJO_WRITE_DATA_FLAG_NONE);
if (*num_bytes > kMaxReadSize)
*num_bytes = kMaxReadSize;
- return result == MOJO_RESULT_OK;
+ return result;
}
ScopedDataPipeProducerHandle Complete(uint32_t num_bytes) {
URLLoaderImpl::URLLoaderImpl(NetworkContext* context)
: context_(context),
+ response_body_buffer_size_(0),
auto_follow_redirects_(true),
weak_ptr_factory_(this) {
}
URLLoaderImpl::~URLLoaderImpl() {
}
-void URLLoaderImpl::OnConnectionError() {
- delete this;
-}
-
void URLLoaderImpl::Start(URLRequestPtr request,
- ScopedDataPipeProducerHandle response_body_stream) {
- // Do not allow starting another request.
+ const Callback<void(URLResponsePtr)>& callback) {
if (url_request_) {
- SendError(net::ERR_UNEXPECTED);
- url_request_.reset();
- response_body_stream_.reset();
+ SendError(net::ERR_UNEXPECTED, callback);
return;
}
if (!request) {
- SendError(net::ERR_INVALID_ARGUMENT);
+ SendError(net::ERR_INVALID_ARGUMENT, callback);
return;
}
- response_body_stream_ = response_body_stream.Pass();
-
- GURL url(request->url);
url_request_.reset(
- new net::URLRequest(url,
+ new net::URLRequest(GURL(request->url),
net::DEFAULT_PRIORITY,
this,
context_->url_request_context()));
headers.AddHeaderFromString(request->headers[i].To<base::StringPiece>());
url_request_->SetExtraRequestHeaders(headers);
}
+ if (request->body) {
+ ScopedVector<net::UploadElementReader> element_readers;
+ for (size_t i = 0; i < request->body.size(); ++i) {
+ element_readers.push_back(
+ new UploadDataPipeElementReader(request->body[i].Pass()));
+ }
+ url_request_->set_upload(make_scoped_ptr(
+ new net::UploadDataStream(element_readers.Pass(), 0)));
+ }
if (request->bypass_cache)
url_request_->SetLoadFlags(net::LOAD_BYPASS_CACHE);
- // TODO(darin): Handle request body.
+ callback_ = callback;
+ response_body_buffer_size_ = request->response_body_buffer_size;
auto_follow_redirects_ = request->auto_follow_redirects;
url_request_->Start();
}
-void URLLoaderImpl::FollowRedirect() {
+void URLLoaderImpl::FollowRedirect(
+ const Callback<void(URLResponsePtr)>& callback) {
+ if (!url_request_) {
+ SendError(net::ERR_UNEXPECTED, callback);
+ return;
+ }
+
if (auto_follow_redirects_) {
DLOG(ERROR) << "Spurious call to FollowRedirect";
+ SendError(net::ERR_UNEXPECTED, callback);
+ return;
+ }
+
+ // TODO(darin): Verify that it makes sense to call FollowDeferredRedirect.
+ url_request_->FollowDeferredRedirect();
+}
+
+void URLLoaderImpl::QueryStatus(
+ const Callback<void(URLLoaderStatusPtr)>& callback) {
+ URLLoaderStatusPtr status(URLLoaderStatus::New());
+ if (url_request_) {
+ status->is_loading = url_request_->is_pending();
+ if (!url_request_->status().is_success())
+ status->error = MakeNetworkError(url_request_->status().error());
} else {
- if (url_request_)
- url_request_->FollowDeferredRedirect();
+ status->is_loading = false;
}
+ // TODO(darin): Populate more status fields.
+ callback.Run(status.Pass());
}
void URLLoaderImpl::OnReceivedRedirect(net::URLRequest* url_request,
- const GURL& new_url,
+ const net::RedirectInfo& redirect_info,
bool* defer_redirect) {
DCHECK(url_request == url_request_.get());
DCHECK(url_request->status().is_success());
+ if (auto_follow_redirects_)
+ return;
+
+ // Send the redirect response to the client, allowing them to inspect it and
+ // optionally follow the redirect.
+ *defer_redirect = true;
+
URLResponsePtr response = MakeURLResponse(url_request);
- std::string redirect_method =
- net::URLRequest::ComputeMethodForRedirect(url_request->method(),
- response->status_code);
- client()->OnReceivedRedirect(
- response.Pass(), new_url.spec(), redirect_method);
+ response->redirect_method = redirect_info.new_method;
+ response->redirect_url = String::From(redirect_info.new_url);
- *defer_redirect = !auto_follow_redirects_;
+ SendResponse(response.Pass());
}
void URLLoaderImpl::OnResponseStarted(net::URLRequest* url_request) {
DCHECK(url_request == url_request_.get());
if (!url_request->status().is_success()) {
- SendError(url_request->status().error());
+ SendError(url_request->status().error(), callback_);
+ callback_ = Callback<void(URLResponsePtr)>();
return;
}
- client()->OnReceivedResponse(MakeURLResponse(url_request));
+ // TODO(darin): Add support for optional MIME sniffing.
+
+ DataPipe data_pipe;
+ // TODO(darin): Honor given buffer size.
+
+ URLResponsePtr response = MakeURLResponse(url_request);
+ response->body = data_pipe.consumer_handle.Pass();
+ response_body_stream_ = data_pipe.producer_handle.Pass();
+
+ SendResponse(response.Pass());
// Start reading...
ReadMore();
void URLLoaderImpl::OnReadCompleted(net::URLRequest* url_request,
int bytes_read) {
+ DCHECK(url_request == url_request_.get());
+
if (url_request->status().is_success()) {
DidRead(static_cast<uint32_t>(bytes_read), false);
} else {
pending_write_ = NULL; // This closes the data pipe.
- // TODO(darin): Perhaps we should communicate this error to our client.
}
}
-void URLLoaderImpl::SendError(int error_code) {
- NetworkErrorPtr error(NetworkError::New());
- error->code = error_code;
- error->description = net::ErrorToString(error_code);
- client()->OnReceivedError(error.Pass());
+void URLLoaderImpl::SendError(
+ int error_code,
+ const Callback<void(URLResponsePtr)>& callback) {
+ URLResponsePtr response(URLResponse::New());
+ if (url_request_)
+ response->url = String::From(url_request_->url());
+ response->error = MakeNetworkError(error_code);
+ callback.Run(response.Pass());
+}
+
+void URLLoaderImpl::SendResponse(URLResponsePtr response) {
+ Callback<void(URLResponsePtr)> callback;
+ std::swap(callback_, callback);
+ callback.Run(response.Pass());
+}
+
+void URLLoaderImpl::OnResponseBodyStreamReady(MojoResult result) {
+ // TODO(darin): Handle a bad |result| value.
+ ReadMore();
+}
+
+void URLLoaderImpl::WaitToReadMore() {
+ handle_watcher_.Start(response_body_stream_.get(),
+ MOJO_HANDLE_SIGNAL_WRITABLE,
+ MOJO_DEADLINE_INDEFINITE,
+ base::Bind(&URLLoaderImpl::OnResponseBodyStreamReady,
+ weak_ptr_factory_.GetWeakPtr()));
}
void URLLoaderImpl::ReadMore() {
pending_write_ = new PendingWriteToDataPipe(response_body_stream_.Pass());
uint32_t num_bytes;
- if (!pending_write_->BeginWrite(&num_bytes))
- CHECK(false); // Oops! TODO(darin): crbug/386877: The pipe might be full!
- if (num_bytes > static_cast<uint32_t>(std::numeric_limits<int>::max()))
- CHECK(false); // Oops!
+ MojoResult result = pending_write_->BeginWrite(&num_bytes);
+ if (result == MOJO_RESULT_SHOULD_WAIT) {
+ // The pipe is full. We need to wait for it to have more space.
+ response_body_stream_ = pending_write_->Complete(num_bytes);
+ pending_write_ = NULL;
+ WaitToReadMore();
+ return;
+ }
+ if (result != MOJO_RESULT_OK) {
+ // The response body stream is in a bad state. Bail.
+ // TODO(darin): How should this be communicated to our client?
+ return;
+ }
+ CHECK_GT(static_cast<uint32_t>(std::numeric_limits<int>::max()), num_bytes);
scoped_refptr<net::IOBuffer> buf = new DependentIOBuffer(pending_write_);
} else {
pending_write_->Complete(0);
pending_write_ = NULL; // This closes the data pipe.
- if (bytes_read == 0) {
- client()->OnReceivedEndOfResponseBody();
- } else {
- DCHECK(!url_request_->status().is_success());
- SendError(url_request_->status().error());
- }
}
}