WebNet_XiUOS/src/wn_module.c

582 lines
15 KiB
C

/*
* File : wn_module.c
* This file is part of RT-Thread RTOS
* COPYRIGHT (C) 2006 - 2018, RT-Thread Development Team
*
* This software is dual-licensed: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation. For the terms of this
* license, see <http://www.gnu.org/licenses/>.
*
* You are free to use this software under the terms of the GNU General
* Public License, but WITHOUT ANY WARRANTY; without even the implied
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*
* Alternatively for commercial application, you can contact us
* by email <business@rt-thread.com> for commercial license.
*
* Change Logs:
* Date Author Notes
* 2022-05-09 chunyexixiaoyu the version from rt-thread
*/
#include <transform.h>
#include <webnet.h>
#include <wn_module.h>
#include <wn_utils.h>
static int _webnet_module_system_init(struct webnet_session *session, int event)
{
#ifdef WEBNET_USING_LOG
webnet_module_log(session, event);
#endif
#ifdef WEBNET_USING_SSL
webnet_module_ssl(session, event);
#endif
#ifdef WEBNET_USING_CGI
webnet_module_cgi(session, event);
#endif
#ifdef WEBNET_USING_DMR
webnet_module_dmr(session, event);
#endif
return WEBNET_MODULE_CONTINUE;
}
static int _webnet_module_system_uri_physical(struct webnet_session *session, int event)
{
int result;
result = WEBNET_MODULE_CONTINUE;
#ifdef WEBNET_USING_LOG
webnet_module_log(session, event);
#endif
#ifdef WEBNET_USING_ALIAS
result = webnet_module_alias(session, event);
if (result == WEBNET_MODULE_FINISHED) return result;
#endif
#ifdef WEBNET_USING_AUTH
result = webnet_module_auth(session, event);
if (result == WEBNET_MODULE_FINISHED) return result;
#endif
#ifdef WEBNET_USING_CGI
result = webnet_module_cgi(session, event);
if (result == WEBNET_MODULE_FINISHED) return result;
#endif
#ifdef WEBNET_USING_DMR
result = webnet_module_dmr(session, event);
if (result == WEBNET_MODULE_FINISHED) return result;
#endif
#ifdef WEBNET_USING_UPLOAD
result = webnet_module_upload(session, event);
if (result == WEBNET_MODULE_FINISHED) return result;
#endif
return result;
}
static void _webnet_dofile_handle(struct webnet_session *session, int event)
{
int fd = session->user_data;
if (event & WEBNET_EVENT_WRITE)
{
unsigned long readbytes;
unsigned long length = ((WEBNET_SESSION_BUFSZ) & ~((4) - 1));
#ifdef WEBNET_USING_RANGE
if(session->request->Range)
{
length = session->request->pos_end - session->request->pos_start;
if(length == 0)
{
goto __exit;
}
if(length > WEBNET_SESSION_BUFSZ)
{
length = WEBNET_SESSION_BUFSZ;
}
lseek(fd, session->request->pos_start, SEEK_SET);
session->request->pos_start += length;
}
#endif
readbytes = read(fd, session->buffer, length);
if (readbytes <= 0) /* end of file */
goto __exit;
if (webnet_session_write(session, session->buffer, readbytes) == 0)
goto __exit;
return;
}
__exit:
close(fd);
session->user_data = 0;
session->session_event_mask = 0; /* clean event */
/* destroy session */
session->session_phase = WEB_PHASE_CLOSE;
return;
}
static const struct webnet_session_ops _dofile_ops =
{
_webnet_dofile_handle,
NULL
};
/* send a file to http client */
int webnet_module_system_dofile(struct webnet_session *session)
{
int fd = -1; /* file descriptor */
struct stat file_stat;
const char *mimetype;
unsigned long file_length;
struct webnet_request *request;
#if WEBNET_CACHE_LEVEL > 0
char ctime_str[32];
char gmtime_str[32];
struct tm* info;
int stat_result = -1;
#endif /* WEBNET_CACHE_LEVEL */
assert(session != NULL);
request = session->request;
assert(request != NULL);
#if WEBNET_CACHE_LEVEL > 0
#ifdef WEBNET_USING_GZIP
/* get .gz Last-Modified. */
if (request->support_gzip)
{
struct stat file_stat;
char *path_gz = wn_malloc(strlen(request->path) + 4); /* ".gz\0" */
if (path_gz != NULL)
{
sprintf(path_gz, "%s.gz", request->path);
stat_result = stat(request->path, &file_stat);
wn_free(path_gz);
}
if (stat_result == 0)
{
PrivTaskenterCritical();
strcpy(ctime_str, ctime((time_t *)&file_stat.st_mtime));
PrivTaskexitCritical();
ctime_str[strlen(ctime_str) - 1] = '\0'; /* clear the end \n */
if ((request->modified != NULL)
&& (strcmp(request->modified, ctime_str) == 0))
{
request->result_code = 304;
return WEBNET_MODULE_FINISHED;
}
}
}
/* .gz not exist, use raw. */
#endif /* WEBNET_USING_GZIP */
/* get Last-Modified. */
if (stat_result != 0)
{
struct stat file_stat;
stat_result = stat(request->path, &file_stat);
if (stat_result == 0)
{
PrivTaskenterCritical();
info = localtime((time_t *)&file_stat.st_mtime);
memset(gmtime_str,0,32);
strftime(gmtime_str,sizeof(ctime_str),"%a, %d %b %Y %H:%M:%S GMT",info);
strcpy(ctime_str, ctime((time_t *)&file_stat.st_mtime));
PrivTaskexitCritical();
ctime_str[strlen(ctime_str) - 1] = '\0'; /* clear the end \n */
gmtime_str[strlen(gmtime_str)] = '\0'; /* clear the end \n */
if ((request->modified != NULL)
&& ((strcmp(request->modified, ctime_str) == 0)||strcmp(request->modified, gmtime_str) == 0))
{
request->result_code = 304;
return WEBNET_MODULE_FINISHED;
}
}
}
#endif /* WEBNET_CACHE_LEVEL > 0 */
/* get mime type */
mimetype = mime_get_type(request->path);
#ifdef WEBNET_USING_GZIP
if (request->support_gzip)
{
char *path_gz = wn_malloc(strlen(request->path) + 4); /* ".gz\0" */
if (path_gz != NULL)
{
sprintf(path_gz, "%s.gz", request->path);
fd = open(path_gz, O_RDONLY, 0);
wn_free(path_gz);
if (fd < 0)
{
/* .gz not exist, use raw. */
request->support_gzip = FALSE;
}
}
}
/* .gz not exist, use raw. */
#endif /* WEBNET_USING_GZIP */
if (fd < 0 && stat(request->path, &file_stat) >= 0 && !S_ISDIR(file_stat.st_mode))
{
fd = open(request->path, O_RDONLY, 0);
}
if (fd < 0)
{
request->result_code = 404;
return WEBNET_MODULE_FINISHED;
}
/* get file size */
file_length = lseek(fd, 0, SEEK_END);
/* seek to beginning of file */
lseek(fd, 0, SEEK_SET);
/*************todo**********************/
#ifdef WEBNET_USING_RANGE
if (request->Range)
{
char *range_start, *range_end;
int32_t pos_start = 0;
uint32_t pos_end = file_length - 1;
range_start = strstr(request->Range, "bytes=");
if (range_start)
{
range_start += 6;
range_end = strstr(range_start, "-");
if (range_start == range_end)
pos_start = 0;
else
pos_start = atoi(range_start);
/* send file to remote */
if ((!range_end) || (strstr(range_start, ",")))
{
request->result_code = 400;
goto _error_exit;
}
if (range_end)
{
*range_end = '\0';
range_end += 1;
pos_end = atoi(range_end);
}
}
#ifdef WEBNET_USING_GZIP
if (request->support_gzip)
{
pos_start = 0; /* */
}
#endif /* WEBNET_USING_GZIP */
if ((pos_start >= file_length) || (pos_end >= file_length))
{
request->result_code = 416;
webnet_session_set_header_status_line(session, request->result_code, "Requested Range Not Satisfiable");
goto _error_exit;
}
if (lseek(fd, pos_start, SEEK_SET) != pos_start)
{
request->result_code = 500;
goto _error_exit;
}
if (pos_end == 0)
{
pos_end = file_length - 1;
}
file_length = pos_end - pos_start + 1;
request->result_code = 216;
request->pos_start = pos_start;
request->pos_end = pos_end;
webnet_session_set_header_status_line(session, request->result_code, "Partial Content");
webnet_session_printf(session, "Content-Range: %d-%d/%d\r\n", pos_start, pos_end, file_length);
}else
#endif /* WEBNET_USING_RANGE */
/*************todo**********************/
{
/* send file to remote */
request->result_code = 200;
webnet_session_set_header_status_line(session, request->result_code, "OK");
}
#if WEBNET_CACHE_LEVEL > 0
/* send Last-Modified. */
webnet_session_printf(session,
"Last-Modified: %s\r\n",
ctime_str);
#endif /* WEBNET_CACHE_LEVEL > 0 */
#if WEBNET_CACHE_LEVEL > 1
/* Cache-Control. */
webnet_session_printf(session,
"Cache-Control: max-age=%d\r\n",
WEBNET_CACHE_MAX_AGE);
#endif /* WEBNET_CACHE_LEVEL > 1 */
/* send Content-Type. */
webnet_session_printf(session,
"Content-Type: %s\r\n",
mimetype);
/* send Content-Length. */
webnet_session_printf(session,
"Content-Length: %ld\r\n",
file_length);
#ifdef WEBNET_USING_KEEPALIVE
if(session->request->connection == WEBNET_CONN_KEEPALIVE)
{
webnet_session_printf(session,
"Connection: %s\r\n",
"Keep-Alive");
}
else
{
webnet_session_printf(session,
"Connection: %s\r\n",
"close");
}
#else
webnet_session_printf(session,
"Connection: %s\r\n",
"close");
#endif
#ifdef WEBNET_USING_GZIP
if (request->support_gzip)
{
/* gzip deflate. */
webnet_session_printf(session, "Content-Encoding: gzip\r\n");
}
#endif /* WEBNET_USING_GZIP */
/* send Access-Control-Allow-Origin. */
webnet_session_printf(session, "Access-Control-Allow-Origin:*\r\n");
/* send http header end. */
webnet_session_printf(session, "\r\n");
if (file_length <= 0)
{
close(fd);
return WEBNET_MODULE_FINISHED;
}
/*
* set session write context
*/
if (request->method != WEBNET_HEADER)
{
/* set dofile session ops */
session->session_event_mask = WEBNET_EVENT_WRITE;
session->user_data = (uint32)fd;
session->session_ops = &_dofile_ops;
}
return WEBNET_MODULE_FINISHED;
_error_exit:
if (fd >= 0)
{
close(fd);
}
return WEBNET_MODULE_FINISHED;
}
static int _webnet_module_system_uri_post(struct webnet_session *session, int event)
{
int result;
result = WEBNET_MODULE_CONTINUE;
#ifdef WEBNET_USING_LOG
webnet_module_log(session, event);
#endif
#ifdef WEBNET_USING_LUA
result = webnet_module_lua(session, event);
if (result == WEBNET_MODULE_FINISHED) return result;
#endif
#ifdef WEBNET_USING_ASP
result = webnet_module_asp(session, event);
if (result == WEBNET_MODULE_FINISHED) return result;
#endif
#ifdef WEBNET_USING_SSI
result = webnet_module_ssi(session, event);
if (result == WEBNET_MODULE_FINISHED) return result;
#endif
#ifdef WEBNET_USING_DAV
result = webnet_module_dav(session, event);
if (result == WEBNET_MODULE_FINISHED) return result;
#endif
#ifdef WEBNET_USING_INDEX
result = webnet_module_dirindex(session, event);
if (result == WEBNET_MODULE_FINISHED) return result;
#endif
/* always module finished in dofile */
result = webnet_module_system_dofile(session);
if (result == WEBNET_MODULE_FINISHED) return result;
return WEBNET_MODULE_CONTINUE;
}
static int _webnet_module_system_response_header(struct webnet_session *session, int event)
{
int result;
result = WEBNET_MODULE_CONTINUE;
return result;
}
static int _webnet_module_system_response_file(struct webnet_session *session, int event)
{
int result;
result = WEBNET_MODULE_CONTINUE;
return result;
}
int webnet_module_handle_event(struct webnet_session *session, int event)
{
switch (event)
{
case WEBNET_EVENT_INIT:
return _webnet_module_system_init(session, event);
case WEBNET_EVENT_URI_PHYSICAL:
return _webnet_module_system_uri_physical(session, event);
case WEBNET_EVENT_URI_POST:
return _webnet_module_system_uri_post(session, event);
case WEBNET_EVENT_RSP_HEADER:
return _webnet_module_system_response_header(session, event);
case WEBNET_EVENT_RSP_FILE:
return _webnet_module_system_response_file(session, event);
default:
assert(0);
break;
}
return WEBNET_MODULE_CONTINUE;
}
/* default index file */
static const char *default_files[] =
{
"",
"/index.html",
"/index.htm",
NULL
};
/**
* handle uri
* there are two phases on uri handling:
* - map url to physical
* - url handling
*/
int webnet_module_handle_uri(struct webnet_session *session)
{
int result, fd;
char *full_path;
uint32 index;
struct webnet_request *request;
assert(session != NULL);
/* get request */
request = session->request;
assert(request != NULL);
/* map uri to physical */
result = webnet_module_handle_event(session, WEBNET_EVENT_URI_PHYSICAL);
if (result == WEBNET_MODULE_FINISHED) return result;
/* made a full physical path */
full_path = (char *) wn_malloc(WEBNET_PATH_MAX);
assert(full_path != NULL);
/* only GET or POST need try default page. */
if ((session->request->method != WEBNET_GET)
&& (session->request->method != WEBNET_POST))
{
index = sizeof(default_files) / sizeof(default_files[0]);
index -= 1;
goto _end_default_files;
}
index = 0;
while (default_files[index] != NULL)
{
struct stat file_stat;
/* made a full path */
snprintf(full_path, WEBNET_PATH_MAX, "%s/%s%s",
webnet_get_root(), request->path, default_files[index]);
/* normalize path */
str_normalize_path(full_path);
if (stat(full_path, &file_stat) >= 0 && !S_ISDIR(file_stat.st_mode))
{
break;
}
index ++;
}
_end_default_files:
/* no this file */
if (default_files[index] == NULL)
{
/* use old full path */
snprintf(full_path, WEBNET_PATH_MAX, "%s/%s", webnet_get_root(), request->path);
/* normalize path */
str_normalize_path(full_path);
}
/* mark path as full physical path */
wn_free(request->path);
request->path = full_path;
/* check uri valid */
if (!str_begin_with(request->path, webnet_get_root()))
{
/* not found */
request->result_code = 404;
return WEBNET_MODULE_FINISHED;
}
/* uri post handle */
return webnet_module_handle_event(session, WEBNET_EVENT_URI_POST);
}