#include "plop.h" #include #include #include #include #include #include #include #include #include #include #include #include #include "http.h" #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; } // TODO: Write documentation for load_handler 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)) { respond_only_status((*request)->fd, STATUS_BAD_REQUEST); return -1; // TODO: Handle errors properly } // 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); size_t length; const char * body = lua_tolstring((*request)->lua, -1, &length); if (NULL != body) { respond_with_body((*request)->fd, STATUS_OK, body, (int) length); } else { respond_only_status((*request)->fd, STATUS_INTERNAL_SERVER_ERROR); } close((*request)->fd); if (NULL != *request) { free_request(L, *request); *request = NULL; } return 0; } /// 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; }