blob: 08a17e3c68aafe1970b50a6bf62f99b9aa657b1c [file] [log] [blame]
// Copyright 2016 The Fuchsia 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 "garnet/bin/media/media_service/network_reader_impl.h"
#include <zx/socket.h>
#include "garnet/bin/network/net_errors.h"
#include "lib/app/cpp/connect.h"
#include "lib/fxl/logging.h"
#include "lib/network/fidl/network_service.fidl.h"
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 fidl::String& url,
fidl::InterfaceRequest<SeekingReader> request,
MediaServiceImpl* owner) {
return std::shared_ptr<NetworkReaderImpl>(
new NetworkReaderImpl(url, std::move(request), owner));
}
NetworkReaderImpl::NetworkReaderImpl(
const fidl::String& url,
fidl::InterfaceRequest<SeekingReader> request,
MediaServiceImpl* owner)
: MediaServiceImpl::Product<SeekingReader>(this, std::move(request), owner),
url_(url) {
network::NetworkServicePtr network_service =
owner->ConnectToEnvironmentService<network::NetworkService>();
network_service->CreateURLLoader(url_loader_.NewRequest());
network::URLRequestPtr url_request(network::URLRequest::New());
url_request->url = url_;
url_request->method = "HEAD";
url_request->auto_follow_redirects = true;
url_loader_->Start(
std::move(url_request), [this](network::URLResponsePtr response) {
if (response->error) {
FXL_LOG(ERROR) << "HEAD response error " << response->error->code
<< " "
<< (response->error->description
? response->error->description
: "<no description>");
result_ =
response->error->code == network::NETWORK_ERR_NAME_NOT_RESOLVED
? MediaResult::NOT_FOUND
: MediaResult::UNKNOWN_ERROR;
ready_.Occur();
return;
}
if (response->status_code != kStatusOk) {
FXL_LOG(ERROR) << "HEAD response status code "
<< response->status_code;
result_ = response->status_code == kStatusNotFound
? MediaResult::NOT_FOUND
: MediaResult::UNKNOWN_ERROR;
ready_.Occur();
return;
}
for (const network::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(result_, size_, can_seek_); });
}
void NetworkReaderImpl::ReadAt(uint64_t position,
const ReadAtCallback& callback) {
ready_.When([this, position, callback]() {
if (result_ != MediaResult::OK) {
callback(result_, zx::socket());
return;
}
if (!can_seek_ && position != 0) {
callback(MediaResult::INVALID_ARGUMENT, zx::socket());
return;
}
network::URLRequestPtr request(network::URLRequest::New());
request->url = url_;
request->method = "GET";
if (position != 0) {
std::ostringstream value;
value << kAcceptRangesHeaderBytesValue << "=" << position << "-";
network::HttpHeaderPtr header(network::HttpHeader::New());
header->name = kRangeHeaderName;
header->value = value.str();
request->headers = fidl::Array<network::HttpHeaderPtr>::New(1);
request->headers[0] = std::move(header);
}
url_loader_->Start(
std::move(request), [this, callback](network::URLResponsePtr response) {
if (response->status_code != kStatusOk &&
response->status_code != kStatusPartialContent) {
FXL_LOG(WARNING)
<< "GET response status code " << response->status_code;
result_ = MediaResult::UNKNOWN_ERROR;
callback(result_, zx::socket());
return;
}
FXL_DCHECK(response->body);
FXL_DCHECK(response->body->get_stream());
callback(result_, std::move(response->body->get_stream()));
});
});
}
} // namespace media