static int httpd_thread_cleanup(void) { int status = kNoErr; switch (httpd_state) { case HTTPD_INIT_DONE: /* * We have no threads, no sockets to close. */ break; case HTTPD_THREAD_RUNNING: status = httpd_signal_and_wait_for_halt(); if (status != kNoErr) httpd_d("Unable to stop thread. Force killing it."); /* No break here on purpose */ case HTTPD_THREAD_SUSPENDED: status = mico_rtos_delete_thread(&httpd_main_thread); if (status != kNoErr) httpd_d("Failed to delete thread."); status = httpd_close_sockets(); httpd_state = HTTPD_INIT_DONE; break; default: return -kInProgressErr; } return status; }
static int httpd_signal_and_wait_for_halt() { const int total_wait_time_ms = 1000 * 20; /* 20 seconds */ const int check_interval_ms = 100; /* 100 ms */ int num_iterations = total_wait_time_ms / check_interval_ms; httpd_d("Sent stop request"); httpd_stop_req = TRUE; /* Do a dummy local connect to wakeup the httpd thread */ int sockfd; int rv = tcp_local_connect(&sockfd); if (rv != kNoErr) return rv; while (httpd_state != HTTPD_THREAD_SUSPENDED && num_iterations--) { mico_thread_msleep(check_interval_ms); } close(sockfd); if (httpd_state == HTTPD_THREAD_SUSPENDED) return kNoErr; httpd_d("Timed out waiting for httpd to stop. " "Force closed temporary socket"); httpd_stop_req = FALSE; return -kInProgressErr; }
/* This pairs with httpd_shutdown() */ int httpd_init() { int status; if (httpd_state != HTTPD_INACTIVE) return kNoErr; httpd_d("Initializing"); client_sockfd = -1; http_sockfd = -1; status = httpd_wsgi_init(); if (status != kNoErr) { httpd_d("Failed to initialize WSGI!"); return status; } status = httpd_ssi_init(); if (status != kNoErr) { httpd_d("Failed to initialize SSI!"); return status; } httpd_state = HTTPD_INIT_DONE; return kNoErr; }
static void httpd_suspend_thread(bool warn) { if (warn) { httpd_d("Suspending thread"); } else { httpd_d("Suspending thread"); } httpd_close_sockets(); httpd_state = HTTPD_THREAD_SUSPENDED; mico_rtos_suspend_thread(NULL); }
/* This pairs with httpd_init() */ int httpd_shutdown(void) { int ret; httpd_d("Shutting down."); ret = httpd_thread_cleanup(); if (ret != kNoErr) httpd_d("Thread cleanup failed"); httpd_state = HTTPD_INACTIVE; return ret; }
static void httpd_main(void *arg) { int status, max_sockfd = -1; fd_set readfds, active_readfds; status = httpd_setup_main_sockets(); if (status != kNoErr) httpd_suspend_thread(true); FD_ZERO(&readfds); FD_SET(http_sockfd, &readfds); max_sockfd = http_sockfd; while (1) { httpd_d("Waiting on main socket"); httpd_select(max_sockfd, &readfds, &active_readfds, -1); httpd_handle_client_connection(&active_readfds); } /* * Thread will never come here. The functions called from the above * infinite loop will cleanly shutdown this thread when situation * demands so. */ }
/* Register an SSI handler */ int httpd_register_ssi(struct httpd_ssi_call *ssi_call) { int i; if (!ssi_ready) return -kInProgressErr; /* Verify that the ssi is not already registered */ for (i = 0; i < MAX_REGISTERED_SSIS; i++) { if (!calls[i]) { continue; } if (strncmp(calls[i]->name, ssi_call->name, strlen(calls[i]->name)) == 0) { return -kInProgressErr; } } /* Find an unoccupied slot in the table */ for (i = 0; i < MAX_REGISTERED_SSIS && calls[i]; i++) ; /* Check that we're not overrunning the table */ if (i == MAX_REGISTERED_SSIS) return -WM_E_HTTPD_SSI_MAX; calls[i] = ssi_call; httpd_d("Register ssi %s at %d", ssi_call->name, i); return kNoErr; }
/* Parse the individual components of the HTTP header and reflect it in * httpd_request_t structure. */ static int __httpd_parse_hdr_tags(char *data_p, int len, httpd_request_t *req_p, uint8_t *done) { // ASSERT((data_p != NULL) && (req_p != NULL)); if ((*data_p == ISO_nl) || (*data_p == ISO_cr) || (*data_p == 0)) { /* Indicate that the parsing is now complete */ *done = 1; return kNoErr; } if (strncasecmp(data_p, http_content_len, sizeof(http_content_len) - 1) == 0) { req_p->body_nbytes = atol(&data_p[sizeof(http_content_len) - 1]); if (req_p->body_nbytes == 0) { httpd_d("got 0 for body length"); return kNoErr; } req_p->remaining_bytes = req_p->body_nbytes; } else if (strncasecmp(data_p, http_user_agent, sizeof(http_user_agent) - 1) == 0) { /* chomp the : and ' ' and pass it to the internal parser */ httpd_parse_useragent(data_p + sizeof(http_user_agent) + 1, &req_p->agent); } else if (strncasecmp(data_p, http_content_type, sizeof(http_content_type) - 1) == 0) { memcpy(req_p->content_type, data_p + strlen(http_content_type), strlen(data_p)); } else if (strncasecmp(data_p, "If-None-Match", sizeof("If-None-Match") - 1) == 0) { /* * We use the FTFS CRC to generate ETag. Hence, the ETag we * receive is not expected to be more that 32 bits in value. */ const char *first_double_quote = strchr(data_p, '"'); if (!first_double_quote) { httpd_d("If_None_Match has no double quote"); return -kInProgressErr; } const char *etag_start = ++first_double_quote; req_p->etag_val = strtol(etag_start, NULL, 16); req_p->if_none_match = TRUE; } else if (strncasecmp(data_p, http_encoding, sizeof(http_encoding) - 1) == 0) { if (!strncasecmp(&data_p[sizeof(http_encoding) - 1], HTTP_CHUNKED, sizeof(HTTP_CHUNKED) - 1)) req_p->chunked = 1; } return kNoErr; }
static inline int tcp_local_connect(int *sockfd) { uint16_t port; int retry_cnt = 3; httpd_d("Doing local connect for shutting down server\n\r"); *sockfd = -1; while (retry_cnt--) { *sockfd = socket(AF_INET, SOCK_STREAM, 0); if (*sockfd >= 0) break; /* Wait some time to allow some sockets to get released */ mico_thread_msleep(1000); } if (*sockfd < 0) { httpd_d("Unable to create socket to stop server"); return -kInProgressErr; } port = HTTP_PORT; char *host = "127.0.0.1"; struct sockaddr_t addr; memset(&addr, 0, sizeof(struct sockaddr_t)); addr.s_port = port; addr.s_ip = inet_addr(host); httpd_d("local connecting ..."); if (connect(*sockfd, &addr, sizeof(addr)) != 0) { httpd_d("Server close error. tcp connect failed %s:%d", host, port); close(*sockfd); *sockfd = 0; return -kInProgressErr; } /* * We do not wish to do anything with this connection. Its sole * purpose was to wake the main httpd thread out of sleep. */ return kNoErr; }
void httpd_set_error(const char *fmt, ...) { va_list argp; va_start(argp, fmt); vsnprintf(httpd_error, HTTPD_MAX_ERROR_STRING + 1, fmt, argp); va_end(argp); httpd_d("%s", httpd_error); }
static int httpd_send_file(struct fs *fs, int conn, const sys_file_t *sys_file_p) { char data[SND_BUF_LEN]; int len; int total = 0; int err = WM_SUCCESS; /* Chunk begin */ err = httpd_send_chunk_begin(conn, htsys_get_file_size(fs, sys_file_p)); if (err != WM_SUCCESS) return err; /* Send the file */ while (TRUE) { len = htsys_read(fs, sys_file_p, (unsigned char *)data, SND_BUF_LEN); if (len <= 0) { if (len < 0) httpd_d("read failed\r\n"); break; } err = httpd_send(conn, data, len); if (err != WM_SUCCESS) { httpd_d("Failed to send file (sent %d bytes)\r\n", total); break; } total += len; } htsys_close(fs, sys_file_p); if (err != WM_SUCCESS) { return err; } /* Chunk end */ err = httpd_send_crlf(conn); return err; }
/* This pairs with httpd_stop() */ int httpd_start(void) { int status; if (httpd_state != HTTPD_INIT_DONE) { httpd_d("Already started"); return kNoErr; } status = mico_rtos_create_thread(&httpd_main_thread, MICO_APPLICATION_PRIORITY, "httpd", httpd_main, http_server_thread_stack_size, NULL); if (status != kNoErr) { httpd_d("Failed to create httpd thread: %d", status); return -kInProgressErr; } httpd_state = HTTPD_THREAD_RUNNING; return kNoErr; }
int htsys_getln_soc(int sd, char *data_p, int buflen) { int len = 0; char *c_p; int result; // ASSERT(data_p != NULL); c_p = data_p; /* Read one byte at a time */ while ((result = httpd_recv(sd, c_p, 1, 0)) != 0) { /* error on recv */ if (result == -1) { *c_p = 0; httpd_d("recv failed len: %d, err: 0x%x", len, serr); return -kInProgressErr; } /* If new line... */ if ((*c_p == ISO_nl) || (*c_p == ISO_cr)) { result = httpd_recv(sd, c_p, 1, 0); if ((*c_p != ISO_nl) && (*c_p != ISO_cr)) { httpd_d("should get double CR LF: %d, %d", (int)*c_p, result); } break; } len++; c_p++; /* give up here since we'll at least need 3 more chars to * finish off the line */ if (len >= buflen - 1) { httpd_d("buf full: recv didn't read complete line."); break; } } *c_p = 0; return len; }
/* Find a matching SSI function from the table and return it */ httpd_ssifunction httpd_ssi(char *name) { struct httpd_ssi_call *f; int i; if (!ssi_ready) return nullfunction; /* Find the matching name in the table, return the function. */ for (i = 0; i < MAX_REGISTERED_SSIS; i++) { f = calls[i]; if (f && strncmp(f->name, name, strlen(f->name)) == 0) { httpd_d("Found function: %s", (f)->name); return f->function; } } httpd_d("Did not find function %s returning nullfunc", name); return nullfunction; }
static int httpd_select(int max_sock, const fd_set *readfds, fd_set *active_readfds, int timeout_secs) { int activefds_cnt; struct timeval_t timeout; fd_set local_readfds; if (timeout_secs >= 0) timeout.tv_sec = timeout_secs; timeout.tv_usec = 0; memcpy(&local_readfds, readfds, sizeof(fd_set)); httpd_d("WAITING for activity"); activefds_cnt = select(1, &local_readfds, NULL, NULL, timeout_secs >= 0 ? &timeout : NULL); if (activefds_cnt < 0) { httpd_d("Select failed: %d", timeout_secs); httpd_suspend_thread(true); } if (httpd_stop_req) { httpd_d("HTTPD stop request received"); httpd_stop_req = FALSE; httpd_suspend_thread(false); } if (activefds_cnt) { /* Update users copy of fd_set only if he wants */ if (active_readfds) memcpy(active_readfds, &local_readfds, sizeof(fd_set)); return activefds_cnt; } httpd_d("TIMEOUT"); return HTTPD_TIMEOUT_EVENT; }
static int httpd_setup_new_socket(int port) { int one = 1; int status, sockfd; struct sockaddr_t addr_listen; /* create listening TCP socket */ sockfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); if (sockfd < 0) { status = net_get_sock_error(sockfd); httpd_d("Socket creation failed: Port: %d Status: %d", port, status); return status; } setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, (char *)&one, sizeof(one)); addr_listen.s_ip = INADDR_ANY; addr_listen.s_port = port; /* bind insocket */ status = bind(sockfd, &addr_listen, sizeof(addr_listen)); if (status < 0) { status = net_get_sock_error(sockfd); httpd_d("Failed to bind socket on port: %d Status: %d", status, port); return status; } status = listen(sockfd, HTTPD_MAX_BACKLOG_CONN); if (status < 0) { status = net_get_sock_error(sockfd); httpd_d("Failed to listen on port %d: %d.", port, status); return status; } httpd_d("Listening on port %d.", port); return sockfd; }
static int httpd_close_sockets() { int ret, status = kNoErr; if (http_sockfd != -1) { ret = close(http_sockfd); if (ret != 0) { httpd_d("failed to close http socket: %d", net_get_sock_error(http_sockfd)); status = -kInProgressErr; } http_sockfd = -1; } if (client_sockfd != -1) { ret = close(client_sockfd); if (ret != 0) { httpd_d("Failed to close client socket: %d", net_get_sock_error(client_sockfd)); status = -kInProgressErr; } client_sockfd = -1; } return status; }
/* One-by-one read lines terminated by CR-LF from the socket, and * parse the headers contained in them. */ int httpd_parse_hdr_tags(httpd_request_t *req, int sock, char *buffer, int len) { int req_line_len; int err; uint8_t done = 0; while (TRUE) { req_line_len = htsys_getln_soc(sock, buffer, len); if (req_line_len == -kInProgressErr) { httpd_d("Could not read line from socket"); return -kInProgressErr; } err = __httpd_parse_hdr_tags(buffer, req_line_len, req, &done); if (err != kNoErr) return err; if (done == 1) { if (req->agent.product[0] != 0) { /* Populate the diagnostics statistics variable * with the user agent string. Do not do * anything if the agent string is NULL * (http-client). This is required since this * code is used by http server as well as client * and we do not want the user agent diagnostics * variable to change during the client code * path */ /* Note that, currently wm_hd_useragent is * populated only for file handling requests and * HTTP POST requests and not for HTTP GET. This * is because, in GET requests we directly purge * all the headers without reading them and * hence do not populate the useragent */ // memcpy(&g_wm_stats.wm_hd_useragent, &req->agent, // sizeof(g_wm_stats.wm_hd_useragent)); // g_wm_stats.wm_hd_time = wmtime_time_get_posix(); } return kNoErr; } } }
/* Unregister an SSI handler */ void httpd_unregister_ssi(struct httpd_ssi_call *ssi_call) { int i; if (!ssi_ready) return; for (i = 0; i < MAX_REGISTERED_SSIS; i++) { if (!calls[i]) { continue; } if (strncmp(calls[i]->name, ssi_call->name, strlen(calls[i]->name)) == 0) { calls[i] = NULL; httpd_d("Unregister ssi %s at %d", ssi_call->name, i); return; } } return; }
/* Parse the main header (GET /index.html HTTP/1.1) within the HTTP header and * update the httpd_request structure with the same. */ int httpd_parse_hdr_main(const char *data_p, httpd_request_t * req_p) { const char *ptr, *next; int err; #define TOKEN_LEN 10 char token[TOKEN_LEN]; /* First token is usually GET/POST */ err = httpd_verify_req_type(data_p, &req_p->type); if (err != kNoErr) { httpd_d("Unknown request type %s", data_p); httpd_purge_headers(req_p->sock); return err; } /* Second token: Filename */ ptr = httpd_next_token(data_p); next = ptr; err = httpd_token_cpy(ptr, req_p->filename, HTTPD_MAX_URI_LENGTH); if (err != kNoErr) { httpd_set_error("Error processing token: filename. " "Filename missing or too long?"); return -WM_E_HTTPD_HDR_FNAME; } /* Third token: HTTP version */ ptr = httpd_next_token(next); next = ptr; err = httpd_token_cpy(ptr, token, TOKEN_LEN); if (!strncmp(token, http_10, TOKEN_LEN)) { httpd_set_error("HTTP/1.0 clients are not supported"); return -WM_E_HTTPD_NOTSUPP; } return kNoErr; }
static int __httpd_parse_all_tags(char *inbuf, const char *tag, char *val, unsigned val_len) { short found = 0; char *ptr; ptr = inbuf; while (1) { ptr = httpd_parse_msgbody(ptr, tag, val, val_len, &found); if (ptr < 0) { httpd_d("Failed to parse request"); return -kInProgressErr; } if (found) return kNoErr; if (ptr == NULL) break; } return -kInProgressErr; }
/* Parse a tag value pair. tag1=value1&tag2=value2 */ static char *httpd_parse_msgbody(char *data_p, const char *tag, char *val, unsigned val_len, short *found) { int i, c1, c2; char *ptr; const char *tag_p; *found = 0; if (data_p == NULL || *data_p == '\0') return NULL; ptr = data_p; tag_p = tag; while (1) { if (*ptr == 0) { httpd_d("End of string reached"); return NULL; } if (*ptr == '=') { /* End of tag */ if (*tag_p != 0) goto next_token; else break; } /* perform url decode */ if (*ptr == '%') { ptr++; c1 = char2nibble(*ptr++); c2 = char2nibble(*ptr++); if (c1 == -1 || c2 == -1) { httpd_d("Invalid URL-encoded" " string. Ignoring."); return NULL; } if (*tag_p != ((c1 << 4) | c2)) goto next_token; tag_p++; } else if (*ptr == '+') { if (*tag_p != ' ') goto next_token; tag_p++; ptr++; } else { if (*tag_p != *ptr) goto next_token; ptr++; tag_p++; } } /* We come here only if tag was found */ *found = 1; ptr = strchr(ptr, '='); /* skip to next token */ if (ptr == NULL) { httpd_d("No = after tag"); return NULL; } ptr++; for (i = 0; i < val_len; i++) { if ((*ptr == '&') || (*ptr == 0)) { val[i] = 0; break; } /* perform url decode */ if (*ptr == '%') { ptr++; c1 = char2nibble(*ptr++); c2 = char2nibble(*ptr++); if (c1 == -1 || c2 == -1) { httpd_d("Invalid URL-encoded" " string. Ignoring."); return NULL; } val[i] = (c1 << 4) | c2; } else if (*ptr == '+') { val[i] = ' '; ptr++; } else { val[i] = *ptr++; } } if (i == val_len) { val[val_len] = 0; if ((*ptr != '&') && (*ptr != 0)) httpd_d("Max val length exceeded. " "Truncating value "); } next_token: ptr = strchr(ptr, '&'); /* skip to next token */ if (ptr == NULL) { return NULL; } return ptr + 1; }
static void httpd_handle_client_connection(const fd_set *active_readfds) { int activefds_cnt, status; fd_set readfds; if (httpd_stop_req) { httpd_d("HTTPD stop request received"); httpd_stop_req = FALSE; httpd_suspend_thread(false); } status = httpd_accept_client_socket(active_readfds); if (status != kNoErr) return; httpd_d("Client socket accepted: %d", client_sockfd); FD_ZERO(&readfds); FD_SET(client_sockfd, &readfds); while (1) { if (httpd_stop_req) { httpd_d("HTTPD stop request received"); httpd_stop_req = FALSE; httpd_suspend_thread(false); } httpd_d("Waiting on client socket"); activefds_cnt = httpd_select(client_sockfd, &readfds, NULL, HTTPD_CLIENT_SOCK_TIMEOUT); if (httpd_stop_req) { httpd_d("HTTPD stop request received"); httpd_stop_req = FALSE; httpd_suspend_thread(false); } if (activefds_cnt == HTTPD_TIMEOUT_EVENT) { /* Timeout has occured */ httpd_d("Client socket timeout occurred. " "Force closing socket"); status = close(client_sockfd); if (status != kNoErr) { status = net_get_sock_error(client_sockfd); httpd_d("Failed to close socket %d", status); httpd_suspend_thread(true); } client_sockfd = -1; break; } httpd_d("Handling %d", client_sockfd); /* Note: * Connection will be handled with call to * httpd_handle_message twice, first for * handling request (kNoErr) and second * time as there is no more data to receive * (client closed connection) and hence * will return with status HTTPD_DONE * closing socket. */ /* FIXME: remove this memset if all is working well */ /* memset(&httpd_message_in[0], 0, sizeof(httpd_message_in)); */ status = httpd_handle_message(client_sockfd); if (status == kNoErr) { /* The handlers are expected more data on the socket */ continue; } /* Either there was some error or everything went well */ httpd_d("Close socket %d. %s: %d", client_sockfd, status == HTTPD_DONE ? "Handler done" : "Handler failed", status); status = close(client_sockfd); if (status != kNoErr) { status = net_get_sock_error(client_sockfd); httpd_d("Failed to close socket %d", status); httpd_suspend_thread(true); } client_sockfd = -1; break; } }
static int httpd_accept_client_socket(const fd_set *active_readfds) { int main_sockfd = -1; struct sockaddr_t addr_from; int addr_from_len; if (FD_ISSET(http_sockfd, active_readfds)) { main_sockfd = http_sockfd; https_active = FALSE; } addr_from_len = sizeof(addr_from); client_sockfd = accept(main_sockfd, &addr_from, &addr_from_len); if (client_sockfd < 0) { httpd_d("net_accept client socket failed %d.", client_sockfd); return -kInProgressErr; } /* * Enable TCP Keep-alive for accepted client connection * -- By enabling this feature TCP sends probe packet if there is * inactivity over connection for specfied interval * -- If there is no response to probe packet for specified retries * then connection is closed with RST packet to peer end * -- Ref: http://tldp.org/HOWTO/html_single/TCP-Keepalive-HOWTO/ * * We are doing this as we have single threaded web server with * synchronous (blocking) API usage like send, recv and they might get * blocked due to un-availability of peer end, causing web server to * be in-responsive forever. */ int optval = true; if (setsockopt(client_sockfd, SOL_SOCKET, 0x0008, &optval, sizeof(optval)) == -1) { httpd_d("Unsupported option SO_KEEPALIVE: %d", net_get_sock_error(client_sockfd)); } /* TCP Keep-alive idle/inactivity timeout is 10 seconds */ optval = 10; if (setsockopt(client_sockfd, IPPROTO_TCP, 0x03, &optval, sizeof(optval)) == -1) { httpd_d("Unsupported option TCP_KEEPIDLE: %d", net_get_sock_error(client_sockfd)); } /* TCP Keep-alive retry count is 5 */ optval = 5; if (setsockopt(client_sockfd, IPPROTO_TCP, 0x05, &optval, sizeof(optval)) == -1) { httpd_d("Unsupported option TCP_KEEPCNT: %d", net_get_sock_error(client_sockfd)); } /* TCP Keep-alive retry interval (in case no response for probe * packet) is 1 second. */ optval = 1; if (setsockopt(client_sockfd, IPPROTO_TCP, 0x04, &optval, sizeof(optval)) == -1) { httpd_d("Unsupported option TCP_KEEPINTVL: %d", net_get_sock_error(client_sockfd)); } httpd_d("connecting %d to %d.", client_sockfd, addr_from.s_port); return kNoErr; }
/* If a script file (shtml), script_file_p is found, send that file to * the client over the socket, conn. Process any SSI directives within * the file. */ static int handle_script(struct fs *fs, httpd_request_t *req, int conn, sys_file_t *script_file_p, char *data_p) { char *cmd, *args; int len, err; int script_cmd; httpd_ssifunction func; SYS_FILE_DECL; err = WM_SUCCESS; /* proccess each line of the script */ while ((len = htsys_getln(fs, script_file_p, data_p, HTTPD_MAX_MESSAGE + 1)) != 0) { if (len < 0) { httpd_d("getln failed\r\n"); err = -WM_FAIL; break; } /* Check if we should start executing a script. */ script_cmd = httpd_is_script_directive(data_p, len, &cmd, &args); if (script_cmd == SSI_INCLUDE_FILE) { /* Include another file and send to client */ if (cmd == NULL) continue; httpd_d("Opening %s from script", cmd); err = htsys_file_open(fs, cmd, sys_filep); if (err != WM_SUCCESS) { httpd_d("open failed\r\n"); break; } httpd_d("sending file %s (%d bytes).\r\n", cmd, htsys_get_file_size(fs, sys_filep)); err = httpd_send_file(fs, conn, sys_filep); if (err != WM_SUCCESS) { break; } } else if (script_cmd == SSI_INCLUDE_VIRTUAL) { /* find the function and run it */ /* it should always at least return a nullfunction */ if (cmd == NULL) continue; httpd_d("Invoking SSI %s from script", cmd); func = httpd_ssi(cmd); /* Pass args in their own buffer so we can use data_p as * scratch */ if (args == NULL) { err = func(req, NULL, conn, data_p); } else { strncpy(script_args, args, HTTPD_MAX_SSI_ARGS); script_args[HTTPD_MAX_SSI_ARGS] = 0; err = func(req, script_args, conn, data_p); } } else { /* send embedded html to client */ err = httpd_send_chunk_begin(conn, len); if (err != WM_SUCCESS) break; err = httpd_send(conn, data_p, len); if (err != WM_SUCCESS) break; err = httpd_send_crlf(conn); if (err != WM_SUCCESS) break; } } return err; }
int httpd_handle_file(httpd_request_t *req_p, struct fs *fs) { const char *anchor = "/"; char tmp[HTTPD_MAX_URI_LENGTH+1]; const char *encoding = NULL; char *msg_in, *ptr, *type = NULL; int err, msg_in_len = HTTPD_MAX_MESSAGE - 1, conn = req_p->sock; int ret; msg_in = os_mem_alloc(HTTPD_MAX_MESSAGE + 2); if (!msg_in) { httpd_e("Failed to allocate memory for file handling"); /* Check what needs to be returned */ return -WM_FAIL; } memset(msg_in, 0, HTTPD_MAX_MESSAGE + 2); SYS_FILE_DECL; if (!fs) { /* The filesystem wasn't mounted properly */ httpd_send_hdr_from_str(conn, http_header_404, CONTENT_TYPE_PLAIN, encoding, NO_CACHE_HEADERS, UNUSED_PARAM); httpd_send_default_headers(conn, req_p->wsgi->hdr_fields); httpd_send_crlf(conn); httpd_send(conn, FILE_NOT_FOUND, sizeof(FILE_NOT_FOUND)); ret = WM_SUCCESS; goto out; } struct ftfs_super *f_super = fs_to_ftfs_sb(fs); /* Ensure that the anchor is the first part of the filename */ ptr = strstr(req_p->filename, anchor); if (ptr == NULL || ptr != req_p->filename) { httpd_d("No anchor in filename\r\n"); ret = httpd_send_error(conn, HTTP_500); goto out; } /* Zap the anchor from the filename */ ptr = req_p->filename + strlen(anchor); err = 0; /* anchors are only across directory boundaries */ if (*ptr != '/') req_p->filename[err++] = '/'; while (*ptr && (*ptr != '?')) { req_p->filename[err] = *ptr; ptr++; err++; } req_p->filename[err] = '\0'; /* "/" implies a request for index.html */ if (strncmp(req_p->filename, "/", 2) == 0) { strncpy(req_p->filename, http_index_html, sizeof(http_index_html)); } /* Find if .gz version exists for file, if it is then serve that */ type = strrchr(req_p->filename, ISO_period); if (type) { strncpy(tmp, req_p->filename, sizeof(tmp)); strncat(tmp, ".gz", 3); httpd_d("Look for gz file if it exists: %s\r\n", tmp); if (htsys_file_open(fs, tmp, sys_filep) == WM_SUCCESS) { /* Gzipped version exists, serve that */ httpd_d("Serve gzipped file\r\n"); strncpy(req_p->filename, tmp, HTTPD_MAX_URI_LENGTH + 1); encoding = ".gz"; htsys_close(fs, sys_filep); } } ret = httpd_parse_hdr_tags(req_p, conn, msg_in, msg_in_len); if (ret < 0) { httpd_d("Parsing headers failed \r\n"); ret = httpd_send_error(conn, HTTP_500); goto out; } /* It is not a WSGI, check to see if the file exists */ if (htsys_file_open(fs, req_p->filename, sys_filep) == -WM_FAIL) { httpd_w("file not found: %s\r\n", req_p->filename); ret = httpd_send_hdr_from_str(conn, http_header_404, type, encoding, NO_CACHE_HEADERS, UNUSED_PARAM); httpd_send_default_headers(conn, req_p->wsgi->hdr_fields); httpd_send_crlf(conn); if (ret != WM_SUCCESS) goto out; ret = htsys_file_open(fs, http_404_html, sys_filep); if (ret == WM_SUCCESS) { ret = httpd_send_file(fs, conn, sys_filep); goto out; } else httpd_w("No local 404 file. Sending empty 404" " response.\r\n"); } else { /* Ok, the file exists, is it a script html or just html */ g_wm_stats.wm_hd_file++; if (req_p->if_none_match && (f_super->fs_crc32 == req_p->etag_val)) { /* We do not need the file handle now */ htsys_close(fs, sys_filep); /* Send Not Modified header */ /* Send header prologue */ ret = httpd_send(conn, http_header_304_prologue, strlen(http_header_304_prologue)); if (ret != WM_SUCCESS) goto out; httpd_send_header(conn, "Server", "Marvell-WM"); httpd_send_header(conn, "Connection", "Close"); /* Send ETag */ int len; const char *h = httpd_get_etag_hdr(req_p->etag_val, &len); ret = httpd_send(conn, h, len); if (ret != WM_SUCCESS) goto out; /* Close the header */ ret = httpd_send_crlf(conn); goto out; } else { ret = httpd_send_hdr_from_str(conn, http_header_200, type, encoding, SEND_CACHE_HEADERS, f_super->fs_crc32); httpd_send_default_headers(conn, req_p->wsgi->hdr_fields); httpd_send_crlf(conn); if (ret != WM_SUCCESS) goto out; ptr = strchr(req_p->filename, ISO_period); if (ptr != NULL && strncmp(ptr, http_shtml, sizeof(http_shtml) - 1) == 0) { httpd_d("Handling script: %s", req_p->filename); ret = handle_script(fs, req_p, conn, sys_filep, msg_in); htsys_close(fs, sys_filep); if (ret != WM_SUCCESS) { httpd_d("Script failed\r\n"); goto out; } } else { ret = httpd_send_file(fs, conn, sys_filep); if (ret != WM_SUCCESS) goto out; } } } ret = httpd_send_last_chunk(conn); out: os_mem_free(msg_in); return ret; }
int httpd_use_tls_certificates(const httpd_tls_certs_t *tls_certs) { httpd_d("HTTPS is not enabled in server. "); return -kInProgressErr; }
/* Handle an incoming message (request) from the client. This is the * main processing function of the HTTPD. */ int httpd_handle_message(int conn) { int err; int req_line_len; char msg_in[128]; /* clear out the httpd_req structure */ memset(&httpd_req, 0x00, sizeof(httpd_req)); httpd_req.sock = conn; /* Read the first line of the HTTP header */ req_line_len = htsys_getln_soc(conn, msg_in, sizeof(msg_in)); if (req_line_len == 0) return HTTPD_DONE; if (req_line_len < 0) { httpd_e("Could not read from socket"); return -WM_FAIL; } /* Parse the first line of the header */ err = httpd_parse_hdr_main(msg_in, &httpd_req); if (err == -WM_E_HTTPD_NOTSUPP) /* Send 505 HTTP Version not supported */ return httpd_send_error(conn, HTTP_505); else if (err != WM_SUCCESS) { /* Send 500 Internal Server Error */ return httpd_send_error(conn, HTTP_500); } /* set a generic error that can be overridden by the wsgi handling. */ httpd_d("Presetting error to: "); httpd_set_error("wsgi handler failed"); /* Web Services Gateway Interface branch point: * At this point we have the request type (httpd_req.type) and the path * (httpd_req.filename) and all the headers waiting to be read from * the socket. * * The call bellow will iterate through all the url patterns and * invoke the handlers that match the request type and pattern. If * request type and url patern match, invoke the handler. */ err = httpd_wsgi(&httpd_req); if (err == HTTPD_DONE) { httpd_d("Done processing request."); return WM_SUCCESS; } else if (err == -WM_E_HTTPD_NO_HANDLER) { httpd_w("No handler for the given URL %s was found", httpd_req.filename); /* * We have not yet read the complete data from the current * request, from the socket. We are in an error state and * we wish to cancel this HTTP transaction. We sent * appropriate message to the client and read (flush) out * all the pending data in the socket. We let the client * close the socket for us, if necessary. */ httpd_purge_socket_data(&httpd_req, msg_in, sizeof(msg_in), conn); httpd_set_error("File %s not_found", httpd_req.filename); httpd_send_error(conn, HTTP_404); return WM_SUCCESS; } else { httpd_e("WSGI handler failed."); /* Send 500 Internal Server Error */ return httpd_send_error(conn, HTTP_500); } }