#include "plop.h" #include #include #include #include #include #include #include #include #include #include #include #include #include "request.h" #include "response.h" /// Tries to create, bind and start listening on INET server socket. /// \param node Hostname /// \param service Port /// \return File descriptor of the socket or -1 in case of an error /// \see getaddrinfo(3) // TODO: Handle UNIX sockets int make_server(const char * node, const char * service) { struct addrinfo hints = { .ai_family = AF_UNSPEC, .ai_socktype = SOCK_STREAM, .ai_flags = AI_PASSIVE, }; struct addrinfo * result; struct addrinfo * it; if (0 != getaddrinfo(node, service, &hints, &result)) { return -1; // TODO: Handle errors properly } int server; int optval = 1; for (it = result; it != NULL; it = it->ai_next) { server = socket(it->ai_family, it->ai_socktype, it->ai_protocol); if (-1 == server) continue; if (-1 == setsockopt(server, SOL_SOCKET, SO_REUSEADDR, (char *) &optval, sizeof(optval))) { close(server); continue; } if (-1 == fcntl(server, F_SETFL, (fcntl(server, F_GETFL, 0) | O_NONBLOCK))) { close(server); continue; } if (0 == bind(server, it->ai_addr,it->ai_addrlen)) break; close(server); } if (it == NULL) { server = -1; // TODO: Handle errors properly } freeaddrinfo(result); if (-1 == server || -1 == listen(server, 5)) { return -1; // TODO: Handle errors properly } return server; } /// Loads a file in path, executes it, then puts first result into package.loaded table and into global table. /// \param L Lua state to load the handler into /// \param path Path the script that returns handle is located at /// \param LUA_OK if successful, appropriate Lua error otherwise int load_handler(lua_State * L, const char * path) { lua_getglobal(L, "package"); lua_pushstring(L, "loaded"); int result = lua_rawget(L, -2); if (LUA_TTABLE != result) { lua_pop(L, 2); return LUA_ERRRUN; } result = luaL_loadfile(L, path) || lua_pcall(L, 0, 1, 0); if (LUA_OK != result) { lua_pop(L, 3); return result; } lua_pushstring(L, "handler"); lua_pushvalue(L, -2); lua_rawset(L, -4); lua_setglobal(L, "handler"); lua_pop(L, 1); return LUA_OK; } /// Handles client events. /// \param L Server's Lua state /// \param event Event for client /// \return -1 if an error occured int handle_client(lua_State * L, struct epoll_event * event) { // TODO: Temporary shenanigans to avoid too much changes. struct request * r = (struct request *) event->data.ptr; struct request ** request = &r; if (-1 == parse_request(L, (*request)->fd, request)) { lua_newtable((*request)->lua); lua_pushstring((*request)->lua, "status"); lua_pushinteger((*request)->lua, 400); // TODO: How about a function that generates error responses? lua_rawset((*request)->lua, -3); } else // TODO: 0 may mean EAGAIN, stuff will be bad very soon from here. { // TODO: Push the handler to stack earlier to avoid shifting it. lua_getglobal((*request)->lua, "handler"); lua_insert((*request)->lua, 1); lua_call((*request)->lua, 5, 1); } int result = response_send((*request)->lua, (*request)->fd); close((*request)->fd); if (NULL != *request) { free_request(L, *request); *request = NULL; } return result; } /// Accepts awaiting connections if any. /// \param L Server's Lua state /// \param efd File descriptor for epoll's context /// \param server File descriptor for server socket /// \return -1 if an error occured int handle_server(lua_State * L, const int efd, const int server) { struct epoll_event e; e.events = EPOLLIN; // TODO: Add EPOLLOUT? const int client = accept(server, NULL, NULL); if (-1 == client) { return -1; // TODO: Consider not crashing entire server because of one accept. } // TODO: Request is not the enitre state of the client. Make them distinct along with responses. struct request * request = new_request(L); if (NULL == request) { return -1; } request->fd = client; e.data.ptr = request; if (-1 == epoll_ctl(efd, EPOLL_CTL_ADD, client, &e)) { return -1; } return 0; }