blob: 07f8d361af375e0b1caf1ec2bf5358d68ea3f599 [file] [log] [blame]
// Copyright 2019 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 "in_stream_http.h"
#include <lib/media/test/one_shot_event.h>
#include <lib/syslog/cpp/macros.h>
#include <lib/zx/time.h>
#include <stdio.h>
#include <zircon/errors.h>
#include <zircon/types.h>
#include <queue>
#include <tuple>
#include "util.h"
InStreamHttp::InStreamHttp(async::Loop* fidl_loop, thrd_t fidl_thread,
sys::ComponentContext* component_context, std::string url)
: InStream(fidl_loop, fidl_thread, component_context), url_(url) {
ZX_DEBUG_ASSERT(thrd_current() != fidl_thread_);
ZX_DEBUG_ASSERT(!url_.empty());
// We're not runnign on the fidl_thread_, so we need to post over to the
// fidl_thread_ for any binding, sending, etc.
http_loader_.set_error_handler(
[](zx_status_t status) { Exit("http_loader_ failed - status: %lu", status); });
component_context->svc()->Connect(http_loader_.NewRequest(fidl_dispatcher_));
ResetToStartInternal(zx::deadline_after(zx::sec(30)));
}
InStreamHttp::~InStreamHttp() {
ZX_DEBUG_ASSERT(thrd_current() != fidl_thread_);
// By fencing anything we've previously posted to fidl_thread, we avoid
// touching "this" too late.
PostToFidlSerial([this] { http_loader_.Unbind(); });
// After this call completes, we know the above post has run on fidl_thread_,
// so no more code re. this instance will be running on fidl_thread_ (partly
// because we Unbind()/reset() in the lambda above, and partly because we
// never re-post from fidl_thread_).
FencePostToFidlSerial();
}
zx_status_t InStreamHttp::ReadBytesInternal(uint32_t max_bytes_to_read, uint32_t* bytes_read_out,
uint8_t* buffer_out, zx::time just_fail_deadline) {
if (eos_position_known_ && cursor_position_ == eos_position_) {
// Not possible to read more because there isn't any more. Not a failure.
*bytes_read_out = 0;
return ZX_OK;
}
zx_signals_t pending{};
zx_status_t status =
socket_.wait_one(ZX_SOCKET_READABLE | ZX_SOCKET_PEER_CLOSED, just_fail_deadline, &pending);
if (status != ZX_OK) {
Exit("socket_ wait failed - status: %d", status);
}
if (pending & ZX_SOCKET_READABLE) {
size_t len = max_bytes_to_read;
size_t actual;
status = socket_.read(0, static_cast<void*>(buffer_out), len, &actual);
if (status != ZX_OK) {
Exit("socket_.read() failed - status: %d", status);
}
*bytes_read_out = actual;
return ZX_OK;
} else if (pending & ZX_SOCKET_PEER_CLOSED) {
// Only handle this after ZX_SOCKET_READABLE, because we must assume this
// means EOS and we don't want to miss any data that was sent before EOS.
//
// If both READABLE and PEER_CLOSED are set, we have to assume that more may
// be readable, so we intentionally only handle PEER_CLOSED when PEER_CLOSED
// && !READABLE.
*bytes_read_out = 0;
// InStream::ReadBytesShort() takes care of seting eos_position_known_ on
// return from this method, so we don't need to do that here.
return ZX_OK;
} else {
Exit("socket_ wait returned success but neither signal set?");
}
FX_NOTREACHED();
return ZX_ERR_INTERNAL;
}
zx_status_t InStreamHttp::ResetToStartInternal(zx::time just_fail_deadline) {
fuchsia::net::http::Request http_request{};
// url_ is already UTF-8
http_request.set_url(url_);
fuchsia::net::http::Response http_response{};
OneShotEvent have_response_event;
http_loader_->Fetch(std::move(http_request), [&http_response, &have_response_event](
fuchsia::net::http::Response response_param) {
http_response = std::move(response_param);
have_response_event.Signal();
});
have_response_event.Wait(zx::deadline_after(zx::sec(30)));
if (http_response.has_error()) {
fprintf(stderr, "*response.error: %d\n", http_response.error());
}
// test only
ZX_ASSERT_MSG(!http_response.has_error(), "http response has error");
ZX_ASSERT_MSG(http_response.has_body(), "http response missing body");
if (http_response.has_headers()) {
for (auto& header : http_response.headers()) {
// TODO(dustingreen): deal with chunked encoding, or switch to a new http
// client impl that deals with de-chunking before we see the data. For now
// we rely on the http server to not generate chunked encoding.
ZX_ASSERT(!(std::string(header.name.begin(), header.name.end()) == "transfer-encoding" &&
std::string(header.value.begin(), header.value.end()) == "chunked"));
}
}
socket_ = std::move(*http_response.mutable_body());
cursor_position_ = 0;
failure_seen_ = false;
eos_position_known_ = false;
eos_position_ = 0;
return ZX_OK;
}