#include #include #include #include #include #include #include #include /// 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; 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 == 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 pfd Element in array processed by poll /// \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(struct pollfd * pfd, const int shift_by) { if (0 == pfd->revents) { return shift_by; } if (~(POLLIN | POLLOUT) & pfd->revents) { return -1; // TODO: Handle errors properly } static const char * response = "HTTP/1.1 200 OK\r\n" "Content-Type: text/plain; charset=utf-8\r\n" "\r\n" "plop\n"; if (-1 == send(pfd->fd, response, strlen(response), 0)) { // TODO: Handle errors properly } close(pfd->fd); (pfd - shift_by)->fd = -1; (pfd - shift_by)->events = pfd->events; 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 /// \param 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) { if (2 != argc) { return 4; } struct pollfd fdv[200]; memset(fdv, 0, sizeof(fdv)); fdv[0].fd = make_server(NULL, argv[1]); fdv[0].events = POLLIN; int fdc = 1; while (-1 != fdc && -1 != poll(fdv, fdc, -1)) { int shift_by = 0; for (int i = 1; i < fdc; ++i) { shift_by = handle_client(&fdv[i], shift_by); } fdc = handle_server(fdv, fdc - shift_by, 200); } }