X-Git-Url: https://git.piment-noir.org/?p=deb_shairplay.git;a=blobdiff_plain;f=src%2Flib%2Fhttpd.c;h=7cb6fdea61d4f322778f3407ba9f056f6afc54b7;hp=8a3844329d86fde041d4d83b14182e7ff7a2076b;hb=1e8b64fdcd07264fcb0577d4fad13428b6cfdd7c;hpb=e1a8dfe69a0419217bcce0354fa920dc34a0569d diff --git a/src/lib/httpd.c b/src/lib/httpd.c index 8a38443..7cb6fde 100644 --- a/src/lib/httpd.c +++ b/src/lib/httpd.c @@ -36,8 +36,6 @@ struct httpd_s { logger_t *logger; httpd_callbacks_t callbacks; - int use_rtsp; - int max_connections; int open_connections; http_connection_t *connections; @@ -48,12 +46,13 @@ struct httpd_s { thread_handle_t thread; mutex_handle_t run_mutex; - /* Server fd for accepting connections */ - int server_fd; + /* Server fds for accepting connections */ + int server_fd4; + int server_fd6; }; httpd_t * -httpd_init(logger_t *logger, httpd_callbacks_t *callbacks, int max_connections, int use_rtsp) +httpd_init(logger_t *logger, httpd_callbacks_t *callbacks, int max_connections) { httpd_t *httpd; @@ -67,7 +66,6 @@ httpd_init(logger_t *logger, httpd_callbacks_t *callbacks, int max_connections, return NULL; } - httpd->use_rtsp = !!use_rtsp; httpd->max_connections = max_connections; httpd->connections = calloc(max_connections, sizeof(http_connection_t)); if (!httpd->connections) { @@ -110,7 +108,8 @@ httpd_add_connection(httpd_t *httpd, int fd, unsigned char *local, int local_len } } if (i == httpd->max_connections) { - logger_log(httpd->logger, LOGGER_INFO, "Max connections reached\n"); + /* This code should never be reached, we do not select server_fds when full */ + logger_log(httpd->logger, LOGGER_INFO, "Max connections reached"); shutdown(fd, SHUT_RDWR); closesocket(fd); return; @@ -122,6 +121,40 @@ httpd_add_connection(httpd_t *httpd, int fd, unsigned char *local, int local_len httpd->connections[i].user_data = httpd->callbacks.conn_init(httpd->callbacks.opaque, local, local_len, remote, remote_len); } +static int +httpd_accept_connection(httpd_t *httpd, int server_fd, int is_ipv6) +{ + struct sockaddr_storage remote_saddr; + socklen_t remote_saddrlen; + struct sockaddr_storage local_saddr; + socklen_t local_saddrlen; + unsigned char *local, *remote; + int local_len, remote_len; + int ret, fd; + + remote_saddrlen = sizeof(remote_saddr); + fd = accept(server_fd, (struct sockaddr *)&remote_saddr, &remote_saddrlen); + if (fd == -1) { + /* FIXME: Error happened */ + return -1; + } + + local_saddrlen = sizeof(local_saddr); + ret = getsockname(fd, (struct sockaddr *)&local_saddr, &local_saddrlen); + if (ret == -1) { + closesocket(fd); + return 0; + } + + logger_log(httpd->logger, LOGGER_INFO, "Accepted %s client on socket %d", + (is_ipv6 ? "IPv6" : "IPv4"), fd); + local = netutils_get_address(&local_saddr, &local_len); + remote = netutils_get_address(&remote_saddr, &remote_len); + + httpd_add_connection(httpd, fd, local, local_len, remote, remote_len); + return 1; +} + static void httpd_remove_connection(httpd_t *httpd, http_connection_t *connection) { @@ -165,8 +198,18 @@ httpd_thread(void *arg) /* Get the correct nfds value and set rfds */ FD_ZERO(&rfds); if (httpd->open_connections < httpd->max_connections) { - FD_SET(httpd->server_fd, &rfds); - nfds = httpd->server_fd+1; + if (httpd->server_fd4 != -1) { + FD_SET(httpd->server_fd4, &rfds); + if (nfds <= httpd->server_fd4) { + nfds = httpd->server_fd4+1; + } + } + if (httpd->server_fd6 != -1) { + FD_SET(httpd->server_fd6, &rfds); + if (nfds <= httpd->server_fd6) { + nfds = httpd->server_fd6+1; + } + } } for (i=0; imax_connections; i++) { int socket_fd; @@ -186,38 +229,27 @@ httpd_thread(void *arg) continue; } else if (ret == -1) { /* FIXME: Error happened */ - logger_log(httpd->logger, LOGGER_INFO, "Error in select\n"); + logger_log(httpd->logger, LOGGER_INFO, "Error in select"); break; } - if (FD_ISSET(httpd->server_fd, &rfds)) { - struct sockaddr_storage remote_saddr; - socklen_t remote_saddrlen; - struct sockaddr_storage local_saddr; - socklen_t local_saddrlen; - unsigned char *local, *remote; - int local_len, remote_len; - int fd; - - remote_saddrlen = sizeof(remote_saddr); - fd = accept(httpd->server_fd, (struct sockaddr *)&remote_saddr, &remote_saddrlen); - if (fd == -1) { - /* FIXME: Error happened */ + if (httpd->open_connections < httpd->max_connections && + httpd->server_fd4 != -1 && FD_ISSET(httpd->server_fd4, &rfds)) { + ret = httpd_accept_connection(httpd, httpd->server_fd4, 0); + if (ret == -1) { break; + } else if (ret == 0) { + continue; } - - local_saddrlen = sizeof(local_saddr); - ret = getsockname(fd, (struct sockaddr *)&local_saddr, &local_saddrlen); + } + if (httpd->open_connections < httpd->max_connections && + httpd->server_fd6 != -1 && FD_ISSET(httpd->server_fd6, &rfds)) { + ret = httpd_accept_connection(httpd, httpd->server_fd6, 1); if (ret == -1) { - closesocket(fd); + break; + } else if (ret == 0) { continue; } - - logger_log(httpd->logger, LOGGER_INFO, "Accepted client on socket %d\n", fd); - local = netutils_get_address(&local_saddr, &local_len); - remote = netutils_get_address(&remote_saddr, &remote_len); - - httpd_add_connection(httpd, fd, local, local_len, remote, remote_len); } for (i=0; imax_connections; i++) { http_connection_t *connection = &httpd->connections[i]; @@ -231,14 +263,14 @@ httpd_thread(void *arg) /* If not in the middle of request, allocate one */ if (!connection->request) { - connection->request = http_request_init(httpd->use_rtsp); + connection->request = http_request_init(); assert(connection->request); } - logger_log(httpd->logger, LOGGER_DEBUG, "Receiving on socket %d\n", connection->socket_fd); + logger_log(httpd->logger, LOGGER_DEBUG, "Receiving on socket %d", connection->socket_fd); ret = recv(connection->socket_fd, buffer, sizeof(buffer), 0); if (ret == 0) { - logger_log(httpd->logger, LOGGER_INFO, "Connection closed for socket %d\n", connection->socket_fd); + logger_log(httpd->logger, LOGGER_INFO, "Connection closed for socket %d", connection->socket_fd); httpd_remove_connection(httpd, connection); continue; } @@ -246,7 +278,7 @@ httpd_thread(void *arg) /* Parse HTTP request from data read from connection */ http_request_add_data(connection->request, buffer, ret); if (http_request_has_error(connection->request)) { - logger_log(httpd->logger, LOGGER_INFO, "Error in parsing: %s\n", http_request_get_error_name(connection->request)); + logger_log(httpd->logger, LOGGER_INFO, "Error in parsing: %s", http_request_get_error_name(connection->request)); httpd_remove_connection(httpd, connection); continue; } @@ -273,15 +305,22 @@ httpd_thread(void *arg) ret = send(connection->socket_fd, data+written, datalen-written, 0); if (ret == -1) { /* FIXME: Error happened */ - logger_log(httpd->logger, LOGGER_INFO, "Error in sending data\n"); + logger_log(httpd->logger, LOGGER_INFO, "Error in sending data"); break; } written += ret; } + + if (http_response_get_disconnect(response)) { + logger_log(httpd->logger, LOGGER_INFO, "Disconnecting on software request"); + httpd_remove_connection(httpd, connection); + } } else { - logger_log(httpd->logger, LOGGER_INFO, "Didn't get response\n"); + logger_log(httpd->logger, LOGGER_INFO, "Didn't get response"); } http_response_destroy(response); + } else { + logger_log(httpd->logger, LOGGER_DEBUG, "Request not complete, waiting for more data..."); } } } @@ -293,11 +332,21 @@ httpd_thread(void *arg) if (!connection->connected) { continue; } - logger_log(httpd->logger, LOGGER_INFO, "Removing connection for socket %d\n", connection->socket_fd); + logger_log(httpd->logger, LOGGER_INFO, "Removing connection for socket %d", connection->socket_fd); httpd_remove_connection(httpd, connection); } - logger_log(httpd->logger, LOGGER_INFO, "Exiting HTTP thread\n"); + /* Close server sockets since they are not used any more */ + if (httpd->server_fd4 != -1) { + closesocket(httpd->server_fd4); + httpd->server_fd4 = -1; + } + if (httpd->server_fd6 != -1) { + closesocket(httpd->server_fd6); + httpd->server_fd6 = -1; + } + + logger_log(httpd->logger, LOGGER_INFO, "Exiting HTTP thread"); return 0; } @@ -305,6 +354,9 @@ httpd_thread(void *arg) int httpd_start(httpd_t *httpd, unsigned short *port) { + /* How many connection attempts are kept in queue */ + int backlog = 5; + assert(httpd); assert(port); @@ -314,18 +366,33 @@ httpd_start(httpd_t *httpd, unsigned short *port) return 0; } - httpd->server_fd = netutils_init_socket(port, 1, 0); - if (httpd->server_fd == -1) { - logger_log(httpd->logger, LOGGER_INFO, "Error initialising socket %d\n", SOCKET_GET_ERROR()); + httpd->server_fd4 = netutils_init_socket(port, 0, 0); + if (httpd->server_fd4 == -1) { + logger_log(httpd->logger, LOGGER_ERR, "Error initialising socket %d", SOCKET_GET_ERROR()); MUTEX_UNLOCK(httpd->run_mutex); return -1; } - if (listen(httpd->server_fd, 5) == -1) { - logger_log(httpd->logger, LOGGER_INFO, "Error listening to socket\n"); + httpd->server_fd6 = netutils_init_socket(port, 1, 0); + if (httpd->server_fd6 == -1) { + logger_log(httpd->logger, LOGGER_WARNING, "Error initialising IPv6 socket %d", SOCKET_GET_ERROR()); + logger_log(httpd->logger, LOGGER_WARNING, "Continuing without IPv6 support"); + } + + if (httpd->server_fd4 != -1 && listen(httpd->server_fd4, backlog) == -1) { + logger_log(httpd->logger, LOGGER_ERR, "Error listening to IPv4 socket"); + closesocket(httpd->server_fd4); + closesocket(httpd->server_fd6); + MUTEX_UNLOCK(httpd->run_mutex); + return -2; + } + if (httpd->server_fd6 != -1 && listen(httpd->server_fd6, backlog) == -1) { + logger_log(httpd->logger, LOGGER_ERR, "Error listening to IPv6 socket"); + closesocket(httpd->server_fd4); + closesocket(httpd->server_fd6); MUTEX_UNLOCK(httpd->run_mutex); return -2; } - logger_log(httpd->logger, LOGGER_INFO, "Initialized server socket\n"); + logger_log(httpd->logger, LOGGER_INFO, "Initialized server socket(s)"); /* Set values correctly and create new thread */ httpd->running = 1; @@ -336,6 +403,20 @@ httpd_start(httpd_t *httpd, unsigned short *port) return 1; } +int +httpd_is_running(httpd_t *httpd) +{ + int running; + + assert(httpd); + + MUTEX_LOCK(httpd->run_mutex); + running = httpd->running || !httpd->joined; + MUTEX_UNLOCK(httpd->run_mutex); + + return running; +} + void httpd_stop(httpd_t *httpd) {