blob: dc99a5d78f84b339f95e08ee41a9651fe1ec2057 [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 <fcntl.h>
#include <cstdio>
#include <fuchsia/net/oldhttp/cpp/fidl.h>
#include <lib/async-loop/cpp/loop.h>
#include <lib/async/default.h>
#include "lib/component/cpp/connect.h"
#include "lib/component/cpp/startup_context.h"
#include "lib/fsl/socket/files.h"
#include "lib/fxl/files/file.h"
#include "lib/fxl/files/file_descriptor.h"
#include "lib/fxl/files/path.h"
#include "lib/fxl/files/unique_fd.h"
namespace examples {
namespace http = ::fuchsia::net::oldhttp;
class ResponsePrinter {
public:
void Run(async::Loop* loop, http::URLResponse response) const {
if (response.error) {
printf("Got error: %d (%s)\n", response.error->code,
response.error->description->c_str());
} else {
PrintResponse(response);
PrintResponseBody(std::move(response.body->stream()));
}
loop->Quit(); // All done!
}
void PrintResponse(const http::URLResponse& response) const {
printf(">>> Headers <<< \n");
printf(" %s\n", response.status_line.get().c_str());
if (response.headers) {
for (size_t i = 0; i < response.headers->size(); ++i)
printf(" %s=%s\n", response.headers->at(i).name->data(),
response.headers->at(i).value->data());
}
}
void PrintResponseBody(zx::socket body) const {
// Read response body in blocking fashion.
printf(">>> Body <<<\n");
for (;;) {
char buf[512];
size_t num_bytes = sizeof(buf);
zx_status_t result = body.read(0u, buf, num_bytes, &num_bytes);
if (result == ZX_ERR_SHOULD_WAIT) {
body.wait_one(ZX_SOCKET_READABLE | ZX_SOCKET_PEER_CLOSED,
zx::time::infinite(), nullptr);
} else if (result == ZX_OK) {
if (fwrite(buf, num_bytes, 1, stdout) != 1) {
printf("\nUnexpected error writing to file\n");
break;
}
} else {
break;
}
}
printf("\n>>> EOF <<<\n");
}
};
class PostFileApp {
public:
PostFileApp(async::Loop* loop)
: loop_(loop),
context_(component::StartupContext::CreateFromStartupInfo()) {
http_service_ = context_->ConnectToEnvironmentService<http::HttpService>();
}
bool Start(const std::vector<std::string>& args) {
if (args.size() < 3) {
printf("usage: %s url upload_file\n", args[0].c_str());
return false;
}
std::string url(args[1]);
std::string upload_file(args[2]);
printf("Posting %s to %s\n", upload_file.c_str(), url.c_str());
std::string boundary = "XXXX"; // TODO: make an option to change this
fxl::UniqueFD fd(open(upload_file.c_str(), O_RDONLY));
if (!fd.is_valid()) {
printf("cannot open %s\n", upload_file.c_str());
return false;
}
http::URLRequest request;
request.url = url;
request.method = "POST";
request.auto_follow_redirects = true;
http::HttpHeader header;
header.name = "Content-Type";
header.value = "multipart/form-data; boundary=" + boundary;
request.headers.push_back(std::move(header));
zx::socket consumer;
zx::socket producer;
zx_status_t status = zx::socket::create(0u, &producer, &consumer);
if (status != ZX_OK) {
printf("cannot create socket\n");
return false;
}
request.body = http::URLBody::New();
request.body->set_stream(std::move(consumer));
async_dispatcher_t* dispatcher = async_get_default_dispatcher();
fsl::CopyFromFileDescriptor(std::move(fd), std::move(producer), dispatcher,
[this](bool result, fxl::UniqueFD fd) {
if (!result) {
printf("file read error\n");
loop_->Quit();
}
});
http_service_->CreateURLLoader(url_loader_.NewRequest());
url_loader_->Start(std::move(request), [this](http::URLResponse response) {
ResponsePrinter printer;
printer.Run(loop_, std::move(response));
});
return true;
}
private:
async::Loop* const loop_;
std::unique_ptr<component::StartupContext> context_;
http::HttpServicePtr http_service_;
http::URLLoaderPtr url_loader_;
};
} // namespace examples
int main(int argc, const char** argv) {
std::vector<std::string> args(argv, argv + argc);
async::Loop loop(&kAsyncLoopConfigAttachToThread);
examples::PostFileApp postfile_app(&loop);
if (postfile_app.Start(args))
loop.Run();
return 0;
}