// 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 #include #include #include #include #include #include #include #include #include #include #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 " "\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(port)); server_address.sin_addr.s_addr = htonl(INADDR_LOOPBACK); UdpTransport transport(fd, server_address); auto* clock = quic::QuicChromiumClock::GetInstance(); std::vector tasks; scoped_refptr task_runner = base::MakeRefCounted(&tasks); auto alarm_factory = std::make_unique(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(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; }