// SPDX-License-Identifier: LGPL-3.0-or-later // Copyright © 2020-2021 Florian Fischer #include <arpa/inet.h> // for inet_addr #include <bits/types/struct_iovec.h> // for iovec #include <fcntl.h> // for open, O_RDONLY #include <netinet/in.h> // for sockaddr_in, htons, in_addr #include <sys/socket.h> // for bind, listen, setsockopt #include <unistd.h> // for close #include <cassert> // for assert #include <cstdlib> // for mkstemp, exit, EXIT_SUCCESS #include <cstring> // for memcmp, memset #include <memory> // for allocator, unique_ptr #include <string> // for string #include <vector> // for vector #include "Common.hpp" // for DIE_MSG_ERRNO, DIE_MSG #include "CountingPrivateSemaphore.hpp" // for CPS #include "emper.hpp" // for spawn #include "fixtures/network.hpp" // for echo_client #include "io.hpp" // for readFile, accept, recv, send #include "io/Future.hpp" // for Future #define PORT 4243 #define MAX 1024 static void server_func(int sockfd) { struct sockaddr_in clientaddr; socklen_t clientaddr_len = sizeof(clientaddr); auto client_fd = emper::io::accept(sockfd, reinterpret_cast<struct sockaddr*>(&clientaddr), &clientaddr_len) ->wait(); if (client_fd < 0) { DIE_MSG_ERRNO("accept failed"); } // NOLINTNEXTLINE(modernize-avoid-c-arrays) char recv_buf[MAX]; // NOLINTNEXTLINE(modernize-avoid-c-arrays) char read_buf[MAX]; for (;;) { int received = emper::io::recv(client_fd, recv_buf, sizeof(recv_buf), 0)->wait(); if (received == 0) { exit(EXIT_SUCCESS); } if (received == -1) { DIE_MSG_ERRNO("recv failed"); break; } // NOLINTNEXTLINE(modernize-avoid-c-arrays) char file_name[] = "/tmp/emper-SimpleDiskAndNetworkTestFile-XXXXXX"; int file_fd = mkstemp(file_name); if (file_fd == -1) { DIE_MSG_ERRNO("mkstemp failed"); } int written = emper::io::writeFile(file_fd, recv_buf, received)->wait(); if (written < 0) { DIE_MSG_ERRNO("write failed"); } close(file_fd); file_fd = emper::io::openAndWait(file_name, O_RDONLY); if (file_fd == -1) { DIE_MSG_ERRNO("open failed"); } int bytes_read = emper::io::readFile(file_fd, read_buf, written)->wait(); if (bytes_read == 0) { DIE_MSG("nothing to read"); } if (bytes_read < 0) { DIE_MSG_ERRNO("read failed"); } close(file_fd); int sent = emper::io::send(client_fd, read_buf, bytes_read, 0)->wait(); if (sent == 0) { DIE_MSG("client socket unexpected shutdown"); } if (sent == -1) { DIE_MSG_ERRNO("send failed"); } // NOLINTNEXTLINE(modernize-avoid-c-arrays) char file2_name[] = "/tmp/emper-SimpleDiskAndNetworkTestFile-XXXXXX"; file_fd = mkstemp(file2_name); if (file_fd == -1) { DIE_MSG_ERRNO("mkstemp failed"); } const int iovcnt = 2; // NOLINTNEXTLINE(modernize-avoid-c-arrays) struct iovec iov[iovcnt]; std::string s1 = "foo"; std::string s2 = "bar"; iov[0].iov_base = (void*)s1.c_str(); iov[0].iov_len = s1.length(); iov[1].iov_base = (void*)s2.c_str(); iov[1].iov_len = s2.length(); auto writevFuture = emper::io::writev(file_fd, &iov[0], iovcnt); written = writevFuture->wait(); if (written < 0) { DIE_MSG_ERRNO("wrtev failed"); } close(file_fd); file_fd = emper::io::openAndWait(file2_name, O_RDONLY); if (file_fd == -1) { DIE_MSG_ERRNO("open failed"); } auto readFuture = emper::io::readFile(file_fd, read_buf, written, 0, true); bytes_read = readFuture->wait(); if (bytes_read == 0) { DIE_MSG("nothing to read"); } if (bytes_read < 0) { DIE_MSG_ERRNO("read failed"); } assert(written == bytes_read); assert(memcmp(read_buf, iov[0].iov_base, iov[0].iov_len) == 0); assert(memcmp((char*)read_buf + iov[0].iov_len, iov[1].iov_base, iov[1].iov_len) == 0); close(file_fd); } } void emperTest() { int sockfd; struct sockaddr_in servaddr; // socket creation and verification sockfd = socket(AF_INET, SOCK_STREAM, 0); if (sockfd == -1) { DIE_MSG_ERRNO("socket creation failed"); } memset(&servaddr, 0, sizeof(servaddr)); // assign IP, PORT servaddr.sin_family = AF_INET; servaddr.sin_addr.s_addr = inet_addr("127.0.0.1"); servaddr.sin_port = htons(PORT); int reuseaddr = 1; if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &reuseaddr, sizeof(reuseaddr)) == -1) { DIE_MSG_ERRNO("setsockopt failed"); } if (bind(sockfd, (sockaddr*)&servaddr, sizeof(servaddr)) == -1) { DIE_MSG_ERRNO("bind failed"); } if (listen(sockfd, 1) != 0) { DIE_MSG_ERRNO("listen failed"); } CPS cps; spawn([=] { server_func(sockfd); }, cps); spawn( [] { const std::vector<std::string> msgs{"foo", "bar"}; std::string port = std::to_string(PORT); std::string host("127.0.0.1"); echo_client(host, port, msgs); }, cps); cps.wait(); }