#include #include #include #include #include #include #include #include #include #include #include #include #include #include "http.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; } /// Progresses the connection for a client and shifts it back in the array. /// `pfd` must be at least `shift_by + 1`-th element of the array. /// \param L Server's Lua state /// \param pfd Element in array processed by poll /// \param request Pointer to request context associated with current client /// \param shift_by Element will be moved by in array this many indices /// \return Shift value for next handler /// \see poll(2) int handle_client(lua_State * L, struct pollfd * pfd, struct request ** request, const int shift_by) { if (0 == pfd->revents) { return shift_by; } if (~(POLLIN | POLLOUT) & pfd->revents) { return -1; // TODO: Handle errors properly } if (-1 == parse_request(pfd->fd, request)) { respond_only_status(pfd->fd, STATUS_BAD_REQUEST); return -1; // TODO: Handle errors properly } // TODO: Remove debug information or move to logging printf( "(%.*s) [%.*s] %.*s\n", (*request)->version.length, (*request)->data + (*request)->version.start, (*request)->method.length, (*request)->data + (*request)->method.start, (*request)->path.length, (*request)->data + (*request)->path.start); // TODO: Use results from parsing instead of raw data lua_getglobal(L, "Handler"); lua_pushlstring(L, (*request)->data, (*request)->length); lua_call(L, 1, 1); size_t length; const char * body = lua_tolstring(L, -1, &length); if (NULL != body) { respond_with_body(pfd->fd, STATUS_OK, body, (int) length); } else { respond_only_status(pfd->fd, STATUS_INTERNAL_SERVER_ERROR); } close(pfd->fd); (pfd - shift_by)->fd = -1; (pfd - shift_by)->events = pfd->events; if (NULL != *request) { free_request(*request); *request = NULL; } return shift_by + 1; } /// Accepts awaiting connections if any. /// \param fdv Array of descriptors for poll /// \param fdc Number of valid descriptors in the array including the server /// \param size Size limit for the array /// \return Number of valid descriptors in the array or -1 if an error occurred. /// \see poll(2) int handle_server(struct pollfd * fdv, int fdc, const int size) { if (0 == fdv[0].revents) { return fdc; } while (size > fdc + 1 && -1 != (fdv[fdc].fd = accept(fdv[0].fd, NULL, NULL))) { fdv[fdc].events = POLLIN | POLLOUT; fdc++; } if (errno != EWOULDBLOCK && errno != EAGAIN) { return -1; } return fdc; } /// Standard entry point for the program. /// \param argc Argument count /// \param argv Argument array /// \return Error code int main(int argc, char ** argv) { lua_State * L = luaL_newstate(); luaL_openlibs(L); if (3 != argc) { return 1; // TODO: Document error codes/print usage information } if (LUA_OK != luaL_dofile(L, argv[2])) { return 2; // TODO: Document error codes/print usage information } static const int max_clients = 200; int fdc = 0; struct request * requests[max_clients]; struct pollfd fdv[max_clients]; memset(requests, 0, sizeof(requests)); memset(fdv, 0, sizeof(fdv)); fdv[0].fd = make_server(NULL, argv[1]); fdv[0].events = POLLIN; fdc++; if (-1 == fdv[0].fd) { return 1; // TODO: Document error codes/print usage information } while (0 < fdc && -1 != poll(fdv, fdc, -1)) { int shift_by = 0; for (int i = 1; i < fdc; ++i) { shift_by = handle_client(L, &fdv[i], &requests[i], shift_by); } fdc = handle_server(fdv, fdc - shift_by, max_clients); } }