diff options
author | Kartik Agaram <vc@akkartik.com> | 2020-01-01 17:04:37 -0800 |
---|---|---|
committer | Kartik Agaram <vc@akkartik.com> | 2020-01-01 17:04:37 -0800 |
commit | 2a4088119cf41175457414dfa59bd4064b8f0562 (patch) | |
tree | 64fe184e399f9870ebd481a90eec34d51e5dff68 /archive/2.vm/091socket.cc | |
parent | 23fd294d85959c6b476bcdc35ed6ad508cc99b8f (diff) | |
download | mu-2a4088119cf41175457414dfa59bd4064b8f0562.tar.gz |
5852
Diffstat (limited to 'archive/2.vm/091socket.cc')
-rw-r--r-- | archive/2.vm/091socket.cc | 348 |
1 files changed, 0 insertions, 348 deletions
diff --git a/archive/2.vm/091socket.cc b/archive/2.vm/091socket.cc deleted file mode 100644 index a0f3b948..00000000 --- a/archive/2.vm/091socket.cc +++ /dev/null @@ -1,348 +0,0 @@ -:(before "End Types") -struct socket_t { - int fd; - sockaddr_in addr; - bool polled; - socket_t() { - fd = 0; - polled = false; - bzero(&addr, sizeof(addr)); - } -}; - -:(before "End Primitive Recipe Declarations") -_OPEN_CLIENT_SOCKET, -:(before "End Primitive Recipe Numbers") -put(Recipe_ordinal, "$open-client-socket", _OPEN_CLIENT_SOCKET); -:(before "End Primitive Recipe Checks") -case _OPEN_CLIENT_SOCKET: { - if (SIZE(inst.ingredients) != 2) { - raise << maybe(get(Recipe, r).name) << "'$open-client-socket' requires exactly two ingredients, but got '" << to_original_string(inst) << "'\n" << end(); - break; - } - if (!is_mu_text(inst.ingredients.at(0))) { - raise << maybe(get(Recipe, r).name) << "first ingredient of '$open-client-socket' should be text (the hostname), but got '" << to_string(inst.ingredients.at(0)) << "'\n" << end(); - break; - } - if (!is_mu_number(inst.ingredients.at(1))) { - raise << maybe(get(Recipe, r).name) << "second ingredient of '$open-client-socket' should be a number (the port of the hostname to connect to), but got '" << to_string(inst.ingredients.at(1)) << "'\n" << end(); - break; - } - if (SIZE(inst.products) != 1) { - raise << maybe(get(Recipe, r).name) << "'$open-client-socket' requires exactly one product, but got '" << to_original_string(inst) << "'\n" << end(); - break; - } - if (!is_mu_number(inst.products.at(0))) { - raise << maybe(get(Recipe, r).name) << "first product of '$open-client-socket' should be a number (socket handle), but got '" << to_string(inst.products.at(0)) << "'\n" << end(); - break; - } - break; -} -:(before "End Primitive Recipe Implementations") -case _OPEN_CLIENT_SOCKET: { - string host = read_mu_text(ingredients.at(0).at(/*skip alloc id*/1)); - int port = ingredients.at(1).at(0); - socket_t* client = client_socket(host, port); - products.resize(1); - if (client->fd < 0) { // error - delete client; - products.at(0).push_back(0); - break; - } - long long int result = reinterpret_cast<long long int>(client); - products.at(0).push_back(static_cast<double>(result)); - break; -} -:(code) -socket_t* client_socket(const string& host, int port) { - socket_t* result = new socket_t; - result->fd = socket(AF_INET, SOCK_STREAM, 0); - if (result->fd < 0) { - raise << "Failed to create socket.\n" << end(); - return result; - } - result->addr.sin_family = AF_INET; - hostent* tmp = gethostbyname(host.c_str()); - bcopy(tmp->h_addr, reinterpret_cast<char*>(&result->addr.sin_addr.s_addr), tmp->h_length); - result->addr.sin_port = htons(port); - if (connect(result->fd, reinterpret_cast<sockaddr*>(&result->addr), sizeof(result->addr)) < 0) { - close(result->fd); - result->fd = -1; - raise << "Failed to connect to " << host << ':' << port << '\n' << end(); - } - return result; -} - -:(before "End Primitive Recipe Declarations") -_OPEN_SERVER_SOCKET, -:(before "End Primitive Recipe Numbers") -put(Recipe_ordinal, "$open-server-socket", _OPEN_SERVER_SOCKET); -:(before "End Primitive Recipe Checks") -case _OPEN_SERVER_SOCKET: { - if (SIZE(inst.ingredients) != 1) { - raise << maybe(get(Recipe, r).name) << "'$open-server-socket' requires exactly one ingredient (the port to listen for requests on), but got '" << to_original_string(inst) << "'\n" << end(); - break; - } - if (!is_mu_number(inst.ingredients.at(0))) { - raise << maybe(get(Recipe, r).name) << "first ingredient of '$open-server-socket' should be a number, but got '" << to_string(inst.ingredients.at(0)) << "'\n" << end(); - break; - } - if (SIZE(inst.products) != 1) { - raise << maybe(get(Recipe, r).name) << "'$open-server-socket' requires exactly one product, but got '" << to_original_string(inst) << "'\n" << end(); - break; - } - if (!is_mu_number(inst.products.at(0))) { - raise << maybe(get(Recipe, r).name) << "first product of '$open-server-socket' should be a number (file handle), but got '" << to_string(inst.products.at(0)) << "'\n" << end(); - break; - } - break; -} -:(before "End Primitive Recipe Implementations") -case _OPEN_SERVER_SOCKET: { - int port = ingredients.at(0).at(0); - socket_t* server = server_socket(port); - products.resize(1); - if (server->fd < 0) { - delete server; - products.at(0).push_back(0); - break; - } - long long int result = reinterpret_cast<long long int>(server); - products.at(0).push_back(static_cast<double>(result)); - break; -} -:(code) -socket_t* server_socket(int port) { - socket_t* result = new socket_t; - result->fd = socket(AF_INET, SOCK_STREAM, 0); - if (result->fd < 0) { - raise << "Failed to create server socket.\n" << end(); - return result; - } - int dummy = 0; - setsockopt(result->fd, SOL_SOCKET, SO_REUSEADDR, &dummy, sizeof(dummy)); - result->addr.sin_family = AF_INET; - result->addr.sin_addr.s_addr = Current_scenario ? htonl(INADDR_LOOPBACK) : INADDR_ANY; // run tests without running afoul of any firewall - result->addr.sin_port = htons(port); - if (bind(result->fd, reinterpret_cast<sockaddr*>(&result->addr), sizeof(result->addr)) >= 0) { - listen(result->fd, /*queue length*/5); - } - else { - close(result->fd); - result->fd = -1; - raise << "Failed to bind result socket to port " << port << ". Something's already using that port.\n" << end(); - } - return result; -} - -:(before "End Primitive Recipe Declarations") -_ACCEPT, -:(before "End Primitive Recipe Numbers") -put(Recipe_ordinal, "$accept", _ACCEPT); -:(before "End Primitive Recipe Checks") -case _ACCEPT: { - if (SIZE(inst.ingredients) != 1) { - raise << maybe(get(Recipe, r).name) << "'$accept' requires exactly one ingredient, but got '" << to_original_string(inst) << "'\n" << end(); - break; - } - if (!is_mu_number(inst.ingredients.at(0))) { - raise << maybe(get(Recipe, r).name) << "first ingredient of '$accept' should be a number, but got '" << to_string(inst.ingredients.at(0)) << "'\n" << end(); - break; - } - if (SIZE(inst.products) != 1) { - raise << maybe(get(Recipe, r).name) << "'$accept' requires exactly one product, but got '" << to_original_string(inst) << "'\n" << end(); - break; - } - if (!is_mu_number(inst.products.at(0))) { - raise << maybe(get(Recipe, r).name) << "first product of '$accept' should be a number (file handle), but got '" << to_string(inst.products.at(0)) << "'\n" << end(); - break; - } - break; -} -:(before "End Primitive Recipe Implementations") -case _ACCEPT: { - products.resize(2); - products.at(1).push_back(ingredients.at(0).at(0)); // indicate it modifies its ingredient - long long int x = static_cast<long long int>(ingredients.at(0).at(0)); - socket_t* server = reinterpret_cast<socket_t*>(x); - if (server) { - socket_t* session = accept_session(server); - long long int result = reinterpret_cast<long long int>(session); - products.at(0).push_back(static_cast<double>(result)); - } - else { - products.at(0).push_back(0); - } - break; -} -:(code) -socket_t* accept_session(socket_t* server) { - if (server->fd == 0) return NULL; - socket_t* result = new socket_t; - socklen_t dummy = sizeof(result->addr); - result->fd = accept(server->fd, reinterpret_cast<sockaddr*>(&result->addr), &dummy); - return result; -} - -:(before "End Primitive Recipe Declarations") -_READ_FROM_SOCKET, -:(before "End Primitive Recipe Numbers") -put(Recipe_ordinal, "$read-from-socket", _READ_FROM_SOCKET); -:(before "End Primitive Recipe Checks") -case _READ_FROM_SOCKET: { - if (SIZE(inst.ingredients) != 1) { - raise << maybe(get(Recipe, r).name) << "'$read-from-socket' requires exactly one ingredient, but got '" << to_original_string(inst) << "'\n" << end(); - break; - } - if (!is_mu_number(inst.ingredients.at(0))) { - raise << maybe(get(Recipe, r).name) << "first ingredient of '$read-from-socket' should be a number (socket), but got '" << to_string(inst.ingredients.at(0)) << "'\n" << end(); - break; - } - int nprod = SIZE(inst.products); - if (nprod == 0 || nprod > 4) { - raise << maybe(get(Recipe, r).name) << "'$read-from-socket' requires 1-4 products, but got '" << to_original_string(inst) << "'\n" << end(); - break; - } - if (!is_mu_character(inst.products.at(0))) { - raise << maybe(get(Recipe, r).name) << "first product of '$read-from-socket' should be a character, but got '" << to_string(inst.products.at(0)) << "'\n" << end(); - break; - } - if (nprod > 1 && !is_mu_boolean(inst.products.at(1))) { - raise << maybe(get(Recipe, r).name) << "second product of '$read-from-socket' should be a boolean (data received?), but got '" << to_string(inst.products.at(1)) << "'\n" << end(); - break; - } - if (nprod > 2 && !is_mu_boolean(inst.products.at(2))) { - raise << maybe(get(Recipe, r).name) << "third product of '$read-from-socket' should be a boolean (eof?), but got '" << to_string(inst.products.at(2)) << "'\n" << end(); - break; - } - if (nprod > 3 && !is_mu_number(inst.products.at(3))) { - raise << maybe(get(Recipe, r).name) << "fourth product of '$read-from-socket' should be a number (error code), but got '" << to_string(inst.products.at(3)) << "'\n" << end(); - break; - } - break; -} -:(before "End Primitive Recipe Implementations") -case _READ_FROM_SOCKET: { - products.resize(4); - long long int x = static_cast<long long int>(ingredients.at(0).at(0)); - socket_t* socket = reinterpret_cast<socket_t*>(x); - // 1. we'd like to simply read() from the socket - // however read() on a socket never returns EOF, so we wouldn't know when to stop - // 2. recv() can signal EOF, but it also signals "no data yet" in the beginning - // so use poll() in the beginning to wait for data before calling recv() - // 3. but poll() will block on EOF, so only use poll() on the very first - // $read-from-socket on a socket - // - // Also, there was an unresolved issue where attempts to read() a small - // number of bytes (less than 447 on Linux and Mac) would cause browsers to - // prematurely close the connection. See commit 3403. That seems to be gone - // after moving to recv()+poll(). It was never observed on OpenBSD. - if (!socket->polled) { - pollfd p; - bzero(&p, sizeof(p)); - p.fd = socket->fd; - p.events = POLLIN | POLLHUP; - int poll_result = poll(&p, /*num pollfds*/1, /*timeout*/100/*ms*/); - if (poll_result == 0) { - products.at(0).push_back(/*no data*/0); - products.at(1).push_back(/*found*/false); - products.at(2).push_back(/*eof*/false); - products.at(3).push_back(/*error*/0); - break; - } - else if (poll_result < 0) { - int error_code = errno; - raise << maybe(current_recipe_name()) << "error in $read-from-socket\n" << end(); - products.at(0).push_back(/*no data*/0); - products.at(1).push_back(/*found*/false); - products.at(2).push_back(/*eof*/false); - products.at(3).push_back(error_code); - break; - } - socket->polled = true; - } - char c = '\0'; - int error_code = 0; - int bytes_read = recv(socket->fd, &c, /*single byte*/1, MSG_DONTWAIT); - if (bytes_read < 0) error_code = errno; -//? if (error_code) { -//? ostringstream out; -//? out << "error in $read-from-socket " << socket->fd; -//? perror(out.str().c_str()); -//? } - products.at(0).push_back(c); - products.at(1).push_back(/*found*/true); - products.at(2).push_back(/*eof*/bytes_read <= 0); - products.at(3).push_back(error_code); - break; -} - -:(before "End Primitive Recipe Declarations") -_WRITE_TO_SOCKET, -:(before "End Primitive Recipe Numbers") -put(Recipe_ordinal, "$write-to-socket", _WRITE_TO_SOCKET); -:(before "End Primitive Recipe Checks") -case _WRITE_TO_SOCKET: { - if (SIZE(inst.ingredients) != 2) { - raise << maybe(get(Recipe, r).name) << "'$write-to-socket' requires exactly two ingredient, but got '" << to_original_string(inst) << "'\n" << end(); - break; - } - break; -} -:(before "End Primitive Recipe Implementations") -case _WRITE_TO_SOCKET: { - long long int x = static_cast<long long int>(ingredients.at(0).at(0)); - socket_t* socket = reinterpret_cast<socket_t*>(x); - // write just one character at a time to the socket - long long int y = static_cast<long long int>(ingredients.at(1).at(0)); - char c = static_cast<char>(y); - if (write(socket->fd, &c, 1) != 1) { - raise << maybe(current_recipe_name()) << "failed to write to socket\n" << end(); - exit(0); - } - products.resize(1); - products.at(0).push_back(ingredients.at(0).at(0)); - break; -} - -:(before "End Primitive Recipe Declarations") -_CLOSE_SOCKET, -:(before "End Primitive Recipe Numbers") -put(Recipe_ordinal, "$close-socket", _CLOSE_SOCKET); -:(before "End Primitive Recipe Checks") -case _CLOSE_SOCKET: { - if (SIZE(inst.ingredients) != 1) { - raise << maybe(get(Recipe, r).name) << "'$close-socket' requires exactly two ingredient, but got '" << to_original_string(inst) << "'\n" << end(); - break; - } - if (!is_mu_number(inst.ingredients.at(0))) { - raise << maybe(get(Recipe, r).name) << "first ingredient of '$close-socket' should be a number, but got '" << to_string(inst.ingredients.at(0)) << "'\n" << end(); - break; - } - if (SIZE(inst.products) != 1) { - raise << maybe(get(Recipe, r).name) << "'$close-socket' requires exactly one product, but got '" << to_original_string(inst) << "'\n" << end(); - break; - } - if (inst.products.at(0).name != inst.ingredients.at(0).name) { - raise << maybe(get(Recipe, r).name) << "product of '$close-socket' must be first ingredient '" << inst.ingredients.at(0).original_string << "', but got '" << inst.products.at(0).original_string << "'\n" << end(); - break; - } - break; -} -:(before "End Primitive Recipe Implementations") -case _CLOSE_SOCKET: { - long long int x = static_cast<long long int>(ingredients.at(0).at(0)); - socket_t* socket = reinterpret_cast<socket_t*>(x); - close(socket->fd); - delete socket; - products.resize(1); - products.at(0).push_back(0); // make sure we can't reuse the socket - break; -} - -:(before "End Includes") -#include <netinet/in.h> -#include <netdb.h> -#include <poll.h> -#include <sys/socket.h> -#include <unistd.h> |