static _mqx_int httpd_readch(HTTPD_SESSION_STRUCT *session, char *ch) { int res; HTTPD_ASSERT(session && ch); HTTPD_ASSERT(session->recv_used >= 0 && session->recv_used < HTTPDCFG_RECV_BUF_LEN); if (session->recv_used == 0) { HTTPD_DEBUG(5, "recv BEGIN\n"); res = recv(session->sock, session->recv_buf, HTTPDCFG_RECV_BUF_LEN, 0); HTTPD_DEBUG(5, "recv END\n"); if (res <= 0) { *ch = 0; return res; } session->recv_used = res; session->recv_buf[session->recv_used] = 0; session->recv_rd = session->recv_buf; } *ch = *session->recv_rd++; session->recv_used--; return 1; }
static void httpd_session_dynamic_task(pointer init_ptr, pointer creator) { HTTPD_SES_TASK_PARAM *param = (HTTPD_SES_TASK_PARAM*)init_ptr; HTTPD_SESSION_STRUCT *session; HTTPD_ASSERT(init_ptr); HTTPD_DEBUG(1, "session task start\n"); session = httpd_ses_alloc(param->server); if (session) { RTCS_task_resume_creator(creator, RTCS_OK); HTTPD_DEBUG(1, "session task run...\n"); httpd_ses_init(param->server, session, param->sock); while (HTTPD_SESSION_VALID == session->valid) { httpd_ses_process(param->server, session); } httpd_ses_free(session); } else { RTCS_task_resume_creator(creator, (uint_32) RTCS_ERROR); } HTTPD_FREE(init_ptr); _lwsem_post(&sem_session_counter); HTTPD_DEBUG(1, "session task stop\n"); }
static uint32 get_json_pointer(struct http_request *req) { uint32 i = 0; uint32 first_matched = 0; uint8 starter = 0; int32 checker_brace = 0; /* checker of '{' '}'*/ int32 checker_bracket = 0; /* checker of '[' ']' */ int32 checker_quote = 0; /* checked of '"' */ for (i = 0; i < req->sz; i++) { switch (req->buff[i]) { case '{': checker_brace++; if ((0 == first_matched)) { req->json_start = i; starter = '{'; first_matched = 1; } break; case '}': checker_brace--; if (starter == '{') req->json_end = i; break; case '[': checker_bracket++; if ((0 == first_matched)) { req->json_start = i; starter = '['; first_matched = 1; } break; case ']': checker_bracket--; if (starter == '[') req->json_end = i; break; case '"': checker_quote++; break; default: break; } } if (checker_brace || checker_bracket || (checker_quote%2 != 0) || (0 == req->json_start) || (0 == req->json_end) || (req->json_start > req->json_end)) { //printf("brace[%d] bracket[%d] quote[%d] content_length[%lld] hd_sz[%d] sz[%d] json_start[%d] json_end[%d]\n", //checker_brace, checker_bracket, checker_quote, req->content_length, req->hd_sz, req->sz, req->json_start, req->json_end); return 0; } HTTPD_DEBUG("content_length[%ld] hd_sz[%d] sz[%d] json_start[%d] json_end[%d]\n", req->content_length, req->hd_sz, req->sz, req->json_start, req->json_end); return (req->json_end-req->json_start + 1); }
/** Send data to socket. * \param s < socket * \param buf < pointer to data buffer * \param len < data length * \return length of sent data */ static _mqx_int httpd_send(int s, char *buf, int len) { int res; HTTPD_DEBUG(4, "httpd_send buf:%p len:%d", buf, len); #if RTCS_MINIMUM_FOOTPRINT // for better performance on small ram devices, split data to teo separate packets if (len > 2) { res = send(s, buf, len / 2, 0); if (res != RTCS_ERROR) res += send(s, buf + res, len - res, 0); } else #endif res = send(s, buf, len, 0); HTTPD_DEBUG(4, "result:%d\n", res); return res; }
_mqx_int httpd_readln(HTTPD_SESSION_STRUCT *session, char *buf, _mqx_int max_len) { int used = 0, ret; char *dst = buf; uint_32 err; HTTPD_ASSERT(session && dst); HTTPD_DEBUG(5, "readln session(%p) buf(%p) max_len(%d)\n", session, buf, max_len); while (used < max_len) { #if HTTPDCFG_POLL_MODE ret = httpd_readch(session, dst); #else ret = recv(session->sock, dst, 1, 0); #endif if (ret <= 0) { err = RTCS_geterror(session->sock); *buf = 0; HTTPD_DEBUG(5, "readln used(%d) ret(%d) err(0x%x)\n", used, ret, err); return ret; } dst++; used++; if (*(dst - 1) == '\n') { break; } } *dst = 0; HTTPD_DEBUG(5, "readln len(%d)\n", used); return used; }
void httpd_send404(HTTPD_STRUCT *server, HTTPD_SESSION_STRUCT *session) { HTTPD_DEBUG(3, "http_send404\n"); session->response.file = fopen(server->params->page404, "r"); session->response.len = 0; if (session->response.file) { httpd_sendfile(server, session); } else { httpd_sendhdr(session, 0, 0); httpd_sendstr(session->sock, "<HTML><HEAD><TITLE>404 Not Found</TITLE></HEAD>\n"); httpd_sendstr(session->sock, "<BODY><H1>The request URL was not found!</H1>\n"); httpd_sendstr(session->sock, "</BODY></HTML>\n"); } }
void httpd_send403(HTTPD_STRUCT *server, HTTPD_SESSION_STRUCT *session) { HTTPD_DEBUG(3, "http_send403\n"); session->response.file = fopen(server->params->page403, "r"); session->response.len = 0; if (session->response.file) { httpd_sendfile(server, session); } else { httpd_sendhdr(session, 0, 0); httpd_sendstr(session->sock, "<HTML><HEAD><TITLE>403 Forbidden</TITLE></HEAD>\n"); httpd_sendstr(session->sock, "<BODY><H1>Forbidden!</H1>\n"); httpd_sendstr(session->sock, "</BODY></HTML>\n"); } }
static int32 re_alloc_buff(struct http_request *req) { int8 *orig = req->buff; if (NULL != req->sec_websocket_key) req->sec_websocket_key_offset = (int32)(req->sec_websocket_key - req->buff); if (NULL != req->origin) req->origin_offset = (int32)(req->origin - req->buff); if (NULL != req->content_type) req->content_type_offset = (int32)(req->content_type - req->buff); if (NULL != req->host) req->host_offset = (int32)(req->host - req->buff); if (NULL != req->path) req->path_offset = (int32)(req->path - req->buff); if (NULL != req->query) req->query_offset = (int32)(req->query - req->buff); req->buff_size = req->content_length + MAX_HEADER_LEN + 1; req->buff = (int8 *)realloc(req->buff, req->content_length + MAX_HEADER_LEN + 1); if (NULL == req->buff) return -1; if (orig != req->buff) HTTPD_DEBUG("\nrealloc buffer size from %d to %ld\n", BUFFSIZ, req->content_length + MAX_HEADER_LEN); if (NULL != req->sec_websocket_key) req->sec_websocket_key = req->buff + req->sec_websocket_key_offset; if (NULL != req->origin) req->origin = req->buff + req->origin_offset; if (NULL != req->content_type) req->content_type = req->buff + req->content_type_offset; if (NULL != req->host) req->host = req->buff + req->host_offset; if (NULL != req->path) req->path = req->buff + req->path_offset; if (NULL != req->query) req->query = req->buff + req->query_offset; return 0; }
void httpd_sendhdr(HTTPD_SESSION_STRUCT *session, _mqx_int content_len, _mqx_int mod_time) { char tmpBuf[100]; HTTPD_ASSERT(session); if (session->response.hdrsent) return; sprintf(tmpBuf, "HTTP/1.0 %d %s\n", session->response.statuscode, get_tbl_string((tbl_str_itm_t*)reason_phrase, session->response.statuscode)); httpd_sendstr(session->sock, tmpBuf); httpd_sendstr(session->sock, session->response.header); if (session->keep_alive) { httpd_sendstr(session->sock, "Connection: Keep-Alive\n"); } else { httpd_sendstr(session->sock, "Connection: close\n"); } sprintf(tmpBuf, "Content-Type: %s\n", get_tbl_string((tbl_str_itm_t*)content_type, session->response.contenttype)); httpd_sendstr(session->sock, tmpBuf); //only non zero length cause to send Content-Length header field if (0 < content_len) { sprintf(tmpBuf, "Content-Length: %d\n", content_len); httpd_sendstr(session->sock, tmpBuf); } httpd_sendstr(session->sock, "\n"); session->response.hdrsent = 1; HTTPD_DEBUG(4, "httpd_sendhdr\n"); }
void httpd_sendfile(HTTPD_STRUCT *server, HTTPD_SESSION_STRUCT *session) { char *ext; int expand = 0; int len; char buf[HTTPDCFG_MAX_BYTES_TO_SEND + 1]; HTTPD_ASSERT(server && session); HTTPD_DEBUG(3, "httpd_sendfile(%p) %s\n", session, session->request.path); ext = strrchr(session->request.path, 46); if (ext) { if (0 == strcasecmp(ext, ".htm") || 0 == strcasecmp(ext, ".html")) { session->response.contenttype = CONTENT_TYPE_HTML; expand = 1; } else if (0 == strcasecmp(ext, ".gif")) session->response.contenttype = CONTENT_TYPE_GIF; else if (0 == strcasecmp(ext, ".jpg")) session->response.contenttype = CONTENT_TYPE_JPG; else if (0 == strcasecmp(ext, ".png")) session->response.contenttype = CONTENT_TYPE_PNG; else if (0 == strcasecmp(ext, ".css")) session->response.contenttype = CONTENT_TYPE_CSS; else if (0 == strcasecmp(ext, ".js")) session->response.contenttype = CONTENT_TYPE_JS; else session->response.contenttype = CONTENT_TYPE_OCTETSTREAM; } if (expand) { //if there will be any expansion there's no way how to calculate correct length //zero length prevents sending Content-Length header field httpd_sendhdr(session, 0, 0); } else { httpd_sendhdr(session, session->response.file->SIZE, 0); } if (expand) { session->response.data = buf; len = read(session->response.file, session->response.data, HTTPDCFG_MAX_BYTES_TO_SEND); if (len > 0) { buf[len] = 0; len = httpd_sendextstr(server, session, session->response.data); if (!len) { session->state = HTTPD_SES_END_REQ; } else { session->response.len += len; fseek(session->response.file, session->response.len, SEEK_SET); } } else session->state = HTTPD_SES_END_REQ; } else { session->response.data = buf; fseek(session->response.file, session->response.len, SEEK_SET); len = read(session->response.file, session->response.data, HTTPDCFG_MAX_BYTES_TO_SEND); HTTPD_DEBUG(4, "httpd_sendfile READ END %d\n", len); if (len > 0) { buf[len] = 0; len = httpd_send(session->sock, session->response.data, len); if (len < 0) { if (errno != EAGAIN) session->state = HTTPD_SES_END_REQ; } else { session->response.len += len; } } else session->state = HTTPD_SES_END_REQ; } HTTPD_DEBUG(4, "httpd_sendfile END %s \n", session->request.path); }
/** Send extended string to socket. * \param s < socket * \param str < pointer to string * \return length of sent data */ static _mqx_int httpd_sendextstr(HTTPD_STRUCT *server, HTTPD_SESSION_STRUCT *session, char *str) { char *src; int len, res; char fname[HTTPDCFG_MAX_SCRIPT_LN + 1]; HTTPD_ASSERT(server && session && str); fname[0] = 0; if (!session->response.data) { session->response.data = str; } src = session->response.data; if (session->response.script_token) { // script token found len = (int)strcspn(src, " ;%<>\r\n\t"); if (len > 1 && len < HTTPDCFG_MAX_SCRIPT_LN) { strncpy(fname, src, (unsigned long)len); fname[len] = 0; // call fn HTTPD_DEBUG(2, "script ln: %s\r\n", fname); httpd_call_fn(server, session, fname); } if (src[len] == '%' && src[len + 1] == '>') { session->response.script_token = 0; len += 1; } len++; session->response.data = src + len; } else { for (len = 0; *src && len < HTTPDCFG_MAX_BYTES_TO_SEND; src++, len++) { if (*src == '<' && *(src + 1) == '%') { session->response.script_token = 1; src += 2; break; } } res = httpd_send(session->sock, session->response.data, len); if (res < 0) { session->response.script_token = 0; res = errno; if (res != EAGAIN) len = 0; } else { if (len == res) { session->response.data = src; } else { session->response.script_token = 0; session->response.data += res; } } } return len; }
/** server task - httpd main task which create new task for each new request */ static void httpd_server_task(pointer init_ptr, pointer creator) { HTTPD_STRUCT *server = (HTTPD_STRUCT*)init_ptr; int s; unsigned short len; struct sockaddr_in sin; HTTPD_SES_TASK_PARAM *param; _mqx_uint res; uint_32 stack; HTTPD_DEBUG(1, "server task start\n"); res = _lwsem_create(&sem_session_counter, server->params->max_ses); if (!server && res != MQX_OK) goto ERROR; RTCS_task_resume_creator(creator, RTCS_OK); HTTPD_DEBUG(1, "server task run...\n"); while (server->run) { // limit maximal opened sessions _lwsem_wait(&sem_session_counter); // allocate session task parameter structure param = _mem_alloc_system(sizeof(HTTPD_SES_TASK_PARAM)); if (param) { param->server = server; param->sock = accept(server->sock, &sin, &len); if (0 < param->sock) { // accept return corect socket number - no error // try create task for session res = RTCS_task_create("httpd session", server->params->ses_prio, server->params->ses_stack, httpd_session_dynamic_task, param); if (MQX_OK != res) { // session task creation failed, clean and wait... shutdown(param->sock, FLAG_ABORT_CONNECTION); // abort opened connection _mem_free(param); _lwsem_post(&sem_session_counter); _time_delay(1); } } else { // accept return error, clean and wait, then try again... _mem_free(param); _lwsem_post(&sem_session_counter); _time_delay(1); } } else { // allocation failed ?!?!? wait some time _time_delay(100); } } HTTPD_DEBUG(1, "server task stop\n"); ERROR: RTCS_task_resume_creator(creator, (uint_32)RTCS_ERROR); }