mirror of
https://github.com/flutter/flutter.git
synced 2026-02-20 02:29:02 +08:00
326 lines
9.0 KiB
C++
326 lines
9.0 KiB
C++
// Copyright 2015 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 <fcntl.h>
|
|
#include <inttypes.h>
|
|
#include <netinet/in.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <sys/socket.h>
|
|
#include <sys/stat.h>
|
|
#include <sys/types.h>
|
|
#include <unistd.h>
|
|
|
|
#include <algorithm>
|
|
#include <memory>
|
|
#include <string>
|
|
#include <vector>
|
|
|
|
// This utility allows to read a file from the local file system through a
|
|
// network protocol. The protocol is the following request response protocol:
|
|
//
|
|
// Opening a new file:
|
|
// Request: O path_to_file\n
|
|
// Success: O
|
|
// Error: E
|
|
//
|
|
// Seeking on the last opened file:
|
|
// Request: S offset whence\n
|
|
// Success: 0
|
|
// Error: E
|
|
//
|
|
// Reading the last opened file at the current position:
|
|
// Request: R size\n
|
|
// Success: 0{size encoded on 4 bytes in network order}{content}
|
|
// Error: E
|
|
|
|
namespace {
|
|
|
|
// Display an error message and exit.
|
|
void error(const char* msg) {
|
|
perror(msg);
|
|
exit(1);
|
|
}
|
|
|
|
// Keep track of an active connection with a client.
|
|
class Connection {
|
|
public:
|
|
// Takes ownership of the given fd which is an opened socket to the client.
|
|
explicit Connection(int fd)
|
|
: fd_(fd), file_fd_(-1), buffer_size_(0), buffer_(nullptr, free) {}
|
|
~Connection() { Close(); }
|
|
|
|
int fd() { return fd_; };
|
|
|
|
void Close() {
|
|
if (fd_ != -1) {
|
|
close(fd_);
|
|
fd_ = -1;
|
|
}
|
|
if (file_fd_ != -1) {
|
|
close(file_fd_);
|
|
file_fd_ = -1;
|
|
}
|
|
}
|
|
|
|
// Returns whether this connection has some data to write to the client.
|
|
bool NeedsWriting() { return write_buffer_.size() > 0; }
|
|
|
|
// Try to write buffered data to the client. This should only be called when
|
|
// the socket is writeable, otherwise this method may block.
|
|
bool Write() {
|
|
int nb_written = write(fd_, write_buffer_.data(), write_buffer_.size());
|
|
if (nb_written <= 0) {
|
|
Close();
|
|
return false;
|
|
}
|
|
write_buffer_ = std::string(write_buffer_, nb_written);
|
|
return true;
|
|
}
|
|
|
|
// Read any data available from the client, and send any responses if a full
|
|
// request has been read. This should only be called when the socket is
|
|
// readable, otherwise this method may block.
|
|
bool Read() {
|
|
char buffer[4096];
|
|
int nb_read = read(fd_, buffer, sizeof(buffer));
|
|
if (nb_read <= 0) {
|
|
Close();
|
|
return false;
|
|
}
|
|
read_buffer_ += std::string(buffer, nb_read);
|
|
Respond();
|
|
return true;
|
|
}
|
|
|
|
private:
|
|
// Success message.
|
|
const char kSuccess = 'O';
|
|
// Error message.
|
|
const char kError = 'E';
|
|
|
|
// Read any available request in the read buffer and write the responses in
|
|
// the write buffer.
|
|
void Respond() {
|
|
std::string::size_type eol_position = read_buffer_.find('\n');
|
|
while (eol_position != std::string::npos) {
|
|
std::string command(read_buffer_, 0, eol_position);
|
|
read_buffer_ = std::string(read_buffer_, eol_position + 1);
|
|
std::string argument(command, 2);
|
|
switch (command[0]) {
|
|
case 'O':
|
|
OpenCommand(argument);
|
|
break;
|
|
case 'S':
|
|
SeekCommand(argument);
|
|
break;
|
|
case 'R':
|
|
ReadCommand(argument);
|
|
break;
|
|
default:
|
|
write_buffer_ += kError;
|
|
}
|
|
eol_position = read_buffer_.find('\n');
|
|
}
|
|
}
|
|
|
|
// Handle the open command.
|
|
void OpenCommand(const std::string& file) {
|
|
if (file_fd_ != -1) {
|
|
close(file_fd_);
|
|
file_fd_ = -1;
|
|
}
|
|
file_fd_ = open(file.c_str(), O_RDONLY);
|
|
if (file_fd_ == -1) {
|
|
perror("Unable to open file");
|
|
write_buffer_ += kError;
|
|
return;
|
|
}
|
|
write_buffer_ += kSuccess;
|
|
}
|
|
|
|
// Handle the seek command.
|
|
void SeekCommand(const std::string& parameters) {
|
|
if (file_fd_ == -1) {
|
|
write_buffer_ += kError;
|
|
return;
|
|
}
|
|
int32_t offset;
|
|
int32_t whence;
|
|
if (sscanf(parameters.c_str(), "%" SCNd32 " %" SCNd32, &offset, &whence) !=
|
|
2) {
|
|
write_buffer_ += kError;
|
|
return;
|
|
}
|
|
if (lseek(file_fd_, offset, whence) == -1) {
|
|
perror("Unable to seek");
|
|
write_buffer_ += kError;
|
|
return;
|
|
}
|
|
write_buffer_ += kSuccess;
|
|
}
|
|
|
|
// Handle the read command.
|
|
void ReadCommand(const std::string& parameters) {
|
|
if (file_fd_ == -1) {
|
|
write_buffer_ += kError;
|
|
return;
|
|
}
|
|
int32_t size;
|
|
if (sscanf(parameters.c_str(), "%" SCNd32, &size) != 1) {
|
|
write_buffer_ += kError;
|
|
return;
|
|
}
|
|
if (size <= 0) {
|
|
write_buffer_ += kError;
|
|
return;
|
|
}
|
|
EnsureBufferCapacity(size);
|
|
int32_t result = read(file_fd_, buffer_.get(), size);
|
|
if (result < 0) {
|
|
perror("Unable to read");
|
|
close(file_fd_);
|
|
file_fd_ = -1;
|
|
write_buffer_ += kError;
|
|
return;
|
|
}
|
|
write_buffer_ += kSuccess;
|
|
int32_t size_to_send = htonl(result);
|
|
static_assert(sizeof(size_to_send) == 4, "Must send size with 4 byte.");
|
|
write_buffer_ += std::string(reinterpret_cast<char*>(&size_to_send), 4);
|
|
write_buffer_ += std::string(buffer_.get(), result);
|
|
}
|
|
|
|
// Ensure that |buffer_| has the capacity needed to read from the local file.
|
|
void EnsureBufferCapacity(size_t capacity) {
|
|
if (buffer_size_ >= capacity) {
|
|
return;
|
|
}
|
|
if (buffer_size_ == 0) {
|
|
buffer_.reset(static_cast<char*>(malloc(capacity)));
|
|
buffer_size_ = capacity;
|
|
return;
|
|
}
|
|
buffer_.reset(static_cast<char*>(realloc(buffer_.release(), capacity)));
|
|
buffer_size_ = capacity;
|
|
}
|
|
|
|
// File descriptor of the socket connected to the client.
|
|
int fd_;
|
|
// File descriptor of the current read file.
|
|
int file_fd_;
|
|
size_t buffer_size_;
|
|
std::unique_ptr<char, decltype(free)*> buffer_;
|
|
std::string write_buffer_;
|
|
std::string read_buffer_;
|
|
};
|
|
|
|
} // namespace
|
|
|
|
int main(int argc, char** argv) {
|
|
// The port to bind to.
|
|
int port = 0;
|
|
if (argc > 1) {
|
|
port = atoi(argv[1]);
|
|
}
|
|
// Setup the server socket.
|
|
int server_socket, connected_socket;
|
|
struct sockaddr_in server_address, client_address;
|
|
server_socket = socket(AF_INET, SOCK_STREAM, 0);
|
|
if (server_socket < 0) {
|
|
error("Unable to open socket");
|
|
}
|
|
bzero(&server_address, sizeof(server_address));
|
|
server_address.sin_family = AF_INET;
|
|
server_address.sin_addr.s_addr = INADDR_ANY;
|
|
server_address.sin_port = port;
|
|
if (bind(server_socket, reinterpret_cast<struct sockaddr*>(&server_address),
|
|
sizeof(server_address)) < 0) {
|
|
error("Unable to bind socket");
|
|
}
|
|
socklen_t socket_size = sizeof(server_address);
|
|
if (getsockname(server_socket,
|
|
reinterpret_cast<struct sockaddr*>(&server_address),
|
|
&socket_size) < 0) {
|
|
perror("Unable to retrieve socket information");
|
|
}
|
|
// Print the port on the bound port on the standard output.
|
|
printf("%d\n", ntohs(server_address.sin_port));
|
|
|
|
// Start listening for client.
|
|
listen(server_socket, 5);
|
|
|
|
// Wait for the first client to connect.
|
|
socket_size = sizeof(client_address);
|
|
connected_socket =
|
|
accept(server_socket, reinterpret_cast<struct sockaddr*>(&client_address),
|
|
&socket_size);
|
|
if (connected_socket < 0) {
|
|
error("Unable to accept the intiial client");
|
|
}
|
|
|
|
// Keep track of active connections.
|
|
std::vector<std::unique_ptr<Connection>> connections;
|
|
connections.push_back(
|
|
std::unique_ptr<Connection>(new Connection(connected_socket)));
|
|
|
|
// Stop when all clients have disconnected.
|
|
while (connections.size()) {
|
|
// Prepate data to wait for any I/O to be ready.
|
|
fd_set rset;
|
|
fd_set wset;
|
|
FD_ZERO(&rset);
|
|
FD_ZERO(&wset);
|
|
// Always wait for a new connection.
|
|
FD_SET(server_socket, &rset);
|
|
int max_fd = server_socket + 1;
|
|
for (auto& c : connections) {
|
|
// Always wait for a client sending data.
|
|
FD_SET(c->fd(), &rset);
|
|
// Wait for a client to be ready to receive data only if some data needs
|
|
// to be sent.
|
|
if (c->NeedsWriting()) {
|
|
FD_SET(c->fd(), &wset);
|
|
}
|
|
max_fd = std::max(max_fd, c->fd() + 1);
|
|
}
|
|
// Wait for any I/O to be ready.
|
|
select(max_fd, &rset, &wset, nullptr, nullptr);
|
|
|
|
// Check if a client a connected.
|
|
if (FD_ISSET(server_socket, &rset)) {
|
|
int fd = accept(server_socket,
|
|
reinterpret_cast<struct sockaddr*>(&client_address),
|
|
&socket_size);
|
|
if (fd >= 0) {
|
|
connections.push_back(std::unique_ptr<Connection>(new Connection(fd)));
|
|
}
|
|
}
|
|
|
|
// Check each connection.
|
|
for (auto& c : connections) {
|
|
if (FD_ISSET(c->fd(), &rset)) {
|
|
if (!c->Read()) {
|
|
// If a fatal error happen, delete the connection.
|
|
c.reset();
|
|
}
|
|
}
|
|
if (c && FD_ISSET(c->fd(), &wset)) {
|
|
if (!c->Write()) {
|
|
// If a fatal error happen, delete the connection.
|
|
c.reset();
|
|
}
|
|
}
|
|
}
|
|
|
|
// Remove any deleted connection from the list of active connections.
|
|
connections.erase(
|
|
std::remove_if(connections.begin(), connections.end(),
|
|
[](const std::unique_ptr<Connection>& c) { return !c; }),
|
|
connections.end());
|
|
}
|
|
return 0;
|
|
}
|