mirror of
				https://github.com/SuperBFG7/ympd
				synced 2025-10-31 13:53:00 +00:00 
			
		
		
		
	Fix: use mongoose user_data to avoid global states
This commit is contained in:
		| @@ -51,6 +51,10 @@ | |||||||
| #define LOG_VERBOSE() if (config.loglevel >= 2)  | #define LOG_VERBOSE() if (config.loglevel >= 2)  | ||||||
| #define LOG_DEBUG() if (config.loglevel == 3)  | #define LOG_DEBUG() if (config.loglevel == 3)  | ||||||
|  |  | ||||||
|  | #define LOG_INFO2() if (config->loglevel >= 1)  | ||||||
|  | #define LOG_VERBOSE2() if (config->loglevel >= 2)  | ||||||
|  | #define LOG_DEBUG2() if (config->loglevel == 3)  | ||||||
|  |  | ||||||
| //check and return buffer size | //check and return buffer size | ||||||
| #define CHECK_RETURN_LEN() do { \ | #define CHECK_RETURN_LEN() do { \ | ||||||
|     if (len > MAX_SIZE) \ |     if (len > MAX_SIZE) \ | ||||||
| @@ -58,10 +62,6 @@ | |||||||
|     return len; \ |     return len; \ | ||||||
| } while (0) | } while (0) | ||||||
|  |  | ||||||
| //enumeration helpers |  | ||||||
| #define GEN_ENUM(X) X, |  | ||||||
| #define GEN_STR(X) #X, |  | ||||||
|  |  | ||||||
| //signal handler | //signal handler | ||||||
| sig_atomic_t s_signal_received; | sig_atomic_t s_signal_received; | ||||||
|  |  | ||||||
|   | |||||||
| @@ -339,7 +339,8 @@ int main(int argc, char **argv) { | |||||||
|     config.max_elements_per_page = 100; |     config.max_elements_per_page = 100; | ||||||
|     config.last_played_count = 20; |     config.last_played_count = 20; | ||||||
|     char *etcdir = strdup(argv[1]); |     char *etcdir = strdup(argv[1]); | ||||||
|     config.etcdir = dirname(etcdir); |     config.etcdir = strdup(dirname(etcdir)); | ||||||
|  |     free(etcdir); | ||||||
|     config.syscmds = false; |     config.syscmds = false; | ||||||
|     config.localplayer = true; |     config.localplayer = true; | ||||||
|     config.loglevel = 1; |     config.loglevel = 1; | ||||||
| @@ -383,7 +384,7 @@ int main(int argc, char **argv) { | |||||||
|  |  | ||||||
|     //init webserver |     //init webserver | ||||||
|     struct mg_mgr mgr; |     struct mg_mgr mgr; | ||||||
|     if (!web_server_init(&mgr)) { |     if (!web_server_init(&mgr, &config)) { | ||||||
|         return EXIT_FAILURE; |         return EXIT_FAILURE; | ||||||
|     } |     } | ||||||
|     //drop privileges |     //drop privileges | ||||||
|   | |||||||
| @@ -119,6 +119,9 @@ | |||||||
|     X(MPD_API_COLS_SAVE) \ |     X(MPD_API_COLS_SAVE) \ | ||||||
|     X(MPD_API_SYSCMD) |     X(MPD_API_SYSCMD) | ||||||
|  |  | ||||||
|  | #define GEN_ENUM(X) X, | ||||||
|  | #define GEN_STR(X) #X, | ||||||
|  |  | ||||||
| enum mpd_cmd_ids { | enum mpd_cmd_ids { | ||||||
|     MPD_CMDS(GEN_ENUM) |     MPD_CMDS(GEN_ENUM) | ||||||
| }; | }; | ||||||
|   | |||||||
							
								
								
									
										157
									
								
								src/web_server.c
									
									
									
									
									
								
							
							
						
						
									
										157
									
								
								src/web_server.c
									
									
									
									
									
								
							| @@ -22,6 +22,8 @@ | |||||||
|    Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. |    Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | ||||||
| */ | */ | ||||||
|  |  | ||||||
|  | #include <limits.h> | ||||||
|  |  | ||||||
| #include "global.h" | #include "global.h" | ||||||
| #include "web_server.h" | #include "web_server.h" | ||||||
| #include "mpd_client.h" | #include "mpd_client.h" | ||||||
| @@ -34,50 +36,60 @@ static void ev_handler_redirect(struct mg_connection *nc_http, int ev, void *ev_ | |||||||
| static void send_ws_notify(struct mg_mgr *mgr, struct work_result_t *response); | static void send_ws_notify(struct mg_mgr *mgr, struct work_result_t *response); | ||||||
| static void send_api_response(struct mg_mgr *mgr, struct work_result_t *response); | static void send_api_response(struct mg_mgr *mgr, struct work_result_t *response); | ||||||
|  |  | ||||||
|  | typedef struct t_user_data { | ||||||
|  |     void *config; //pointer to mympd config | ||||||
|  |     long conn_id;  | ||||||
|  | } t_user_data; | ||||||
|  |  | ||||||
| //api functions | //api functions | ||||||
| bool web_server_init(void *arg) { | bool web_server_init(void *arg_mgr, void *arg_config) { | ||||||
|     struct mg_mgr *mgr = (struct mg_mgr *) arg; |     struct mg_mgr *mgr = (struct mg_mgr *) arg_mgr; | ||||||
|     struct mg_connection *nc; |     t_config *config = (t_config *) arg_config; | ||||||
|  |     struct mg_connection *nc_https; | ||||||
|     struct mg_connection *nc_http; |     struct mg_connection *nc_http; | ||||||
|     struct mg_bind_opts bind_opts; |     struct mg_bind_opts bind_opts_https; | ||||||
|     const char *err; |     struct mg_bind_opts bind_opts_http; | ||||||
|  |     const char *err_https; | ||||||
|  |     const char *err_http; | ||||||
|  |      | ||||||
|  |     t_user_data *user_data = (t_user_data*)malloc(sizeof(t_user_data)); | ||||||
|  |     user_data->config = config; | ||||||
|  |     user_data->conn_id = 1; | ||||||
|      |      | ||||||
|     mg_mgr_init(mgr, NULL); |     mg_mgr_init(mgr, NULL); | ||||||
|      |      | ||||||
|     if (config.ssl == true) { |     //bind to webport | ||||||
|         nc_http = mg_bind(mgr, config.webport, ev_handler_redirect); |     memset(&bind_opts_http, 0, sizeof(bind_opts_http)); | ||||||
|         if (nc_http == NULL) { |     bind_opts_http.user_data = (void *)user_data; | ||||||
|             printf("Error listening on port %s\n", config.webport); |     bind_opts_http.error_string = &err_http; | ||||||
|             mg_mgr_free(mgr); |     if (config->ssl == true) | ||||||
|             return false; |         nc_http = mg_bind_opt(mgr, config->webport, ev_handler_redirect, bind_opts_http); | ||||||
|         } |     else | ||||||
|         mg_set_protocol_http_websocket(nc_http); |         nc_http = mg_bind_opt(mgr, config->webport, ev_handler, bind_opts_http); | ||||||
|         LOG_INFO() printf("Listening on http port %s (redirect only).\n", config.webport); |     if (nc_http == NULL) { | ||||||
|  |         printf("Error listening on port %s\n", config->webport); | ||||||
|         memset(&bind_opts, 0, sizeof(bind_opts)); |         mg_mgr_free(mgr); | ||||||
|         bind_opts.ssl_cert = config.sslcert; |         return false; | ||||||
|         bind_opts.ssl_key = config.sslkey; |  | ||||||
|         bind_opts.error_string = &err; |  | ||||||
|         nc = mg_bind_opt(mgr, config.sslport, ev_handler, bind_opts); |  | ||||||
|         if (nc == NULL) { |  | ||||||
|             printf("Error listening on port %s: %s\n", config.sslport, err); |  | ||||||
|             mg_mgr_free(mgr); |  | ||||||
|             return false; |  | ||||||
|         }  |  | ||||||
|         LOG_INFO() printf("Listening on ssl port %s\n", config.sslport); |  | ||||||
|     } |     } | ||||||
|     else { |     mg_set_protocol_http_websocket(nc_http); | ||||||
|         nc = mg_bind(mgr, config.webport, ev_handler); |     LOG_INFO2() printf("Listening on http port %s.\n", config->webport); | ||||||
|         if (nc == NULL) { |  | ||||||
|             printf("Error listening on port %s\n", config.webport); |     //bind to sslport | ||||||
|  |     if (config->ssl == true) { | ||||||
|  |         memset(&bind_opts_https, 0, sizeof(bind_opts_https)); | ||||||
|  |         bind_opts_https.user_data = (void *)user_data; | ||||||
|  |         bind_opts_https.error_string = &err_https; | ||||||
|  |         bind_opts_https.ssl_cert = config->sslcert; | ||||||
|  |         bind_opts_https.ssl_key = config->sslkey; | ||||||
|  |         nc_https = mg_bind_opt(mgr, config->sslport, ev_handler, bind_opts_https); | ||||||
|  |         if (nc_https == NULL) { | ||||||
|  |             printf("Error listening on port %s: %s\n", config->sslport, err_https); | ||||||
|             mg_mgr_free(mgr); |             mg_mgr_free(mgr); | ||||||
|             return false; |             return false; | ||||||
|         }  |         }  | ||||||
|         LOG_INFO() printf("Listening on http port %s\n", config.webport); |         LOG_INFO2() printf("Listening on ssl port %s\n", config->sslport); | ||||||
|  |         mg_set_protocol_http_websocket(nc_https); | ||||||
|     } |     } | ||||||
|      |  | ||||||
|     mg_set_protocol_http_websocket(nc); |  | ||||||
|      |  | ||||||
|     return mgr; |     return mgr; | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -113,27 +125,23 @@ static int is_websocket(const struct mg_connection *nc) { | |||||||
| } | } | ||||||
|  |  | ||||||
| static void send_ws_notify(struct mg_mgr *mgr, struct work_result_t *response) { | static void send_ws_notify(struct mg_mgr *mgr, struct work_result_t *response) { | ||||||
|     struct mg_connection *c; |     struct mg_connection *nc; | ||||||
|     LOG_DEBUG() fprintf(stderr, "DEBUG: Got ws notify, broadcasting\n"); |     for (nc = mg_next(mgr, NULL); nc != NULL; nc = mg_next(mgr, nc)) { | ||||||
|      |         if (!is_websocket(nc)) | ||||||
|     for (c = mg_next(mgr, NULL); c != NULL; c = mg_next(mgr, c)) { |  | ||||||
|         if (!is_websocket(c)) |  | ||||||
|             continue; |             continue; | ||||||
|         mg_send_websocket_frame(c, WEBSOCKET_OP_TEXT, response->data, response->length); |         mg_send_websocket_frame(nc, WEBSOCKET_OP_TEXT, response->data, response->length); | ||||||
|     } |     } | ||||||
|     free(response); |     free(response); | ||||||
| } | } | ||||||
|  |  | ||||||
| static void send_api_response(struct mg_mgr *mgr, struct work_result_t *response) { | static void send_api_response(struct mg_mgr *mgr, struct work_result_t *response) { | ||||||
|     struct mg_connection *c; |     struct mg_connection *nc; | ||||||
|     LOG_DEBUG() fprintf(stderr, "DEBUG: Got API response for connection %lu.\n", response->conn_id); |     for (nc = mg_next(mgr, NULL); nc != NULL; nc = mg_next(mgr, nc)) { | ||||||
|      |         if (nc->user_data != NULL) { | ||||||
|     for (c = mg_next(mgr, NULL); c != NULL; c = mg_next(mgr, c)) { |             t_user_data *user_data = (t_user_data *) nc->user_data; | ||||||
|         if (c->user_data != NULL) { |             if (user_data->conn_id == response->conn_id) { | ||||||
|             if ((unsigned long)c->user_data == response->conn_id) { |                 mg_send_head(nc, 200, response->length, "Content-Type: application/json"); | ||||||
|                 LOG_DEBUG() fprintf(stderr, "DEBUG: Sending to connection %lu: %s\n", (unsigned long)c->user_data, response->data); |                 mg_printf(nc, "%s", response->data); | ||||||
|                 mg_send_head(c, 200, response->length, "Content-Type: application/json"); |  | ||||||
|                 mg_printf(c, "%s", response->data); |  | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| @@ -141,21 +149,28 @@ static void send_api_response(struct mg_mgr *mgr, struct work_result_t *response | |||||||
| } | } | ||||||
|  |  | ||||||
| static void ev_handler(struct mg_connection *nc, int ev, void *ev_data) { | static void ev_handler(struct mg_connection *nc, int ev, void *ev_data) { | ||||||
|     (void) nc; |     t_user_data *user_data = (t_user_data *) nc->user_data; | ||||||
|     (void) ev_data; |     t_config *config = (t_config *) user_data->config; | ||||||
|      |      | ||||||
|     switch(ev) { |     switch(ev) { | ||||||
|         case MG_EV_ACCEPT: { |         case MG_EV_ACCEPT: { | ||||||
|             struct timespec start; |             //increment conn_id | ||||||
|             clock_gettime(CLOCK_MONOTONIC_RAW, &start); |             if (user_data->conn_id < LONG_MAX) | ||||||
|             long unsigned conn_id = (start.tv_sec * 1000 + start.tv_nsec / 1000) * 100 + randrange(100); |                 user_data->conn_id++; | ||||||
|             nc->user_data = (void *)conn_id; |             else | ||||||
|             LOG_DEBUG() fprintf(stderr, "DEBUG: New connection id %lu.\n", conn_id); |                 user_data->conn_id = 1; | ||||||
|  |              | ||||||
|  |             //remove mgr user_data and set connection specific user_data | ||||||
|  |             t_user_data *nc_user_data = (t_user_data*)malloc(sizeof(t_user_data)); | ||||||
|  |             nc_user_data->config = config; | ||||||
|  |             nc_user_data->conn_id = user_data->conn_id; | ||||||
|  |             nc->user_data = nc_user_data; | ||||||
|  |             LOG_DEBUG2() fprintf(stderr, "DEBUG: New connection id %ld.\n", user_data->conn_id); | ||||||
|             break; |             break; | ||||||
|         } |         } | ||||||
|         case MG_EV_WEBSOCKET_HANDSHAKE_REQUEST: { |         case MG_EV_WEBSOCKET_HANDSHAKE_REQUEST: { | ||||||
|             struct http_message *hm = (struct http_message *) ev_data; |             struct http_message *hm = (struct http_message *) ev_data; | ||||||
|             LOG_VERBOSE() printf("New websocket request: %.*s\n", hm->uri.len, hm->uri.p); |             LOG_VERBOSE2() printf("New websocket request (%ld): %.*s\n", user_data->conn_id, hm->uri.len, hm->uri.p); | ||||||
|             if (mg_vcmp(&hm->uri, "/ws") != 0) { |             if (mg_vcmp(&hm->uri, "/ws") != 0) { | ||||||
|                 printf("ERROR: Websocket request not to /ws, closing connection\n"); |                 printf("ERROR: Websocket request not to /ws, closing connection\n"); | ||||||
|                 mg_printf(nc, "%s", "HTTP/1.1 403 FORBIDDEN\r\n\r\n"); |                 mg_printf(nc, "%s", "HTTP/1.1 403 FORBIDDEN\r\n\r\n"); | ||||||
| @@ -164,17 +179,17 @@ static void ev_handler(struct mg_connection *nc, int ev, void *ev_data) { | |||||||
|             break; |             break; | ||||||
|         } |         } | ||||||
|         case MG_EV_WEBSOCKET_HANDSHAKE_DONE: { |         case MG_EV_WEBSOCKET_HANDSHAKE_DONE: { | ||||||
|              LOG_VERBOSE() printf("New Websocket connection established.\n"); |              LOG_VERBOSE2() printf("New Websocket connection established (%ld).\n", user_data->conn_id); | ||||||
|              char response[] = "{\"type\": \"welcome\", \"data\": {\"mympdVersion\": \"" MYMPD_VERSION "\"}}"; |              char response[] = "{\"type\": \"welcome\", \"data\": {\"mympdVersion\": \"" MYMPD_VERSION "\"}}"; | ||||||
|              mg_send_websocket_frame(nc, WEBSOCKET_OP_TEXT, response, strlen(response)); |              mg_send_websocket_frame(nc, WEBSOCKET_OP_TEXT, response, strlen(response)); | ||||||
|              break; |              break; | ||||||
|         } |         } | ||||||
|         case MG_EV_HTTP_REQUEST: { |         case MG_EV_HTTP_REQUEST: { | ||||||
|             struct http_message *hm = (struct http_message *) ev_data; |             struct http_message *hm = (struct http_message *) ev_data; | ||||||
|             LOG_VERBOSE() printf("HTTP request: %.*s\n", hm->uri.len, hm->uri.p); |             LOG_VERBOSE2() printf("HTTP request (%ld): %.*s\n", user_data->conn_id, hm->uri.len, hm->uri.p); | ||||||
|             if (mg_vcmp(&hm->uri, "/api") == 0) { |             if (mg_vcmp(&hm->uri, "/api") == 0) { | ||||||
|                 struct work_request_t *request = (struct work_request_t*)malloc(sizeof(struct work_request_t)); |                 struct work_request_t *request = (struct work_request_t*)malloc(sizeof(struct work_request_t)); | ||||||
|                 request->conn_id = (unsigned long)nc->user_data; |                 request->conn_id = user_data->conn_id; | ||||||
|                 request->length = copy_string(request->data, hm->body.p, 1000, hm->body.len); |                 request->length = copy_string(request->data, hm->body.p, 1000, hm->body.len); | ||||||
|                 tiny_queue_push(mpd_client_queue, request); |                 tiny_queue_push(mpd_client_queue, request); | ||||||
|             } |             } | ||||||
| @@ -187,13 +202,8 @@ static void ev_handler(struct mg_connection *nc, int ev, void *ev_data) { | |||||||
|             break; |             break; | ||||||
|         } |         } | ||||||
|         case MG_EV_CLOSE: { |         case MG_EV_CLOSE: { | ||||||
|             if (nc->user_data) { |             LOG_VERBOSE2() fprintf(stderr, "HTTP connection %ld closed.\n", user_data->conn_id); | ||||||
|                 LOG_VERBOSE() fprintf(stderr, "HTTP connection %lu closed.\n", (unsigned long)nc->user_data); |             free(nc->user_data); | ||||||
|                 nc->user_data = NULL; |  | ||||||
|             } |  | ||||||
|             else { |  | ||||||
|                 LOG_VERBOSE() printf("HTTP connection closed.\n"); |  | ||||||
|             } |  | ||||||
|             break; |             break; | ||||||
|         } |         } | ||||||
|         default: { |         default: { | ||||||
| @@ -202,7 +212,7 @@ static void ev_handler(struct mg_connection *nc, int ev, void *ev_data) { | |||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| static void ev_handler_redirect(struct mg_connection *nc_http, int ev, void *ev_data) { | static void ev_handler_redirect(struct mg_connection *nc, int ev, void *ev_data) { | ||||||
|     char *host; |     char *host; | ||||||
|     char *crap; |     char *crap; | ||||||
|     char host_header[1024]; |     char host_header[1024]; | ||||||
| @@ -210,15 +220,18 @@ static void ev_handler_redirect(struct mg_connection *nc_http, int ev, void *ev_ | |||||||
|         case MG_EV_HTTP_REQUEST: { |         case MG_EV_HTTP_REQUEST: { | ||||||
|             struct http_message *hm = (struct http_message *) ev_data; |             struct http_message *hm = (struct http_message *) ev_data; | ||||||
|             struct mg_str *host_hdr = mg_get_http_header(hm, "Host"); |             struct mg_str *host_hdr = mg_get_http_header(hm, "Host"); | ||||||
|  |             t_user_data *user_data = (t_user_data *) nc->user_data; | ||||||
|  |             t_config *config = (t_config *) user_data->config; | ||||||
|  |              | ||||||
|             snprintf(host_header, 1024, "%.*s", host_hdr->len, host_hdr->p); |             snprintf(host_header, 1024, "%.*s", host_hdr->len, host_hdr->p); | ||||||
|             host = strtok_r(host_header, ":", &crap); |             host = strtok_r(host_header, ":", &crap); | ||||||
|             char s_redirect[250]; |             char s_redirect[250]; | ||||||
|             if (strcmp(config.sslport, "443") == 0) |             if (strcmp(config->sslport, "443") == 0) | ||||||
|                 snprintf(s_redirect, 250, "https://%s/", host); |                 snprintf(s_redirect, 250, "https://%s/", host); | ||||||
|             else |             else | ||||||
|                 snprintf(s_redirect, 250, "https://%s:%s/", host, config.sslport); |                 snprintf(s_redirect, 250, "https://%s:%s/", host, config->sslport); | ||||||
|             LOG_VERBOSE() printf("Redirecting to %s\n", s_redirect); |             LOG_VERBOSE2() printf("Redirecting to %s\n", s_redirect); | ||||||
|             mg_http_send_redirect(nc_http, 301, mg_mk_str(s_redirect), mg_mk_str(NULL)); |             mg_http_send_redirect(nc, 301, mg_mk_str(s_redirect), mg_mk_str(NULL)); | ||||||
|             break; |             break; | ||||||
|         } |         } | ||||||
|         default: { |         default: { | ||||||
|   | |||||||
| @@ -30,19 +30,19 @@ | |||||||
| tiny_queue_t *web_server_queue; | tiny_queue_t *web_server_queue; | ||||||
|  |  | ||||||
| struct work_request_t { | struct work_request_t { | ||||||
|     unsigned long conn_id;  // needed to identify the connection where to send the reply |     long conn_id;  // needed to identify the connection where to send the reply | ||||||
|     char data[1000]; |     char data[1000]; | ||||||
|     int length; |     int length; | ||||||
| } work_request_t; | } work_request_t; | ||||||
|  |  | ||||||
| struct work_result_t { | struct work_result_t { | ||||||
|     unsigned long conn_id;  // needed to identify the connection where to send the reply |     long conn_id;  // needed to identify the connection where to send the reply | ||||||
|     char data[MAX_SIZE]; |     char data[MAX_SIZE]; | ||||||
|     int length; |     int length; | ||||||
| } work_result_t; | } work_result_t; | ||||||
|  |  | ||||||
| void *web_server_loop(void *arg); | void *web_server_loop(void *arg); | ||||||
| bool web_server_init(void *arg); | bool web_server_init(void *arg_mgr, void *arg_config); | ||||||
| void web_server_free(void *arg); | void web_server_free(void *arg); | ||||||
|  |  | ||||||
| #endif | #endif | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user
	 jcorporation
					jcorporation