diff --git a/luprex/core/cpp/drivenengine.cpp b/luprex/core/cpp/drivenengine.cpp index 2596338a..ab1a9182 100644 --- a/luprex/core/cpp/drivenengine.cpp +++ b/luprex/core/cpp/drivenengine.cpp @@ -126,6 +126,10 @@ void DrivenEngine::drv_set_lua_source(util::LuaSourcePtr source) { rescan_lua_source_ = false; } +void DrivenEngine::drv_invoke_event_init() { + event_init(); +} + void DrivenEngine::drv_invoke_event_update() { event_update(); } diff --git a/luprex/core/cpp/drivenengine.hpp b/luprex/core/cpp/drivenengine.hpp index c628c717..95608847 100644 --- a/luprex/core/cpp/drivenengine.hpp +++ b/luprex/core/cpp/drivenengine.hpp @@ -152,12 +152,13 @@ public: // The update callback. You may override this in a subclass. // This will be called whenever anything changes. // + virtual void event_init() {} virtual void event_update() {} // Get the current time. // // DRIVER: This returns the time most recently stored by the driver - // using drv_set_clock. + // using drv_set_clock. // double get_clock(); @@ -291,8 +292,9 @@ public: // void drv_set_lua_source(util::LuaSourcePtr source); - // Invoke the update event. + // Invoke the init or update event. // + void drv_invoke_event_init(); void drv_invoke_event_update(); // Check the 'rescan_lua_source' flag. If this flag is set, it means diff --git a/luprex/core/cpp/driver-mingw.cpp b/luprex/core/cpp/driver-mingw.cpp index fb8e12f1..a9e43ec3 100644 --- a/luprex/core/cpp/driver-mingw.cpp +++ b/luprex/core/cpp/driver-mingw.cpp @@ -33,23 +33,54 @@ public: return nullptr; } - static SOCKET open_connection(const std::string &target) { + SOCKET open_connection(const std::string &target, std::string &err) { + PADDRINFOA addrs = nullptr; + PADDRINFOA goodaddr = nullptr; + SOCKET sock = INVALID_SOCKET; std::string host, port; - util::split_host_port(target, host, port); - PADDRINFOA addrs; - int status = getaddrinfo(host.c_str(), port.c_str(), nullptr, &addrs); - assert(status == 0); - PADDRINFOA goodaddr = find_good_addr(addrs); - assert(goodaddr != nullptr); - freeaddrinfo(addrs); - SOCKET sock = socket(goodaddr->ai_family, SOCK_STREAM, IPPROTO_TCP); - assert(sock != INVALID_SOCKET); u_long mode = 1; // 1 to enable non-blocking socket + + err = ""; + util::split_host_port(target, host, port); + int status = getaddrinfo(host.c_str(), port.c_str(), nullptr, &addrs); + while (status == WSATRY_AGAIN) { + status = getaddrinfo(host.c_str(), port.c_str(), nullptr, &addrs); + goto error; + } + if (status == WSAHOST_NOT_FOUND) { + err = "host not found"; + goto error; + } + if (status != 0) { + err = "DNS resolution hard failure"; + goto error; + } + goodaddr = find_good_addr(addrs); + if (goodaddr == nullptr) { + err = "host not an internet host"; + goto error; + } + sock = socket(goodaddr->ai_family, SOCK_STREAM, IPPROTO_TCP); + assert(sock != INVALID_SOCKET); status = ioctlsocket(sock, FIONBIO, &mode); assert(status == 0); status = connect(sock, goodaddr->ai_addr, goodaddr->ai_addrlen); - assert(status == 0); + if (status != 0) { + int errcode = WSAGetLastError(); + if (errcode != WSAEWOULDBLOCK) { + std::ostringstream oss; + oss << "Error " << errcode; + err = oss.str(); + goto error; + } + } + freeaddrinfo(addrs); return sock; + + error: + if (sock != INVALID_SOCKET) closesocket(sock); + if (addrs != nullptr) freeaddrinfo(addrs); + return SOCKET_ERROR; } void init(DrivenEngine *de) { @@ -87,9 +118,15 @@ public: driven_->drv_get_new_outgoing(chans); for (int chid : chans) { assert(socket_[chid] == INVALID_SOCKET); - SOCKET sock = open_connection(driven_->drv_get_target(chid)); - socket_[chid] = sock; - connected_[chid] = false; + std::string err; + SOCKET sock = open_connection(driven_->drv_get_target(chid), err); + if (sock == INVALID_SOCKET) { + driven_->drv_notify_close(chid); + engine_wakeup_ = true; + } else { + socket_[chid] = sock; + connected_[chid] = false; + } } } @@ -191,12 +228,7 @@ public: timeout.tv_sec = mstimeout / 1000; timeout.tv_usec = (mstimeout - (timeout.tv_sec*1000)) * 1000; int status = select(1, &rfds, &wfds, &efds, &timeout); - if (status == SOCKET_ERROR) { - int err = WSAGetLastError(); - std::cerr << "ERR:" << err << std::endl; - } assert(status != SOCKET_ERROR); - for (int chid = 1; chid < MAX_CHAN; chid++) { SOCKET sock = socket_[chid]; if (sock == INVALID_SOCKET) continue; @@ -215,7 +247,7 @@ public: } if (FD_ISSET(sock, &rfds)) { int nrecv = recv(sock, chbuf.get(), 65536, 0); - if (nrecv == SOCKET_ERROR) { + if ((nrecv == SOCKET_ERROR) || (nrecv == 0)) { close_socket(chid); continue; } else { @@ -234,7 +266,7 @@ public: init(de); DrivenEngine::set(de); driven_->drv_set_lua_source(util::read_lua_source("lua")); - driven_->drv_invoke_event_update(); + driven_->drv_invoke_event_init(); while (!de->drv_get_stop_driver()) { engine_wakeup_ = false; handle_lua_source(); diff --git a/luprex/core/cpp/main.cpp b/luprex/core/cpp/main.cpp index 2ef99c9a..027fbef4 100644 --- a/luprex/core/cpp/main.cpp +++ b/luprex/core/cpp/main.cpp @@ -1,9 +1,36 @@ #include "textgame.hpp" #include "driver.hpp" +#include "drivenengine.hpp" + +class TNTest : public DrivenEngine { +public: + std::unique_ptr chan_; + virtual void event_init() { + chan_ = new_outgoing_channel("stanford.edu:80"); + chan_->out()->write_bytes("GET /index.html HTTP/1.1\n\n"); + } + virtual void event_update() { + std::string input = get_stdio_channel()->in()->read_entire_contents(); + if (input != "") { + get_stdio_channel()->out()->write_bytes("stdin: "); + get_stdio_channel()->out()->write_bytes(input); + } + if (chan_ != nullptr) { + if (chan_->closed()) { + get_stdio_channel()->out()->write_bytes("Connection closed.\n"); + chan_.reset(); + } else { + chan_->in()->copy_into(get_stdio_channel()->out()); + chan_->in()->clear(); + } + } + } +}; int main(int argc, char **argv) { - TextGame tg; + //TextGame tg; + TNTest tg; driver_drive(&tg); } diff --git a/luprex/core/cpp/streambuffer.cpp b/luprex/core/cpp/streambuffer.cpp index b9518808..14683951 100644 --- a/luprex/core/cpp/streambuffer.cpp +++ b/luprex/core/cpp/streambuffer.cpp @@ -341,6 +341,11 @@ std::string StreamBuffer::read_string_limit(int64_t max_allowed) { return std::string(bytes, len); } +std::string StreamBuffer::read_entire_contents() { + std::string result(read_cursor_, fill()); + clear(); + return result; +} void StreamBuffer::overwrite_int8(int64_t write_count_after, int64_t vv) { assert(safe_to_cast_to_int8(vv)); diff --git a/luprex/core/cpp/streambuffer.hpp b/luprex/core/cpp/streambuffer.hpp index b63d8dbc..8eef118c 100644 --- a/luprex/core/cpp/streambuffer.hpp +++ b/luprex/core/cpp/streambuffer.hpp @@ -337,6 +337,10 @@ public: std::string read_string(); std::string read_string_limit(int64_t max_allowed); + // Read the entire contents of the buffer as a string. + // + std::string read_entire_contents(); + // Overwrite values previously written to the buffer. // // See the comment at the top of this file for an explanation.