You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
167 lines
5.6 KiB
167 lines
5.6 KiB
// Copyright (c) 2018 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 <arpa/inet.h>
|
|
#include <errno.h>
|
|
#include <fcntl.h>
|
|
#include <netinet/in.h>
|
|
#include <netinet/ip.h>
|
|
#include <stdio.h>
|
|
#include <sys/socket.h>
|
|
#include <sys/types.h>
|
|
#include <unistd.h>
|
|
|
|
#include <memory>
|
|
#include <vector>
|
|
|
|
#include "base/at_exit.h"
|
|
#include "base/command_line.h"
|
|
#include "base/logging.h"
|
|
#include "net/quic/quic_chromium_alarm_factory.h"
|
|
#include "net/third_party/quic/core/quic_constants.h"
|
|
#include "net/third_party/quic/platform/impl/quic_chromium_clock.h"
|
|
#include "net/third_party/quic/quartc/quartc_factory.h"
|
|
#include "third_party/chromium_quic/demo/delegates.h"
|
|
|
|
int main(int argc, char** argv) {
|
|
base::AtExitManager exit_manager;
|
|
base::CommandLine::Init(argc, argv);
|
|
logging::LoggingSettings logging_settings;
|
|
logging_settings.logging_dest = logging::LOG_TO_FILE;
|
|
logging_settings.log_file = "/dev/stderr";
|
|
logging_settings.lock_log = logging::DONT_LOCK_LOG_FILE;
|
|
logging::InitLogging(logging_settings);
|
|
|
|
if (argc < 2) {
|
|
dprintf(STDERR_FILENO,
|
|
"Missing port number\nusage: streaming_playback_controller "
|
|
"<server-port>\n");
|
|
return 1;
|
|
}
|
|
int port = atoi(argv[1]);
|
|
if (port <= 0 || port > UINT16_MAX) {
|
|
dprintf(STDERR_FILENO, "Invalid port number: %d\n", port);
|
|
return 1;
|
|
}
|
|
|
|
int fd = socket(AF_INET, SOCK_DGRAM, 0);
|
|
if (fd == -1) {
|
|
perror("socket()");
|
|
return 1;
|
|
}
|
|
// On non-Linux, the SOCK_NONBLOCK option is not available, so use the
|
|
// more-portable method of calling fcntl() to set this behavior.
|
|
if (fcntl(fd, F_SETFL, fcntl(fd, F_GETFL, 0) | O_NONBLOCK) == -1) {
|
|
perror("fcntl(F_SETFL, +O_NONBLOCK)");
|
|
return 1;
|
|
}
|
|
|
|
struct sockaddr_in address = {};
|
|
address.sin_family = AF_INET;
|
|
address.sin_port = 0;
|
|
address.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
|
|
int reuse = 1;
|
|
int result = setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse));
|
|
if (result == -1) {
|
|
perror("setsockopt()");
|
|
return 1;
|
|
}
|
|
result = bind(fd, (struct sockaddr*)(&address), sizeof(address));
|
|
if (result == -1) {
|
|
perror("bind()");
|
|
return 1;
|
|
}
|
|
|
|
struct sockaddr_in server_address;
|
|
server_address.sin_family = AF_INET;
|
|
server_address.sin_port = htons(static_cast<uint16_t>(port));
|
|
server_address.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
|
|
|
|
UdpTransport transport(fd, server_address);
|
|
auto* clock = quic::QuicChromiumClock::GetInstance();
|
|
std::vector<FakeTask> tasks;
|
|
scoped_refptr<FakeTaskRunner> task_runner =
|
|
base::MakeRefCounted<FakeTaskRunner>(&tasks);
|
|
auto alarm_factory =
|
|
std::make_unique<net::QuicChromiumAlarmFactory>(task_runner.get(), clock);
|
|
quic::QuartcFactoryConfig factory_config;
|
|
factory_config.alarm_factory = alarm_factory.get();
|
|
factory_config.clock = clock;
|
|
quic::QuartcFactory factory(factory_config);
|
|
|
|
quic::QuartcSessionConfig session_config;
|
|
session_config.perspective = quic::Perspective::IS_CLIENT;
|
|
session_config.unique_remote_server_id = "asdf";
|
|
session_config.packet_transport = &transport;
|
|
// rest are defaults
|
|
|
|
auto session = factory.CreateQuartcSession(session_config);
|
|
SessionDelegate session_delegate;
|
|
session->SetDelegate(&session_delegate);
|
|
|
|
StreamDelegate outgoing_delegate;
|
|
|
|
session->OnTransportCanWrite();
|
|
session->StartCryptoHandshake();
|
|
auto last = clock->WallNow();
|
|
bool once = false;
|
|
while (true) {
|
|
auto now = clock->WallNow();
|
|
for (auto it = tasks.begin(); it != tasks.end();) {
|
|
auto& next_task = *it;
|
|
next_task.delay -= base::TimeDelta::FromMicroseconds(
|
|
now.ToUNIXMicroseconds() - last.ToUNIXMicroseconds());
|
|
if (next_task.delay.InMicroseconds() < 0) {
|
|
printf("task: %s\n", next_task.whence.ToString().c_str());
|
|
std::move(next_task.task).Run();
|
|
it = tasks.erase(it);
|
|
} else {
|
|
++it;
|
|
}
|
|
}
|
|
last = now;
|
|
// NOTE: The stream will not close immediately on send, because it waits for
|
|
// ACKs, including for the fin bit. Calling CloseConnection, however,
|
|
// immediately closes the connection so it's safe to exit immediately here.
|
|
if (outgoing_delegate.closed()) {
|
|
session->CloseConnection("all your base");
|
|
break;
|
|
}
|
|
struct sockaddr_in peer_address;
|
|
socklen_t addrlen = sizeof(peer_address);
|
|
char buffer[quic::kMaxPacketSize] = {};
|
|
int buffer_length = sizeof(buffer);
|
|
result = recvfrom(fd, buffer, buffer_length, 0,
|
|
(struct sockaddr*)(&peer_address), &addrlen);
|
|
if ((result == -1 && errno != EWOULDBLOCK && errno != EAGAIN) ||
|
|
addrlen != sizeof(peer_address)) {
|
|
perror("recvfrom()");
|
|
return 1;
|
|
} else if (result > 0) {
|
|
printf("recvfrom()\n");
|
|
session->OnTransportReceived(buffer, result);
|
|
if (session->IsCryptoHandshakeConfirmed() &&
|
|
session->IsEncryptionEstablished() && !once) {
|
|
auto* stream = session->CreateOutgoingDynamicStream();
|
|
stream->SetDelegate(&outgoing_delegate);
|
|
|
|
// TODO(btolsch): This seems odd. Can we do better when reimplementing
|
|
// platform/impl?
|
|
// TODO(btolsch): Specify the number of copies/refcounts per write.
|
|
char data[] = "turtle";
|
|
auto buffer = base::MakeRefCounted<net::IOBuffer>(sizeof(data) - 1);
|
|
int length = sizeof(data) - 1;
|
|
memcpy(buffer->data(), data, sizeof(data) - 1);
|
|
stream->WriteMemSlices(
|
|
quic::QuicMemSliceSpan(
|
|
quic::QuicMemSliceSpanImpl(&buffer, &length, 1)),
|
|
true);
|
|
once = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|