| // Copyright 2016 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "base/logging.h" |
| #include "mojo/public/cpp/application/connect.h" |
| #include "mojo/public/cpp/system/data_pipe.h" |
| #include "services/media/factory_service/network_reader_impl.h" |
| |
| namespace mojo { |
| namespace media { |
| |
| const char* NetworkReaderImpl::kContentLengthHeaderName = "Content-Length"; |
| const char* NetworkReaderImpl::kAcceptRangesHeaderName = "Accept-Ranges"; |
| const char* NetworkReaderImpl::kAcceptRangesHeaderBytesValue = "bytes"; |
| const char* NetworkReaderImpl::kRangeHeaderName = "Range"; |
| |
| // static |
| std::shared_ptr<NetworkReaderImpl> NetworkReaderImpl::Create( |
| const String& url, |
| InterfaceRequest<SeekingReader> request, |
| MediaFactoryService* owner) { |
| return std::shared_ptr<NetworkReaderImpl>( |
| new NetworkReaderImpl(url, request.Pass(), owner)); |
| } |
| |
| NetworkReaderImpl::NetworkReaderImpl(const String& url, |
| InterfaceRequest<SeekingReader> request, |
| MediaFactoryService* owner) |
| : MediaFactoryService::Product<SeekingReader>(this, request.Pass(), owner), |
| url_(url) { |
| NetworkServicePtr network_service; |
| |
| ConnectToService(app()->shell(), "mojo:network_service", |
| GetProxy(&network_service)); |
| |
| network_service->CreateURLLoader(GetProxy(&url_loader_)); |
| |
| URLRequestPtr url_request(URLRequest::New()); |
| url_request->url = url_; |
| url_request->method = "HEAD"; |
| |
| url_loader_->Start(url_request.Pass(), [this](URLResponsePtr response) { |
| // TODO(dalesat): Handle redirects. |
| if (response->status_code != kStatusOk) { |
| LOG(WARNING) << "HEAD response status code " << response->status_code; |
| result_ = response->status_code == kStatusNotFound |
| ? MediaResult::NOT_FOUND |
| : MediaResult::UNKNOWN_ERROR; |
| ready_.Occur(); |
| return; |
| } |
| |
| for (const HttpHeaderPtr& header : response->headers) { |
| if (header->name == kContentLengthHeaderName) { |
| size_ = std::stoull(header->value); |
| } else if (header->name == kAcceptRangesHeaderName && |
| header->value == kAcceptRangesHeaderBytesValue) { |
| can_seek_ = true; |
| } |
| } |
| |
| ready_.Occur(); |
| }); |
| } |
| |
| NetworkReaderImpl::~NetworkReaderImpl() {} |
| |
| void NetworkReaderImpl::Describe(const DescribeCallback& callback) { |
| ready_.When([this, callback]() { callback.Run(result_, size_, can_seek_); }); |
| } |
| |
| void NetworkReaderImpl::ReadAt(uint64_t position, |
| const ReadAtCallback& callback) { |
| ready_.When([this, position, callback]() { |
| if (result_ != MediaResult::OK) { |
| callback.Run(result_, ScopedHandleBase<DataPipeConsumerHandle>()); |
| return; |
| } |
| |
| if (!can_seek_ && position != 0) { |
| callback.Run(MediaResult::INVALID_ARGUMENT, |
| ScopedHandleBase<DataPipeConsumerHandle>()); |
| return; |
| } |
| |
| URLRequestPtr request(URLRequest::New()); |
| request->url = url_; |
| request->method = "GET"; |
| |
| if (position != 0) { |
| std::ostringstream value; |
| value << kAcceptRangesHeaderBytesValue << "=" << position << "-"; |
| |
| HttpHeaderPtr header(HttpHeader::New()); |
| header->name = kRangeHeaderName; |
| header->value = value.str(); |
| |
| request->headers = Array<HttpHeaderPtr>::New(1).Pass(); |
| request->headers[0] = header.Pass(); |
| } |
| |
| url_loader_->Start(request.Pass(), [this, |
| callback](URLResponsePtr response) { |
| if (response->status_code != kStatusOk && |
| response->status_code != kStatusPartialContent) { |
| LOG(WARNING) << "GET response status code " << response->status_code; |
| result_ = MediaResult::UNKNOWN_ERROR; |
| callback.Run(result_, ScopedHandleBase<DataPipeConsumerHandle>()); |
| return; |
| } |
| |
| DCHECK(response->body.is_valid()); |
| callback.Run(result_, response->body.Pass()); |
| }); |
| }); |
| } |
| |
| } // namespace media |
| } // namespace mojo |