#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; } /// 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(L, 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\n", (*request)->version.length, (*request)->data + (*request)->version.start, (*request)->path.length, (*request)->data + (*request)->path.start); for (int h = 0; h < (*request)->headerc; ++h) { printf( "\t'%.*s' = '%.*s'\n", (*request)->headerv[h].name.length, (*request)->data + (*request)->headerv[h].name.start, (*request)->headerv[h].value.length, (*request)->data + (*request)->headerv[h].value.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(L, *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 (EWOULDBLOCK != errno && EAGAIN != errno) { return -1; } return fdc; }