source code first submission

This commit is contained in:
chunyexixiaoyu 2022-05-23 14:35:35 +08:00
parent e8fbedc7af
commit 3bb87b35fb
28 changed files with 6254 additions and 0 deletions

52
.gitignore vendored Normal file
View File

@ -0,0 +1,52 @@
# Prerequisites
*.d
# Object files
*.o
*.ko
*.obj
*.elf
# Linker output
*.ilk
*.map
*.exp
# Precompiled Headers
*.gch
*.pch
# Libraries
*.lib
*.a
*.la
*.lo
# Shared objects (inc. Windows DLLs)
*.dll
*.so
*.so.*
*.dylib
# Executables
*.exe
*.out
*.app
*.i*86
*.x86_64
*.hex
# Debug files
*.dSYM/
*.su
*.idb
*.pdb
# Kernel Module Compile Results
*.mod*
*.cmd
.tmp_versions/
modules.order
Module.symvers
Mkfile.old
dkms.conf

92
Kconfig Normal file
View File

@ -0,0 +1,92 @@
menuconfig APP_USING_WEBNET
bool "WebNet: A lightweight, customizable, embeddable Web Server for RT-Thread"
default n
if APP_USING_WEBNET
config PKG_WEBNET_PATH
string
default "/packages/iot/webnet"
config WEBNET_PORT
int "Server listen port"
default 80
range 0 65535
config WEBNET_CONN_MAX
int "Maximum number of server connections"
default 16
range 1 100
config WEBNET_ROOT
string "Server root directory"
default "/webnet"
menu "Select supported modules"
config WEBNET_USING_LOG
bool "LOG: Enable output log support"
default n
config WEBNET_USING_AUTH
bool "AUTH: Enable basic HTTP authentication support"
default n
config WEBNET_USING_CGI
bool "CGI: Enable Common Gateway Interface support"
default n
config WEBNET_USING_ASP
bool "ASP: Enable Active Server Pages support"
default n
config WEBNET_USING_SSI
bool "SSI: Enable Server Side Includes support"
default n
config WEBNET_USING_INDEX
bool "INDEX: Enable list all the file in the directory support"
default n
config WEBNET_USING_ALIAS
bool "ALIAS: Enable alias support"
default n
config WEBNET_USING_DAV
bool "DAV: Enable Web-based Distributed Authoring and Versioning support"
default n
config WEBNET_USING_UPLOAD
bool "UPLOAD: Enable upload file support"
default n
config WEBNET_USING_GZIP
bool "GZIP: Enable compressed file support by GZIP"
default n
config WEBNET_CACHE_LEVEL
int "CACHE: Configure cache level(0:disable 1:use Last-Modified 2:use Cache-Control)"
default 0
range 0 2
if WEBNET_CACHE_LEVEL = 2
config WEBNET_CACHE_MAX_AGE
int "Cache-Control time in seconds"
default 1800
endif
endmenu
config WEBNET_USING_SAMPLES
bool "Enable webnet samples"
default n
select WEBNET_USING_ASP
select WEBNET_USING_AUTH
select WEBNET_USING_CGI
select WEBNET_USING_INDEX
select WEBNET_USING_ALIAS
select WEBNET_USING_SSI
select WEBNET_USING_UPLOAD
endif

64
SConscript Normal file
View File

@ -0,0 +1,64 @@
#
# File : SConscript
# This file is part of RT-Thread RTOS/WebNet Server
# COPYRIGHT (C) 2011, Shanghai Real-Thread Technology Co., Ltd
#
# All rights reserved.
#
# Change Logs:
# Date Author Notes
# 2011-08-02 Bernard the first version
#
Import('RTT_ROOT')
from building import *
cwd = GetCurrentDir()
src = Split("""
src/webnet.c
src/wn_mimetype.c
src/wn_request.c
src/wn_session.c
src/wn_utils.c
src/wn_module.c
""")
if GetDepend(['WEBNET_USING_ASP']):
src += Glob('module/wn_module_asp.c')
if GetDepend(['WEBNET_USING_AUTH']):
src += Glob('module/wn_module_auth.c')
if GetDepend(['WEBNET_USING_CGI']):
src += Glob('module/wn_module_cgi.c')
if GetDepend(['WEBNET_USING_INDEX']):
src += Glob('module/wn_module_index.c')
if GetDepend(['WEBNET_USING_ALIAS']):
src += Glob('module/wn_module_alias.c')
if GetDepend(['WEBNET_USING_LOG']):
src += Glob('module/wn_module_log.c')
if GetDepend(['WEBNET_USING_UPLOAD']):
src += Glob('module/wn_module_upload.c')
if GetDepend(['WEBNET_USING_SSI']):
src += Glob('module/wn_module_ssi.c')
if GetDepend(['WEBNET_USING_DAV']):
src += Glob('module/wn_module_dav.c')
if GetDepend(['WEBNET_USING_SAMPLES']):
src += Glob('samples/wn_sample.c')
if GetDepend(['WEBNET_USING_SAMPLES']) and GetDepend(['WEBNET_USING_UPLOAD']):
src += Glob('samples/wn_sample_upload.c')
CPPPATH = [cwd + '/inc']
group = DefineGroup('WebNet', src, depend = ['APP_USING_WEBNET'], CPPPATH = CPPPATH)
Return('group')

101
inc/webnet.h Normal file
View File

@ -0,0 +1,101 @@
/*
* File : webnet.h
* 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.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Change Logs:
* Date Author Notes
* 2022-05-10 chunyexixiaoyu the version transplanted from rt-thread
*/
#ifndef __WEBNET_H__
#define __WEBNET_H__
#include <transform.h>
#ifdef __cplusplus
extern "C" {
#endif
#ifndef wn_malloc
#define wn_malloc malloc
#endif
#ifndef wn_free
#define wn_free free
#endif
#ifndef wn_realloc
#define wn_realloc realloc
#endif
#ifndef wn_strdup
#define wn_strdup strdup
#endif
#ifndef WEBNET_USING_RANGE
#define WEBNET_USING_RANGE
#endif
#ifndef WEBNET_USING_KEEPALIVE
#define WEBNET_USING_KEEPALIVE
#endif
#ifndef WEBNET_USING_COOKIE
#define WEBNET_USING_COOKIE
#endif
#define WEBNET_VERSION "2.0.3" /* webnet version string */
#define WEBNET_VERSION_NUM 0x20003 /* webnet version number */
#define WEBNET_THREAD_NAME "webnet" /* webnet thread name */
#define WEBNET_THREAD_STACKSIZE (4 * 1024) /* webnet thread stack size */
#define WEBNET_PRIORITY 20 /* webnet thread priority */
#define WEBNET_PATH_MAX 256 /* maxiaml path length in webnet */
#define WEBNET_SERVER "Server: webnet "WEBNET_VERSION"\r\n"
/* Pre-declaration */
struct webnet_session;
/* webnet query item definitions */
struct webnet_query_item
{
char* name;
char* value;
};
/* get mimetype according to URL */
const char* mime_get_type(const char* url);
/* set and get listen socket port */
void webnet_set_port(int port);
int webnet_get_port(void);
/* set and get root directory path */
void webnet_set_root(const char* webroot_path);
const char* webnet_get_root(void);
/* webnet initialize */
int webnet_init(void);
#ifdef __cplusplus
}
#endif
#endif /* __WEBNET_H__ */

107
inc/wn_module.h Normal file
View File

@ -0,0 +1,107 @@
/*
* File : wn_module.h
* 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.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Change Logs:
* Date Author Notes
* 2022-05-10 chunyexixiaoyu the version transplanted from rt-thread
*/
#ifndef __WN_MODULE_H__
#define __WN_MODULE_H__
#include <transform.h>
#include <wn_session.h>
#ifdef __cplusplus
extern "C" {
#endif
/* initialization event */
#define WEBNET_EVENT_INIT (1 << 0)
/* map uri request to physical url */
#define WEBNET_EVENT_URI_PHYSICAL (1 << 1)
/* uri request */
#define WEBNET_EVENT_URI_POST (1 << 2)
/* header of response */
#define WEBNET_EVENT_RSP_HEADER (1 << 3)
/* file content of response */
#define WEBNET_EVENT_RSP_FILE (1 << 4)
/* continue other modules */
#define WEBNET_MODULE_CONTINUE 0
/* this session is finished */
#define WEBNET_MODULE_FINISHED 1
int webnet_module_handle_event(struct webnet_session* session, int event);
int webnet_module_system_dofile(struct webnet_session* session);
int webnet_module_handle_uri(struct webnet_session* session);
/* module function pre-declaration */
int webnet_module_alias(struct webnet_session* sesion, int event);
int webnet_module_auth(struct webnet_session* session, int event);
int webnet_module_asp(struct webnet_session* session, int event);
int webnet_module_cgi(struct webnet_session* session, int event);
int webnet_module_dirindex(struct webnet_session* session, int event);
int webnet_module_log(struct webnet_session* session, int event);
int webnet_module_lua(struct webnet_session* sesion, int event);
int webnet_module_ssl(struct webnet_session* sesion, int event);
int webnet_module_ssi(struct webnet_session* session, int event);
int webnet_module_dav(struct webnet_session* session, int event);
int webnet_module_dmr(struct webnet_session* session, int event);
/* add ASP variable */
void webnet_asp_add_var(const char* name, void (*handler)(struct webnet_session* session));
/* register CGI event */
void webnet_cgi_register(const char* name, void (*handler)(struct webnet_session* session));
void webnet_cgi_set_root(const char* root);
/* set basic authentication configure */
void webnet_auth_set(const char* path, const char* username_password);
/* set directory alias */
void webnet_alias_set(char* old_path, char* new_path);
/* upload module */
struct webnet_module_upload_entry
{
const char* url;
int (*upload_open) (struct webnet_session* session);
int (*upload_close)(struct webnet_session* session);
int (*upload_write)(struct webnet_session* session, const void* data, unsigned long length);
int (*upload_done) (struct webnet_session* session);
};
int webnet_module_upload(struct webnet_session* session, int event);
void webnet_upload_add(const struct webnet_module_upload_entry* entry);
const char* webnet_upload_get_filename(struct webnet_session* session);
const char* webnet_upload_get_content_type(struct webnet_session* session);
const char* webnet_upload_get_nameentry(struct webnet_session* session, const char* name);
const void* webnet_upload_get_userdata(struct webnet_session* session);
int webnet_upload_file_open(struct webnet_session* session);
int webnet_upload_file_close(struct webnet_session* session);
int webnet_upload_file_write(struct webnet_session* session, const void* data, unsigned long length);
#ifdef __cplusplus
}
#endif
#endif /* __WN_MODULE_H__ */

133
inc/wn_request.h Normal file
View File

@ -0,0 +1,133 @@
/*
* File : wn_request.h
* 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-10 chunyexixiaoyu the version the version transplanted from rt-thread
*/
#ifndef __WN_REQUEST_H__
#define __WN_REQUEST_H__
#include <transform.h>
#include <rtthread.h>
#include <wn_session.h>
#ifdef __cplusplus
extern "C" {
#endif
/* http request method */
enum webnet_method
{
WEBNET_UNKNOWN = 0,
WEBNET_GET,
WEBNET_POST,
WEBNET_HEADER,
WEBNET_HEAD,
WEBNET_PUT,
WEBNET_OPTIONS,
WEBNET_PROPFIND,
WEBNET_PROPPATCH,
WEBNET_DELETE,
WEBNET_CONNECT,
WEBNET_MKCOL,
WEBNET_MOVE,
WEBNET_SUBSCRIBE,
WEBNET_UNSUBSCRIBE,
WEBNET_NOTIFY,
};
/* http connection status */
enum webnet_connection
{
WEBNET_CONN_CLOSE,
WEBNET_CONN_KEEPALIVE,
};
/* http request structure */
struct webnet_request
{
enum webnet_method method;
int result_code;
int content_length;
/* the corresponding session */
struct webnet_session *session;
/* path and authorization */
char* path;
char* host;
char* authorization;
#if WEBNET_CACHE_LEVEL > 0
char* modified;
#endif /* WEBNET_CACHE_LEVEL */
#ifdef WEBNET_USING_GZIP
bool support_gzip;
#endif /* WEBNET_USING_GZIP */
char* user_agent;
char* accept_language;
char* cookie;
char* referer;
#ifdef WEBNET_USING_RANGE
char *Range;
size_t pos_start;
size_t pos_end;
#endif /* WEBNET_USING_RANGE */
#ifdef WEBNET_USING_DAV
char* depth;
char* destination;
#endif /* WEBNET_USING_DAV */
/* DMR */
char *soap_action;
char *callback;
char *sid;
/* Content-Type */
char* content_type;
/* query information */
char* query;
int query_offset;
struct webnet_query_item* query_items;
uint16 query_counter;
enum webnet_connection connection;
/* whether the string filed is copied */
bool field_copied;
};
struct webnet_request* webnet_request_create(void);
void webnet_request_destory(struct webnet_request* request);
int webnet_request_parse_method(struct webnet_request *request, char* buffer, int length);
int webnet_request_parse_header(struct webnet_request *request, char* buffer, int length);
int webnet_request_parse_post(struct webnet_request* request, char* buffer, int length);
void webnet_request_parse(struct webnet_request* request, char* buffer, int length);
bool webnet_request_has_query(struct webnet_request* request, char* name);
const char* webnet_request_get_query(struct webnet_request* request, char* name);
#ifdef __cplusplus
}
#endif
#endif /* __WN_REQUEST_H__ */

105
inc/wn_session.h Normal file
View File

@ -0,0 +1,105 @@
/*
* File : wn_session.h
* 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-10 chunyexixiaoyu the version transplanted from rt-thread
*/
#ifndef __WN_SESSION_H__
#define __WN_SESSION_H__
#include <transform.h>
#include <sys/select.h>
#include <wn_request.h>
#ifdef __cplusplus
extern "C" {
#endif
#define WEBNET_SESSION_BUFSZ (4 * 1024)
/* close session */
#define WEBNET_EVENT_CLOSE (1 << 5)
/* read session */
#define WEBNET_EVENT_READ (1 << 6)
/* write session */
#define WEBNET_EVENT_WRITE (1 << 7)
struct webnet_session_ops
{
void (*session_handle)(struct webnet_session* session, int event);
void (*session_close) (struct webnet_session* session);
};
enum webnet_session_phase
{
WEB_PHASE_METHOD = 0, /* parse method */
WEB_PHASE_HEADER, /* handle web request header */
WEB_PHASE_QUERY, /* handle web query */
WEB_PHASE_RESPONSE, /* handle web response */
WEB_PHASE_CLOSE, /* to close session */
};
struct webnet_session
{
struct webnet_session *next;
/* socket information */
int socket;
struct sockaddr_in cliaddr;
/* webnet request */
struct webnet_request* request;
/* session buffer */
uint16 buffer_length;
uint16 buffer_offset;
uint8 buffer[WEBNET_SESSION_BUFSZ];
/* session phase */
uint32 session_phase;
uint32 session_event_mask;
const struct webnet_session_ops* session_ops;
uint32 user_data;
};
struct webnet_session* webnet_session_create(int listenfd);
int webnet_session_read(struct webnet_session *session, char *buffer, int length);
void webnet_session_close(struct webnet_session *session);
void webnet_session_printf(struct webnet_session *session, const char* fmt, ...);
int webnet_session_write(struct webnet_session *session, const uint8* data, uint32 size);
int webnet_session_redirect(struct webnet_session *session, const char* url);
int webnet_session_get_physical_path(struct webnet_session *session, const char* virtual_path, char* full_path);
void webnet_session_set_header(struct webnet_session *session, const char* mimetype, int code, const char* status, int length);
void webnet_session_set_header_status_line(struct webnet_session *session, int code, const char * reason_phrase);
int webnet_sessions_set_fds(fd_set *readset, fd_set *writeset);
void webnet_sessions_handle_fds(fd_set *readset, fd_set *writeset);
void webnet_sessions_set_err_callback(void (*callback)(struct webnet_session *session));
#ifdef __cplusplus
}
#endif
#endif /* __WN_SESSION_H__ */

47
inc/wn_utils.h Normal file
View File

@ -0,0 +1,47 @@
/*
* File : wn_utils.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-10 chunyexixiaoyu the version transplanted from rt-thread
*/
#ifndef __WN_UTILS_H__
#define __WN_UTILS_H__
#include <transform.h>
#include <string.h>
#ifdef __cplusplus
extern "C" {
#endif
int str_begin_with(const char *s, const char *t);
int str_end_with(const char* s, const char* t);
int str_path_with(const char *s, const char *t);
char *str_decode_path(char *path);
char *str_base64_encode(const char* src);
char* str_normalize_path(char* fullpath);
char * urlencode(const char *str, int len, int *new_length);
int urldecode(char *str, int len);
#ifdef __cplusplus
}
#endif
#endif /* __WN_UTILS_H__ */

98
module/wn_module_alias.c Normal file
View File

@ -0,0 +1,98 @@
/*
* File : wn_module_alias.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 transplanted from rt-thread
*/
#include <webnet.h>
#include <wn_module.h>
#include <wn_utils.h>
#ifdef WEBNET_USING_ALIAS
struct webnet_alias_item
{
char* old_path;
char* new_path;
};
static struct webnet_alias_item *_alias_items = NULL;
static uint32 _alias_item_count = 0;
void webnet_alias_set(char* old_path, char* new_path)
{
if (_alias_items == NULL)
{
_alias_item_count = 1;
_alias_items = (struct webnet_alias_item*)wn_malloc (sizeof(struct webnet_alias_item) *
_alias_item_count);
}
else
{
_alias_item_count += 1;
_alias_items = (struct webnet_alias_item*) wn_realloc (_alias_items, sizeof(struct webnet_alias_item) *
_alias_item_count);
}
assert(_alias_items != NULL);
_alias_items[_alias_item_count - 1].old_path = wn_strdup(old_path);
assert(_alias_items[_alias_item_count - 1].old_path != NULL);
_alias_items[_alias_item_count - 1].new_path = wn_strdup(new_path);
assert(_alias_items[_alias_item_count - 1].new_path != NULL);
}
int webnet_module_alias(struct webnet_session* session, int event)
{
if (event == WEBNET_EVENT_URI_PHYSICAL)
{
int index;
struct webnet_request* request;
assert(session != NULL);
request = session->request;
assert(request != NULL);
/* check whether the uri is a alias */
for (index = 0; index < _alias_item_count; index ++)
{
if (str_path_with(request->path, _alias_items[index].old_path))
{
char* map_path;
map_path = (char*) wn_malloc (WEBNET_PATH_MAX);
assert(map_path != NULL);
snprintf(map_path, WEBNET_PATH_MAX, "%s/%s",
_alias_items[index].new_path,
request->path + strlen(_alias_items[index].old_path));
/* set new path */
wn_free(request->path);
request->path = map_path;
return WEBNET_MODULE_CONTINUE;
}
}
}
return WEBNET_MODULE_CONTINUE;
}
#endif /* WEBNET_USING_ALIAS */

268
module/wn_module_asp.c Normal file
View File

@ -0,0 +1,268 @@
/*
* File : wn_module_asp.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 transplanted from rt-thread
*/
#include<transform.h>
#include <string.h>
#include <webnet.h>
#include <wn_module.h>
#include <wn_utils.h>
#ifdef WEBNET_USING_ASP
struct webnet_asp_variable
{
char* name;
void (*handler)(struct webnet_session* session);
};
static struct webnet_asp_variable* _webnet_asp_vars = NULL;
int32 _webnet_asp_vars_count = 0;
void webnet_asp_add_var(const char* name, void (*handler)(struct webnet_session* session))
{
if (_webnet_asp_vars == NULL)
{
_webnet_asp_vars_count = 1;
_webnet_asp_vars = (struct webnet_asp_variable*)wn_malloc (sizeof(struct webnet_asp_variable) *
_webnet_asp_vars_count);
}
else
{
_webnet_asp_vars_count += 1;
_webnet_asp_vars = (struct webnet_asp_variable*) wn_realloc (_webnet_asp_vars, sizeof(struct webnet_asp_variable) *
_webnet_asp_vars_count);
}
assert(_webnet_asp_vars != NULL);
_webnet_asp_vars[_webnet_asp_vars_count - 1].name = wn_strdup(name);
_webnet_asp_vars[_webnet_asp_vars_count - 1].handler = handler;
}
static void _default_asp_handler(struct webnet_session* session, const char* name)
{
if (strncmp(name, "VERION", 6) == 0)
{
webnet_session_printf(session, "WebNet 1.0.0");
}
else if (strncmp(name, "REMOTE_ADDR", 11) == 0)
{
webnet_session_printf(session, "%s", inet_ntoa(session->cliaddr.sin_addr));
}
else if (strncmp(name, "REMOTE_PORT", 11) == 0)
{
webnet_session_printf(session, "%d", ntohs(session->cliaddr.sin_port));
}
else if (strncmp(name, "SERVER_PORT", 11) == 0)
{
webnet_session_printf(session, "%d", WEBNET_PORT);
}
else if (strncmp(name, "DOCUMENT_ROOT", 13) == 0)
{
webnet_session_printf(session, "%s", webnet_get_root());
}
else if (strncmp(name, "SERVER", 6) == 0)
{
webnet_session_printf(session, "RT-Thread/WebNet");
}
else if (strncmp(name, "HOST", 4) == 0)
{
webnet_session_printf(session, "WebNet");
}
else if (strncmp(name, "DATE", 4) == 0)
{
webnet_session_printf(session, "2011/08/01");
}
else if (strncmp(name, "USER_AGENT", 10) == 0)
{
webnet_session_printf(session, "%s", session->request->user_agent);
}
else if (strncmp(name, "COOKIE", 6) == 0)
{
webnet_session_printf(session, "%s", session->request->cookie);
}
else if (strncmp(name, "MEMUSAGE", 8) == 0)
{
int32 total, used, max_used;
total = 1024*32;
used = 1024*16;
max_used = 1024*24;
webnet_session_printf(session, "current %d/maximal used %d/total %d", used, max_used, total);
}
else if (strncmp(name, "TICK", 4) == 0)
{
int32 tick;
int32 hour, min, second;
#ifdef ADD_RTTHREAD_FETURES
tick = rt_tick_get()/RT_TICK_PER_SECOND;
#endif
second = tick % 60;
min = (tick/60) % 60;
hour = tick / (60 * 60);
webnet_session_printf(session, "%02d:%02d:%02d", hour, min, second);
}
}
static void _webnet_asp_dofile(struct webnet_session* session, int fd)
{
char *asp_begin, *asp_end;
char *offset, *end;
char *buffer;
int32 length, index;
/* get file length */
length = lseek(fd, 0, SEEK_END);
lseek(fd, 0, SEEK_SET);
/* allocate read buffer */
buffer = (char*) wn_malloc (length);
if (buffer == NULL)
{
session->request->result_code = 500;
return;
}
/* write page header */
webnet_session_set_header(session, "text/html", 200, "OK", -1);
/* read file to buffer */
if (read(fd, buffer, (uint32)length) != length) /* read failed */
{
wn_free(buffer);
session->request->result_code = 500;
return;
}
offset = buffer;
end = buffer + length;
while (offset < end)
{
/* get beginning of asp variable */
asp_begin = strstr(offset, "<%");
if (asp_begin == NULL)
{
/* write content directly */
webnet_session_write(session, (const uint8*)offset, end - offset);
break;
}
/* get end of aps variable */
asp_end = strstr(asp_begin, "%>");
if (asp_end == NULL)
{
/* write content directly */
webnet_session_write(session, (const uint8*)offset, end - offset);
break;
}
else
{
/* write content */
webnet_session_write(session, (const uint8*)offset, asp_begin - offset);
offset = asp_begin + 2;
while ((*offset == ' ') || (*offset == '\t')) offset ++;
/* extrace asp variable */
for (index = 0; index < _webnet_asp_vars_count; index ++)
{
if (str_begin_with(offset, _webnet_asp_vars[index].name))
{
/* found asp variable */
_webnet_asp_vars[index].handler(session);
break;
}
}
if (index == _webnet_asp_vars_count)
{
/* use default handler */
_default_asp_handler(session, offset);
}
/* move to the end of asp varaialbe */
offset = asp_end + 2;
}
}
/* release read buffer */
wn_free(buffer);
}
int webnet_module_asp(struct webnet_session* session, int event)
{
struct webnet_request* request;
assert(session != NULL);
request = session->request;
assert(request != NULL);
if (event == WEBNET_EVENT_URI_POST)
{
int fd;
/* check whether a asp file */
if ((strstr(request->path, ".asp") != NULL) ||
(strstr(request->path, ".ASP") != NULL))
{
/* try to open this file */
fd = open(request->path, O_RDONLY, 0);
if ( fd >= 0)
{
_webnet_asp_dofile(session, fd);
close(fd);
return WEBNET_MODULE_FINISHED;
}
else
{
/* no this file */
request->result_code = 404;
return WEBNET_MODULE_FINISHED;
}
}
else
{
/* try index.asp */
char *asp_filename;
asp_filename = (char*) wn_malloc (WEBNET_PATH_MAX);
if (asp_filename != NULL)
{
snprintf(asp_filename, WEBNET_PATH_MAX, "%s/index.asp", request->path);
fd = open(asp_filename, O_RDONLY, 0);
if (fd >= 0)
{
wn_free(asp_filename);
_webnet_asp_dofile(session, fd);
close(fd);
return WEBNET_MODULE_FINISHED;
}
}
wn_free(asp_filename);
}
}
return WEBNET_MODULE_CONTINUE;
}
#endif /* WEBNET_USING_ASP */

131
module/wn_module_auth.c Normal file
View File

@ -0,0 +1,131 @@
/*
* File : wn_module_auth.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 transplanted from rt-thread
*/
#include <string.h>
#include <webnet.h>
#include <wn_module.h>
#include <wn_utils.h>
#ifdef WEBNET_USING_AUTH
struct webnet_auth_item
{
char *path;
/* username and password, which will encode as base64 as - username:password*/
char *username_password;
};
static struct webnet_auth_item* _auth_items = NULL;
static uint32 _auth_items_count = 0;
/**
* set the authorization on the path
*
* @param path the path to be authorized
* @param username_password the user and password, which format shall be
* username:password
*/
void webnet_auth_set(const char* path, const char* username_password)
{
if (_auth_items == NULL)
{
_auth_items_count = 1;
_auth_items = (struct webnet_auth_item*)wn_malloc (sizeof(struct webnet_auth_item) *
_auth_items_count);
}
else
{
unsigned long index;
/* check whether modify a password */
for (index = 0; index < _auth_items_count; index ++)
{
if (strcmp(path, _auth_items[index].path) == 0)
{
wn_free(_auth_items[index].username_password);
_auth_items[index].username_password = str_base64_encode(username_password);
return;
}
}
_auth_items_count += 1;
_auth_items = (struct webnet_auth_item*) wn_realloc (_auth_items, sizeof(struct webnet_auth_item) *
_auth_items_count);
}
assert(_auth_items != NULL);
_auth_items[_auth_items_count - 1].path = wn_strdup(path);
_auth_items[_auth_items_count - 1].username_password = str_base64_encode(username_password);
}
RTM_EXPORT(webnet_auth_set);
/**
* Authorization module handler
*/
int webnet_module_auth(struct webnet_session* session, int event)
{
if (event == WEBNET_EVENT_URI_PHYSICAL)
{
uint32 index;
struct webnet_request *request;
assert(session != NULL);
request = session->request;
assert(request != NULL);
/* check authorization item */
for (index = 0; index < _auth_items_count; index ++)
{
if (str_path_with(request->path, _auth_items[index].path))
{
if (request->authorization == NULL ||
strlen(_auth_items[index].username_password) !=
strlen(request->authorization))
{
/* set authorization request, 401 */
request->result_code = 401;
return WEBNET_MODULE_FINISHED;
}
/* check authorization */
if (strcmp(request->authorization,
_auth_items[index].username_password) == 0)
{
/* authorization OK */
request->result_code = 200;
return WEBNET_MODULE_CONTINUE;
}
else
{
/* set authorization request, 401 */
request->result_code = 401;
return WEBNET_MODULE_FINISHED;
}
}
}
}
return WEBNET_MODULE_CONTINUE;
}
#endif /* WEBNET_USING_AUTH */

122
module/wn_module_cgi.c Normal file
View File

@ -0,0 +1,122 @@
/*
* File : wn_module_cgi.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 transplanted from rt-thread
*/
#include <transform.h>
#include <string.h>
#include <webnet.h>
#include <wn_module.h>
#include <wn_utils.h>
#ifdef WEBNET_USING_CGI
#define CGI_ROOT_PATH_MAX 64
static char _cgi_root[CGI_ROOT_PATH_MAX] = {0};
struct webnet_cgi_item
{
const char* name;
void (*handler)(struct webnet_session* session);
};
static struct webnet_cgi_item* _cgi_items = NULL;
static uint32 _cgi_count = 0;
void webnet_cgi_set_root(const char* root)
{
if (strlen(root) > CGI_ROOT_PATH_MAX)
{
assert(0);
return;
}
strncpy(_cgi_root, root, strlen(root));
if (_cgi_root[strlen(_cgi_root)] != '/')
{
_cgi_root[strlen(_cgi_root) + 1] = '/';
_cgi_root[strlen(_cgi_root) + 1] = '\0';
}
}
void webnet_cgi_register(const char* name, void (*handler)(struct webnet_session* session))
{
if (_cgi_items == NULL)
{
_cgi_count = 1;
_cgi_items = (struct webnet_cgi_item*) wn_malloc (sizeof(struct webnet_cgi_item) * _cgi_count);
}
else
{
_cgi_count += 1;
_cgi_items = (struct webnet_cgi_item*) wn_realloc (_cgi_items, sizeof(struct webnet_cgi_item) * _cgi_count);
}
assert(_cgi_items != NULL);
_cgi_items[_cgi_count - 1].name = name;
_cgi_items[_cgi_count - 1].handler = handler;
}
int webnet_module_cgi(struct webnet_session* session, int event)
{
if (event == WEBNET_EVENT_INIT)
{
/* set default cgi path */
if (_cgi_root[0] == '\0')
{
strcpy(_cgi_root, "/cgi-bin/");
}
}
else if (event == WEBNET_EVENT_URI_PHYSICAL)
{
struct webnet_request* request;
char *cgi_path = NULL;
assert(session != NULL);
request = session->request;
assert(request != NULL);
/* check whether a cgi request */
cgi_path = strstr(request->path, _cgi_root);
if (cgi_path != NULL)
{
char* cgi_name;
uint32 index;
cgi_name = cgi_path + strlen(_cgi_root);
for (index = 0; index < _cgi_count; index ++)
{
if ((strlen(cgi_name) == strlen(_cgi_items[index].name))
&& strncasecmp(cgi_name, _cgi_items[index].name, strlen(_cgi_items[index].name)) == 0)
{
/* found it */
_cgi_items[index].handler(session);
return WEBNET_MODULE_FINISHED;
}
}
/* set 404 not found error */
request->result_code = 404;
}
}
return WEBNET_MODULE_CONTINUE;
}
#endif /* WEBNET_USING_CGI */

486
module/wn_module_dav.c Normal file
View File

@ -0,0 +1,486 @@
/*
* File : wn_module_dav.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 transplanted from rt-thread
*/
#include <transform.h>
#include <webnet.h>
#include <wn_session.h>
#include <wn_module.h>
#include <wn_utils.h>
#ifdef WEBNET_USING_DAV
struct webnet_module_put_entry
{
int (*put_open) (struct webnet_session* session);
int (*put_close)(struct webnet_session* session);
int (*put_write)(struct webnet_session* session, const void* data, uint32 length);
int (*put_done) (struct webnet_session* session);
};
struct webnet_module_put_session
{
uint16 file_opened;
/* put entry */
const struct webnet_module_put_entry* entry;
uint32 user_data;
};
static const char* propfind_element ="<d:response>"
"<d:href>%s</d:href>"
"<d:propstat>"
"<d:prop>"
"<d:resourcetype>%s</d:resourcetype>"
"<d:getcontentlength>%d</d:getcontentlength>"
"<d:getlastmodified>%s</d:getlastmodified>" // Sat, 05 Sep 2015 09:47:53 GMT
"</d:prop>"
"<d:status>HTTP/1.1 200 OK</d:status>"
"</d:propstat>"
"</d:response>\n";
static const void* webnet_put_get_userdata(struct webnet_session* session);
int webnet_module_put_method(struct webnet_session* session);
static void print_propfind_element(struct webnet_session *session, const char *uri,struct stat *stp);
int webnet_module_dav(struct webnet_session* session, int event)
{
if (event != WEBNET_EVENT_URI_POST)
return WEBNET_MODULE_CONTINUE;
if(session->request->method == WEBNET_OPTIONS)
{
static const char* status = "Allow: GET, POST, HEAD, PUT, DELETE, OPTIONS, PROPFIND, MKCOL\r\nDAV: 1\r\n\r\n";
printf("OPTIONS %s", session->request->path);
session->request->result_code = 200;
webnet_session_set_header_status_line(session, session->request->result_code, "OK");
webnet_session_printf(session, status);
return WEBNET_MODULE_FINISHED;
}
else if(session->request->method == WEBNET_PROPFIND)
{
int fd;
DIR *dir;
uint8 exit_file = 0;
struct stat file_stat;
const char* parent_path;
static const char header[] = "HTTP/1.1 207 Multi-Status\r\n"
"Connection: close\r\n"
"Content-Type: text/xml; charset=utf-8\r\n\r\n"
"<?xml version=\"1.0\" encoding=\"utf-8\"?>"
"<d:multistatus xmlns:d='DAV:'>\n";
static const char footer[] = "</d:multistatus>";
char *depth = session->request->depth;
printf("PROPFIND %s depth: %s", session->request->path,
session->request->depth?session->request->depth:"null");
fd = open(session->request->path, O_RDONLY, 0);
if (fd >= 0)
{
close(fd);
exit_file = 1;
}
dir = opendir(session->request->path);
if(dir != NULL)
{
closedir(dir);
exit_file = 1;
}
if(!exit_file)
{
printf("Open file(%s) is not exist.", session->request->path);
session->request->result_code = 404;
return WEBNET_MODULE_FINISHED;
}
webnet_session_printf(session, header);
parent_path = session->request->path + strlen(webnet_get_root());
/* output the parent. */
file_stat.st_size = 0;
file_stat.st_mtime = 0;
file_stat.st_mode = S_IFDIR;
print_propfind_element(session, parent_path, &file_stat);
/* depth: 0, 1, infinity. */
if( (depth == NULL || (strcmp(depth, "0") != 0) ) )
{
struct dirent* dirent;
char *fullpath;
fullpath = wn_malloc (WEBNET_PATH_MAX);
memset(&file_stat, 0, sizeof(struct stat));
//eg. dir = opendir("/webnet/SD");
dir = opendir(session->request->path);
while((dirent = readdir(dir)) != NULL)
{
/* build full path for each file */
sprintf(fullpath, "%s/%s",session->request->path, dirent->d_name);
str_normalize_path(fullpath);
stat(fullpath, &file_stat);
print_propfind_element(session, dirent->d_name,&file_stat);
}
closedir(dir);
wn_free(fullpath);
}
webnet_session_printf(session,footer);
return WEBNET_MODULE_FINISHED;
}
else if(session->request->method == WEBNET_PUT)
{
printf("PUT %s", session->request->path);
return webnet_module_put_method(session);
}
else if(session->request->method == WEBNET_PROPPATCH)
{
int proppatch_length = 0;
static const char Proppatch_header[] = "HTTP/1.1 207 Multi-Status\r\n"
//"Date: Mon, 07 Sep 2015 01:50:42 GMT\r\n"
"Server: %s %s\r\n"
"Content-Length: %d \r\n"
"Content-Type: text/xml; charset=\"utf-8\"\r\n\r\n";
static const char Proppatch_ex[] = "<?xml version=\"1.0\" encoding=\"utf-8\"?>"
"<D:multistatus xmlns:D=\"DAV:\" xmlns:ns0=\"DAV:\">"
"<D:response>"
"<D:href>%s</D:href>"
"<D:propstat>"
"<D:prop><ns0:getlastmodified/>"
"</D:prop>"
"<D:status>HTTP/1.1 409 (status)</D:status>"
"<D:responsedescription>"
"Property is read-only.</D:responsedescription>"
"</D:propstat>"
"</D:response>"
"</D:multistatus>";
printf("PROPPATCH %s", session->request->path);
proppatch_length = strlen(Proppatch_ex)+strlen(session->request->path)-2;
webnet_session_printf(session,Proppatch_header,WEBNET_THREAD_NAME,WEBNET_VERSION,proppatch_length);
webnet_session_printf(session,Proppatch_ex,session->request->path);
return WEBNET_MODULE_FINISHED;
}
else if (session->request->method == WEBNET_DELETE)
{
printf("DELETE %s", session->request->path);
unlink(session->request->path);
webnet_session_printf(session,"HTTP/1.1 204 No Content\r\n"
"Server: webnet\r\n"
"Content-Length: 0\r\n"
"Content-Type: text/plain\r\n\r\n");
return WEBNET_MODULE_FINISHED;
}
else if (session->request->method == WEBNET_MKCOL)
{
int ret;
printf("MKCOL %s", session->request->path);
ret = mkdir(session->request->path, 0x777);
if (ret < 0)
{
session->request->result_code = 404;
printf("MKCOL mkdir error, path %s.", session->request->path);
}
else
{
printf("MKCOL mkdir ok !");
}
webnet_session_printf(session,"HTTP/1.1 201 Created\r\n"
"Server: webnet\r\n"
"Content-Length: 0\r\n"
"Content-Type: text/plain\r\n\r\n");
return WEBNET_MODULE_FINISHED;
}
else if (session->request->method == WEBNET_MOVE)
{
char *path, *full_path;
path = strstr(session->request->destination, session->request->host);
if (path)
{
path += strlen(session->request->host);
full_path = (char*) wn_malloc (WEBNET_PATH_MAX);
if (full_path)
{
webnet_session_get_physical_path(session, path, full_path);
printf("Get full path, %s => %s\n", session->request->path, full_path);
rename(session->request->path, full_path);
wn_free(full_path);
}
}
webnet_session_printf(session,"HTTP/1.1 200 OK\r\n"
"Server: webnet\r\n"
"Content-Length: 0\r\n"
"Content-Type: text/plain\r\n\r\n");
return WEBNET_MODULE_FINISHED;
}
return WEBNET_MODULE_CONTINUE;
}
#include <time.h>
static void print_propfind_element(struct webnet_session *session, const char *uri, struct stat *file_stat)
{
char ctime_str[64];
time_t t = file_stat->st_mtime;
PrivTaskenterCritical();
strftime(ctime_str, sizeof(ctime_str), "%a, %d %b %Y %H:%M:%S GMT", localtime(&t));
PrivTaskexitCritical();
//printf("strftime: %s\n", ctime_str);
webnet_session_printf(session,
propfind_element,
uri, S_ISDIR(file_stat->st_mode) ? "<d:collection/>" : "",
file_stat->st_size, ctime_str);
}
static int put_open (struct webnet_session* session)
{
int fd;
if(session->request->path == NULL)
{
fd = -1;
goto _exit;
}
fd = open(session->request->path, O_WRONLY | O_CREAT | O_TRUNC, 0);
if(fd < 0)
{
session->session_phase = WEB_PHASE_CLOSE;
fd = -1;
goto _exit;
}
return fd;
_exit:
printf(" %s failed ,file:%s ,line:%d",__FUNCTION__,__FILE__,__LINE__);
return (int)fd;
}
static int put_close(struct webnet_session* session)
{
int fd;
fd = (int)webnet_put_get_userdata(session);
if (fd < 0) return 0;
close(fd);
return 0;
}
static int put_done (struct webnet_session* session)
{
int put_done_h_length = 0;
static const char put_done_h[] = "HTTP/1.1 201 Created\r\n"
"Date: Mon, 07 Sep 2015 01:50:42 GMT\r\n"
"Server: %s %s\r\n"
"Location: http://%s%s \r\n"
"Content-Length: %d \r\n"
"Content-Type: text/html; charset=ISO-8859-1\r\n\r\n";
static const char put_done_t[] ="<!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML 2.0//EN\">"
"<html><head>"
"<title>";
printf("put_done.");
put_done_h_length = strlen(put_done_t);
webnet_session_printf(session,put_done_h,WEBNET_THREAD_NAME,WEBNET_VERSION,inet_ntoa(session->cliaddr.sin_addr),session->request->path,put_done_h_length);
webnet_session_printf(session,put_done_t);
return 0;
}
static int put_write(struct webnet_session* session, const void* data, uint32 length)
{
int fd;
// get fd
fd = (int)webnet_put_get_userdata(session);
if (fd < 0) return 0;
write(fd, data, length);
return length;
}
static const void* webnet_put_get_userdata(struct webnet_session* session)
{
struct webnet_module_put_session *put_session;
/* get put session */
put_session = (struct webnet_module_put_session *)session->user_data;
if (put_session == NULL) return NULL;
return (const void*) put_session->user_data;
}
static void _webnet_module_put_close(struct webnet_session* session)
{
struct webnet_module_put_session *put_session;
/* get put session */
put_session = (struct webnet_module_put_session *)session->user_data;
if (put_session == NULL) return;
/* close file */
if (put_session->file_opened == 1)
{
put_session->entry->put_close(session);
put_session->file_opened = 0;
}
wn_free(put_session);
/* remove private data */
session->user_data = 0;
session->session_ops = NULL;
}
static void _webnet_module_put_handle(struct webnet_session* session, int event)
{
if(session->request->method == WEBNET_PUT)
{
int length = 0;
static int read_bytes = 0;
struct webnet_module_put_session *put_session;
put_session = (struct webnet_module_put_session *)session->user_data;
/* read stream */
length = webnet_session_read(session, (char *)session->buffer, sizeof(session->buffer) - 1);
/* connection break out */
if (length <= 0)
{
printf(" %s failed ,file:%s ,line:%d\n",__FUNCTION__,__FILE__,__LINE__);
/* read stream failed (connection break out), close this session */
session->session_phase = WEB_PHASE_CLOSE;
return;
}
read_bytes = read_bytes + length;
session->buffer[length] = '\0';
/* open file */
if (put_session->file_opened == 0)
{
/* open file */
put_session->user_data = put_session->entry->put_open(session);
put_session->file_opened = 1;
}
//write data
if (length > 0 && session->buffer != NULL)
{
put_session->entry->put_write(session, (char*)session->buffer, length);
}
if (read_bytes >= (session->request->content_length))
{
put_session->entry->put_done(session);
_webnet_module_put_close(session);
read_bytes = 0;
}
}
}
static const struct webnet_session_ops _put_ops =
{
_webnet_module_put_handle,
_webnet_module_put_close
};
const struct webnet_module_put_entry put_entry_put =
{
put_open,
put_close,
put_write,
put_done
};
int webnet_module_put_method(struct webnet_session* session)
{
const struct webnet_module_put_entry *entry = &put_entry_put;
struct webnet_module_put_session *put_session;
/* create a uploading session */
put_session = (struct webnet_module_put_session*) wn_malloc (sizeof (struct webnet_module_put_session));
if(put_session == NULL)
{
printf("No memory for module put session.");
return WEBNET_MODULE_CONTINUE;
}
put_session->file_opened = 0;
put_session->entry = entry;
put_session->user_data = 0;
/* add this put session into webnet session */
session->user_data = (uint32) put_session;
/* set webnet session operations */
session->session_ops = &_put_ops;
return WEBNET_MODULE_FINISHED;
}
#endif /* WEBNET_USING_DAV */

120
module/wn_module_index.c Normal file
View File

@ -0,0 +1,120 @@
/*
* File : wn_module_index.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 transplanted from rt-thread
*/
#include <transform.h>
#include <webnet.h>
#include <wn_module.h>
#include <wn_utils.h>
#ifdef WEBNET_USING_INDEX
int webnet_module_dirindex(struct webnet_session* session, int event)
{
if( (session->request->method != WEBNET_GET)
&& (session->request->method != WEBNET_POST) )
{
return WEBNET_MODULE_CONTINUE;
}
if (event == WEBNET_EVENT_URI_POST)
{
DIR *dir;
struct stat file_stat;
struct webnet_request *request;
static const char* header = "<html><head><title>Index of %s</title></head><body bgcolor=\"white\"><h1>Index of %s</h1><hr><pre>";
static const char* foot = "</pre><hr>WebNet/%s (RT-Thread)</body></html>";
assert(session != NULL);
request = session->request;
assert(request != NULL);
if (stat(request->path, &file_stat) < 0 || !S_ISDIR(file_stat.st_mode))
{
return WEBNET_MODULE_CONTINUE;
}
dir = opendir(request->path);
if (dir != NULL)
{
struct stat s;
struct dirent* dirent;
const char* sub_path;
char *fullpath;
char *delim;
dirent = NULL;
fullpath = wn_malloc (WEBNET_PATH_MAX);
if (fullpath == NULL)
{
request->result_code = 500;
return WEBNET_MODULE_FINISHED;
}
webnet_session_set_header(session, "text/html", 200, "OK", -1);
/* get sub path */
sub_path = request->path + strlen(webnet_get_root());
delim = strrchr(sub_path, '/');
snprintf(fullpath, delim - sub_path + 1, "%s", sub_path);
webnet_session_printf(session, header, sub_path, sub_path);
/* display parent directory */
webnet_session_printf(session, "<a href=\"../\">..</a>\n");
/* list directory */
do
{
dirent = readdir(dir);
if (dirent == NULL) break;
memset(&s, 0, sizeof(struct stat));
/* build full path for each file */
sprintf(fullpath, "%s/%s", request->path, dirent->d_name);
str_normalize_path(fullpath);
stat(fullpath, &s);
sprintf(fullpath, "%s/%s", sub_path, dirent->d_name);
if ( s.st_mode & S_IFDIR )
{
webnet_session_printf(session, "<a href=\"%s/\">%s/</a>\n", fullpath, dirent->d_name);
}
else
{
webnet_session_printf(session, "<a href=\"%s\">%s</a>\t\t\t\t\t%d\n", fullpath, dirent->d_name, s.st_size);
}
}
while (dirent != NULL);
closedir(dir);
wn_free(fullpath);
/* set foot */
webnet_session_printf(session, foot, WEBNET_VERSION);
return WEBNET_MODULE_FINISHED;
}
}
return WEBNET_MODULE_CONTINUE;
}
#endif /* WEBNET_USING_INDEX */

101
module/wn_module_log.c Normal file
View File

@ -0,0 +1,101 @@
/*
* File : wn_module_log.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 transplanted from rt-thread
*/
#include <transform.h>
#include <webnet.h>
#include <wn_module.h>
#include <wn_utils.h>
#ifdef WEBNET_USING_LOG
int webnet_module_log(struct webnet_session* session, int event)
{
struct webnet_request* request;
if (session != NULL)
{
request = session->request;
}
else
{
request = NULL;
}
if (event == WEBNET_EVENT_INIT)
{
printf("server initialize success.");
}
else if (event == WEBNET_EVENT_URI_PHYSICAL)
{
uint32 index;
printf("\n");
printf(" new client: %s:%u",
inet_ntoa(session->cliaddr.sin_addr),
ntohs(session->cliaddr.sin_port));
switch (request->method)
{
case WEBNET_GET:
printf(" method: GET");
break;
case WEBNET_PUT:
printf(" method: PUT");
break;
case WEBNET_POST:
printf(" method: POST");
break;
case WEBNET_HEADER:
printf(" method: HEADER");
break;
case WEBNET_SUBSCRIBE:
printf(" method: SUBSCRIBE");
break;
case WEBNET_UNSUBSCRIBE:
printf(" method: UNSUBSCRIBE");
break;
default:
break;
}
printf(" request: %s", request->path);
for (index = 0; index < request->query_counter; index ++)
{
printf(" query[%d]: %s => %s", index,
request->query_items[index].name,
request->query_items[index].value);
}
}
else if (event == WEBNET_EVENT_URI_POST)
{
printf("physical url: %s", request->path);
printf(" mime type: %s", mime_get_type(request->path));
}
else if (event == WEBNET_EVENT_RSP_HEADER)
{
}
else if (event == WEBNET_EVENT_RSP_FILE)
{
}
return WEBNET_MODULE_CONTINUE;
}
#endif /* WEBNET_USING_LOG */

253
module/wn_module_ssi.c Normal file
View File

@ -0,0 +1,253 @@
/*
* File : wn_module_ssi.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 transplanted from rt-thread
*/
#include <transform.h>
#include <webnet.h>
#include <wn_session.h>
#include <wn_module.h>
#if defined(WEBNET_USING_SSI)
#define SSI_INCLUDE_STRING "<!--#include "
#define SSI_EXEC_STRING "<!--#exec "
#define SSI_VIRTUAL_STRING "virtual=\""
#define SSI_FILE_STRING "file=\""
#define SSI_CGI_STRING "cgi=\""
#define SSI_END_STRING "\" -->"
static void _webnet_ssi_sendfile(struct webnet_session* session, const char* filename)
{
int fd;
int file_length;
unsigned long size, readbytes;
fd = open(filename, O_RDONLY, 0);
if (fd < 0) return; /* open file failed */
/* get file size */
file_length = lseek(fd, 0, SEEK_END);
/* seek to beginning of file */
lseek(fd, 0, SEEK_SET);
if (file_length <= 0)
{
close(fd);
return ;
}
while (file_length)
{
if (file_length > sizeof(session->buffer))
size = (unsigned long) sizeof(session->buffer);
else
size = file_length;
readbytes = read(fd, session->buffer, size);
if (readbytes <= 0)
/* no more data */
break;
if (webnet_session_write(session, session->buffer, readbytes) == 0)
break;
file_length -= (long) readbytes;
}
/* close file */
close(fd);
}
static void _webnet_ssi_dofile(struct webnet_session* session, int fd)
{
char *ssi_begin, *ssi_end;
char *offset, *end;
char *buffer;
char *path;
uint32 length;
ssi_begin = ssi_end = NULL;
offset = end = NULL;
buffer = path = NULL;
/* get file length */
length = lseek(fd, 0, SEEK_END);
lseek(fd, 0, SEEK_SET);
/* allocate ssi include file path */
path = (char*) wn_malloc(WEBNET_PATH_MAX);
/* allocate read buffer */
buffer = (char*) wn_malloc (length);
if (path == NULL || buffer == NULL)
{
session->request->result_code = 500;
goto __exit;
}
/* write page header */
webnet_session_set_header(session, "text/html", 200, "OK", -1);
/* read file to buffer */
if (read(fd, buffer, length) != length) /* read failed */
{
session->request->result_code = 500;
close(fd);
goto __exit;
}
/* close file */
close(fd);
offset = buffer;
end = buffer + length;
while (offset < end)
{
/* get beginning of ssi */
ssi_begin = strstr(offset, SSI_INCLUDE_STRING);
if (ssi_begin == NULL)
{
/* write content directly */
webnet_session_write(session, (const uint8_t*)offset, end - offset);
break;
}
/* get end of ssi */
ssi_end = strstr(ssi_begin, "\" -->");
if (ssi_end == NULL)
{
/* write content directly */
webnet_session_write(session, (const uint8_t*)offset, end - offset);
break;
}
else
{
char *include_begin, *include_end;
char *filename;
/* write content */
webnet_session_write(session, (const uint8_t*)offset, ssi_begin - offset);
offset = ssi_begin + sizeof(SSI_INCLUDE_STRING) - 1;
include_begin = strstr(ssi_begin, SSI_VIRTUAL_STRING);
if (include_begin != NULL)
{
filename = include_begin + sizeof(SSI_VIRTUAL_STRING) - 1;
include_end = strstr(filename, "\"");
*include_end = '\0';
if (webnet_session_get_physical_path(session, filename, path) == 0)
{
_webnet_ssi_sendfile(session, path);
}
}
else
{
include_begin = strstr(ssi_begin, SSI_FILE_STRING);
if (include_begin != NULL)
{
filename = include_begin + sizeof(SSI_FILE_STRING) - 1;
include_end = strstr(filename, "\"");
*include_end = '\0';
_webnet_ssi_sendfile(session, filename);
}
}
offset = ssi_end + sizeof(SSI_END_STRING) - 1;
}
}
/* exit and release buffer buffer */
__exit:
if (path != NULL) wn_free(path);
if (buffer != NULL) wn_free(buffer);
}
static const char* ssi_extension[] =
{
".shtm",
".SHTM",
".shtml",
".SHTML",
".stm",
".STM",
NULL
};
int webnet_module_ssi(struct webnet_session* session, int event)
{
struct webnet_request* request;
/* check parameter */
assert(session != NULL);
request = session->request;
assert(request != NULL);
if (event == WEBNET_EVENT_URI_POST)
{
int fd;
int index;
/* check whether a ssi file */
index = 0;
while (ssi_extension[index] != NULL)
{
if (strstr(request->path, ssi_extension[index]) != NULL)
{
/* try to open this file */
fd = open(request->path, O_RDONLY, 0);
if ( fd >= 0)
{
_webnet_ssi_dofile(session, fd);
close(fd);
return WEBNET_MODULE_FINISHED;
}
else
{
/* no this file */
request->result_code = 404;
return WEBNET_MODULE_FINISHED;
}
}
index ++;
}
/* no this file, try index.shtm */
{
char *ssi_filename;
ssi_filename = (char*) wn_malloc (WEBNET_PATH_MAX);
if (ssi_filename != NULL)
{
snprintf(ssi_filename, WEBNET_PATH_MAX, "%s/index.shtm", request->path);
fd = open(ssi_filename, O_RDONLY, 0);
if (fd >= 0)
{
wn_free(ssi_filename);
_webnet_ssi_dofile(session, fd);
close(fd);
return WEBNET_MODULE_FINISHED;
}
}
wn_free(ssi_filename);
}
}
return WEBNET_MODULE_CONTINUE;
}
#endif

772
module/wn_module_upload.c Normal file
View File

@ -0,0 +1,772 @@
/*
* File : wn_module_upload.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 transplanted from rt-thread
*/
#include <transform.h>
#include <webnet.h>
#include <wn_module.h>
#include <wn_utils.h>
#if defined(WEBNET_USING_UPLOAD)
#define MULTIPART_FORM_DATA_STRING "multipart/form-data"
#define BOUNDARY_STRING "boundary="
#define CONTENT_DISPOSITION_STRING "Content-Disposition:"
#define CONTENT_TYPE_STRING "Content-Type:"
#define CONTENT_RANGE_STRING "Content-Range:"
#define FORM_DATA_STRING "form-data"
#define FILENAME_STRING "filename=\""
#define FIELDNAME_STRING "name=\""
#define FIRST_BOUNDARY 2, upload_session->boundary, "\r\n"
#define NORMAL_BOUNDARY 3, "\r\n", upload_session->boundary, "\r\n"
#define COMMON_BOUNDARY 2, "\r\n", upload_session->boundary
#define LAST_BOUNDARY 3, "\r\n", upload_session->boundary, "--\r\n"
#define FIRST_BOUNDARY_SIZE (strlen(upload_session->boundary) + 2)
#define NORMAL_BOUNDARY_SIZE (strlen(upload_session->boundary) + 4)
#define COMMON_BOUNDARY_SIZE (strlen(upload_session->boundary) + 2)
#define LAST_BOUNDARY_SIZE (strlen(upload_session->boundary) + 6)
struct webnet_upload_name_entry
{
char *name;
char *value;
};
static const struct webnet_module_upload_entry **_upload_entries = NULL;
static uint16 _upload_entries_count = 0;
struct webnet_module_upload_session
{
char* boundary;
char* filename;
char* content_type;
struct webnet_upload_name_entry* name_entries;
uint16 name_entries_count;
uint16 file_opened;
/* upload entry */
const struct webnet_module_upload_entry* entry;
/* user data */
uint32 user_data;
};
static int str_begin_with_strs(const char* str, int num, ...)
{
char *match;
va_list args;
int index, result;
result = 1;
va_start(args,num);
for (index = 0; index < num; index ++)
{
match = va_arg(args, char*);
if (strncasecmp(str, match, strlen(match)) != 0)
{
result = 0;
break;
}
str += strlen(match);
}
va_end(args);
return result;
}
/* Search string on binary data */
static char *memstr(const char *haystack, uint32 length, const char *needle)
{
uint32 nl=strlen(needle);
uint32 hl=length;
uint32 i;
if (!nl) goto __found;
if (nl > length) return 0;
for (i=hl-nl+1; i; --i)
{
if (*haystack==*needle && !memcmp(haystack,needle,nl))
__found:
return (char*)haystack;
++haystack;
}
return 0;
}
/* Search string array on binary data */
static char* memstrs(const char* str, uint32 length, int num, ...)
{
char *substr;
char *index_str, *ptr;
va_list args;
int index;
int total_size;
/* get the match total string length */
va_start(args, num);
total_size = 0;
for (index = 0; index < num; index ++)
{
index_str = va_arg(args, char*);
total_size += strlen(index_str);
}
va_end(args);
substr = wn_malloc(total_size + 1);
ptr = substr;
va_start(args, num);
for (index = 0; index < num; index++)
{
index_str = va_arg(args, char*);
memcpy(ptr, index_str, strlen(index_str));
ptr += strlen(index_str);
}
substr[total_size] = '\0';
va_end(args);
/* find in binary data */
ptr = memstr(str, length, substr);
wn_free(substr);
return ptr;
}
#if !(defined(__GNUC__) && !defined(__ARMCC_VERSION)/*GCC*/)
static void* memrchr(const void *s, int c, uint32 n)
{
register const char* t=s;
register const char* last=0;
int i;
t = t + n;
for (i=n; i; --i)
{
if (*t==c)
{
last=t;
break;
}
t--;
}
return (void*)last; /* man, what an utterly b0rken prototype */
}
#endif
static char* _webnet_module_upload_parse_header(struct webnet_session* session, char* buffer, uint32 length)
{
char *ptr, *ch, *end_ptr;
struct webnet_module_upload_session *upload_session;
char *name = NULL, *filename = NULL, *content_type = NULL;
/* get upload session */
upload_session = (struct webnet_module_upload_session *)session->user_data;
ptr = buffer;
end_ptr = buffer + length;
/* begin with last boundary */
if (str_begin_with_strs(ptr, LAST_BOUNDARY))
{
ptr += LAST_BOUNDARY_SIZE;
return ptr;
}
/* begin with normal boundary */
if (str_begin_with_strs(ptr, NORMAL_BOUNDARY))
ptr += NORMAL_BOUNDARY_SIZE;
else if (str_begin_with_strs(ptr, FIRST_BOUNDARY))
ptr += FIRST_BOUNDARY_SIZE;
if (ptr == buffer) return NULL; /* not begin with boundary */
if (upload_session->filename != NULL &&
upload_session->content_type != NULL)
{
/* end of file name section */
wn_free(upload_session->filename);
wn_free(upload_session->content_type);
upload_session->filename = NULL;
upload_session->content_type = NULL;
}
while ((uint32)ptr - (uint32)buffer < length)
{
/* handle Content-Disposition: */
if (str_begin_with(ptr, CONTENT_DISPOSITION_STRING))
{
ptr += sizeof(CONTENT_DISPOSITION_STRING) - 1;
while (*ptr == ' ') ptr ++;
/* form-data */
if (str_begin_with(ptr, FORM_DATA_STRING))
{
ptr += sizeof(FORM_DATA_STRING) - 1;
while (*ptr == ' ' || *ptr == ';') ptr ++;
/* get name="str" */
if (str_begin_with(ptr, FIELDNAME_STRING))
{
ptr += sizeof(FIELDNAME_STRING) - 1;
name = ptr;
ch = memchr(ptr, '"', buffer - ptr);
if (ch != NULL)
{
*ch = '\0';
ch ++;
while (*ch == ' ' || *ch ==';') ch ++;
ptr = ch;
}
}
/* get filename="str" */
if (str_begin_with(ptr, FILENAME_STRING))
{
ptr += sizeof(FILENAME_STRING) - 1;
filename = ptr;
ch = memchr(ptr, '"', buffer - ptr);
if (ch != NULL)
{
*ch = '\0';
ch ++;
ptr = ch;
}
}
}
}
/* handle Content-Type */
else if (str_begin_with(ptr, CONTENT_TYPE_STRING))
{
ptr += sizeof(CONTENT_TYPE_STRING) - 1;
while (*ptr == ' ') ptr ++;
content_type = ptr;
}
/* handle Content-Range: */
else if (str_begin_with(ptr, CONTENT_RANGE_STRING))
{
ptr += sizeof(CONTENT_RANGE_STRING) - 1;
while (*ptr == ' ') ptr ++;
}
/* whether end of header */
else if (str_begin_with(ptr, "\r\n"))
{
ptr += 2;
if (upload_session->filename != NULL)
{
wn_free(upload_session->filename);
upload_session->filename = NULL;
}
if (upload_session->content_type != NULL)
{
wn_free(upload_session->content_type);
upload_session->content_type = NULL;
}
if (filename != NULL)
{
upload_session->filename = wn_strdup(filename);
}
if (content_type != NULL)
{
upload_session->content_type = wn_strdup(content_type);
}
if (name != NULL)
{
/* add a name entry in the upload session */
if (upload_session->name_entries == NULL)
{
upload_session->name_entries_count = 1;
upload_session->name_entries = (struct webnet_upload_name_entry*)
wn_malloc(sizeof(struct webnet_upload_name_entry));
}
else
{
upload_session->name_entries_count += 1;
upload_session->name_entries = (struct webnet_upload_name_entry*)
wn_realloc(upload_session->name_entries,
sizeof(struct webnet_upload_name_entry) * upload_session->name_entries_count);
}
upload_session->name_entries[upload_session->name_entries_count - 1].name = wn_strdup(name);
upload_session->name_entries[upload_session->name_entries_count - 1].value = NULL;
}
return ptr;
}
/* skip this request header line */
ch = memstr(ptr, end_ptr - ptr, "\r\n");
*ch = '\0';
ch ++;
*ch = '\0';
ch ++;
ptr = ch;
}
return ptr;
}
static char* _next_possible_boundary(struct webnet_session* session, char* buffer, uint32 length)
{
char *ptr;
struct webnet_module_upload_session *upload_session;
/* get upload session */
upload_session = (struct webnet_module_upload_session *)session->user_data;
/* try the beginning */
if (str_begin_with_strs(buffer, COMMON_BOUNDARY)) return buffer;
/* search in all the string */
ptr = memstrs(buffer, length, COMMON_BOUNDARY);
if (ptr != NULL) return ptr;
/* search from the end of string */
ptr = memrchr(buffer, '\r', length);
if (ptr == NULL ) return NULL;
if (ptr - buffer == length - 1) return ptr; /* only \r */
if (*(ptr + 1) == '\n' && ptr - buffer == length - 2) return NULL; /* only \r\n */
if (memcmp(ptr + 2, upload_session->boundary, length - (ptr - buffer + 2)) == 0) return ptr;
return NULL;
}
static void _handle_section(struct webnet_session* session, char* buffer, uint32 length)
{
char *ptr;
struct webnet_module_upload_session *upload_session;
#define name_entry \
(upload_session->name_entries[upload_session->name_entries_count - 1])
/* get upload session */
upload_session = (struct webnet_module_upload_session *)session->user_data;
if (upload_session->filename != NULL &&
upload_session->content_type != NULL&&
length != 0)
{
upload_session->entry->upload_write(session, buffer, length);
}
else
{
/* get name value */
ptr = memstr(buffer, length, "\r\n");
if (ptr != NULL)
{
int value_size = ptr - buffer + 1;
name_entry.value = wn_malloc(value_size);
memcpy(name_entry.value, buffer, value_size - 1);
name_entry.value[value_size - 1] = '\0';
}
}
}
static void _webnet_module_upload_handle(struct webnet_session* session, int event)
{
int length;
char *ptr, *end_ptr;
char *upload_buffer;
uint32 chunk_size;
struct webnet_module_upload_session *upload_session;
if (event != WEBNET_EVENT_READ) return;
/* get upload session */
upload_session = (struct webnet_module_upload_session *)session->user_data;
upload_buffer = (char*)session->buffer;
/* read stream */
if (memstrs((const char *)session->buffer, session->buffer_offset, LAST_BOUNDARY))
{
length = session->buffer_offset;
}
else
{
if (session->buffer_offset != 0)
{
length = webnet_session_read(session,
(char*)&session->buffer[session->buffer_offset],
sizeof(session->buffer) - session->buffer_offset - 1);
if (length > 0)
{
length += session->buffer_offset;
}
else
{
length = session->buffer_offset;
}
}
else
{
length = webnet_session_read(session, (char *)session->buffer, sizeof(session->buffer) - 1);
}
}
/* connection break out */
if (length <= 0)
{
/* read stream failed (connection break out), close this session */
session->session_phase = WEB_PHASE_CLOSE;
return;
}
end_ptr = (char*)(session->buffer + length);
session->buffer[length] = '\0';
while (upload_buffer < end_ptr)
{
if (str_begin_with_strs(upload_buffer, LAST_BOUNDARY))
{
/* upload done */
upload_session->entry->upload_done(session);
session->session_phase = WEB_PHASE_CLOSE;
return;
}
/* read more data */
if (end_ptr - upload_buffer < sizeof(session->buffer)/3 &&
memstrs(upload_buffer, end_ptr - upload_buffer, LAST_BOUNDARY) == NULL)
{
/* read more data */
memmove(session->buffer, upload_buffer, end_ptr - upload_buffer);
session->buffer_offset = end_ptr - upload_buffer;
return ;
}
ptr = _webnet_module_upload_parse_header(session, upload_buffer,
(uint32)end_ptr - (uint32)upload_buffer);
if (ptr == NULL)
{
/* not begin with a boundary */
ptr = _next_possible_boundary(session, upload_buffer,
(uint32)end_ptr - (uint32)upload_buffer);
if (ptr == NULL)
{
/* all are the data section */
_handle_section(session, upload_buffer, (uint32)end_ptr - (uint32)upload_buffer);
upload_buffer = end_ptr;
}
else
{
chunk_size = (uint32)ptr - (uint32)upload_buffer;
_handle_section(session, upload_buffer, chunk_size);
upload_buffer += chunk_size;
}
}
else
{
if (upload_session->filename != NULL &&
upload_session->content_type != NULL)
{
if (upload_session->file_opened == 0)
{
/* open file */
upload_session->user_data = upload_session->entry->upload_open(session);
upload_session->file_opened = 1;
}
}
else
{
if (upload_session->file_opened == 1)
{
/* close file */
upload_session->entry->upload_close(session);
upload_session->user_data = 0;
upload_session->file_opened = 0;
}
}
upload_buffer = ptr;
ptr = _next_possible_boundary(session, upload_buffer,
(uint32)end_ptr - (uint32)upload_buffer);
if (ptr == NULL)
{
/* all are the data section */
_handle_section(session, upload_buffer, (uint32)end_ptr - (uint32)upload_buffer);
upload_buffer = end_ptr;
}
else
{
chunk_size = (uint32)ptr - (uint32)upload_buffer;
_handle_section(session, upload_buffer, chunk_size);
upload_buffer += chunk_size;
}
}
}
session->buffer_offset = 0;
}
static void _webnet_module_upload_close(struct webnet_session* session)
{
struct webnet_module_upload_session *upload_session;
/* get upload session */
upload_session = (struct webnet_module_upload_session *)session->user_data;
if (upload_session == NULL) return;
/* close file */
if (upload_session->file_opened == 1)
{
upload_session->entry->upload_close(session);
upload_session->file_opened = 0;
}
/* free session */
if (upload_session->filename != NULL)
wn_free(upload_session->filename);
if (upload_session->content_type != NULL)
wn_free(upload_session->content_type);
if (upload_session->boundary != NULL)
wn_free(upload_session->boundary);
if (upload_session->entry != NULL)
{
uint32 index;
for (index = 0; index < upload_session->name_entries_count; index ++)
{
if (upload_session->name_entries[index].value != NULL)
wn_free(upload_session->name_entries[index].value);
if (upload_session->name_entries[index].name != NULL)
wn_free(upload_session->name_entries[index].name);
}
wn_free(upload_session->name_entries);
upload_session->name_entries = NULL;
}
wn_free(upload_session);
/* remove private data */
session->user_data = 0;
session->session_ops = NULL;
session->session_phase = WEB_PHASE_CLOSE;
}
static const struct webnet_session_ops _upload_ops =
{
_webnet_module_upload_handle,
_webnet_module_upload_close
};
int webnet_module_upload_open(struct webnet_session* session)
{
char* boundary;
uint32 index, length;
const struct webnet_module_upload_entry *entry = NULL;
struct webnet_module_upload_session *upload_session;
if (session->request->method != WEBNET_POST)
return WEBNET_MODULE_CONTINUE;
/* get upload entry */
for (index = 0; index < _upload_entries_count; index ++)
{
if (str_begin_with(session->request->path, _upload_entries[index]->url))
{
length = strlen(_upload_entries[index]->url);
if (session->request->path[length] == '\0' ||
session->request->path[length] == '?')
{
/* found entry */
entry = _upload_entries[index];
break;
}
}
}
if (entry == NULL) /* no this entry */
return WEBNET_MODULE_CONTINUE;
/* create a uploading session */
upload_session = (struct webnet_module_upload_session*) wn_malloc (
sizeof (struct webnet_module_upload_session));
if (upload_session == NULL) return 0; /* no memory */
/* get boundary */
boundary = strstr(session->request->content_type, BOUNDARY_STRING);
if (boundary != NULL)
{
int boundary_size;
/* make boundary */
boundary_size = strlen(boundary + sizeof(BOUNDARY_STRING) - 1) + 3;
upload_session->boundary = wn_malloc(boundary_size);
sprintf(upload_session->boundary, "--%s", boundary + sizeof(BOUNDARY_STRING) - 1);
}
upload_session->filename = NULL;
upload_session->content_type = NULL;
upload_session->name_entries = NULL;
upload_session->name_entries_count = 0;
upload_session->file_opened = 0;
upload_session->entry = entry;
upload_session->user_data = 0;
/* add this upload session into webnet session */
session->user_data = (uint32) upload_session;
/* set webnet session operations */
session->session_ops = &_upload_ops;
session->session_ops->session_handle(session, WEBNET_EVENT_READ);
return WEBNET_MODULE_FINISHED;
}
int webnet_module_upload(struct webnet_session* session, int event)
{
if (event == WEBNET_EVENT_URI_PHYSICAL)
{
return webnet_module_upload_open(session);
}
return WEBNET_MODULE_CONTINUE;
}
/* webnet upload module APIs */
void webnet_upload_add(const struct webnet_module_upload_entry* entry)
{
if (_upload_entries == NULL)
{
_upload_entries_count = 1;
/* first entries, malloc upload entry */
_upload_entries = (const struct webnet_module_upload_entry**)
wn_malloc (sizeof(struct webnet_module_upload_entry));
}
else
{
_upload_entries_count += 1;
_upload_entries = (const struct webnet_module_upload_entry**) wn_realloc (_upload_entries,
sizeof(void*) * _upload_entries_count);
}
assert(_upload_entries != NULL);
_upload_entries[_upload_entries_count - 1] = entry;
}
const char* webnet_upload_get_filename(struct webnet_session* session)
{
struct webnet_module_upload_session *upload_session;
/* get upload session */
upload_session = (struct webnet_module_upload_session *)session->user_data;
if (upload_session == NULL) return NULL;
return upload_session->filename;
}
const char* webnet_upload_get_content_type(struct webnet_session* session)
{
struct webnet_module_upload_session *upload_session;
/* get upload session */
upload_session = (struct webnet_module_upload_session *)session->user_data;
if (upload_session == NULL) return NULL;
return upload_session->content_type;
}
const char* webnet_upload_get_nameentry(struct webnet_session* session, const char* name)
{
uint32 index;
struct webnet_module_upload_session *upload_session;
/* get upload session */
upload_session = (struct webnet_module_upload_session *)session->user_data;
if (upload_session == NULL) return NULL;
for (index = 0; index < upload_session->name_entries_count; index ++)
{
if (strcasecmp(upload_session->name_entries[index].name, name) == 0)
{
return upload_session->name_entries[index].value;
}
}
return NULL;
}
const void* webnet_upload_get_userdata(struct webnet_session* session)
{
struct webnet_module_upload_session *upload_session;
/* get upload session */
upload_session = (struct webnet_module_upload_session *)session->user_data;
if (upload_session == NULL) return NULL;
return (const void*) upload_session->user_data;
}
/* ---- upload file interface ---- */
int webnet_upload_file_open(struct webnet_session* session)
{
struct webnet_module_upload_session* upload_session;
/* get upload session */
upload_session = (struct webnet_module_upload_session *)session->user_data;
if (upload_session->filename != NULL)
{
/* open file */
upload_session->user_data = open(upload_session->filename, O_WRONLY, 0);
return upload_session->user_data;
}
return -1;
}
int webnet_upload_file_close(struct webnet_session* session)
{
int fd;
struct webnet_module_upload_session* upload_session;
/* get upload session */
upload_session = (struct webnet_module_upload_session *)session->user_data;
fd = upload_session->user_data;
if (fd >= 0)
{
return close(fd);
}
return -1;
}
int webnet_upload_file_write(struct webnet_session* session, const void* data, unsigned long length)
{
int fd;
struct webnet_module_upload_session* upload_session;
/* get upload session */
upload_session = (struct webnet_module_upload_session *)session->user_data;
fd = upload_session->user_data;
if (fd >= 0)
{
return write(fd, data, length);
}
return 0;
}
#endif /* WEBNET_USING_UPLOAD */

81
samples/index.html Normal file
View File

@ -0,0 +1,81 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<title> WebNet </title>
<meta http-equiv="Content-Type" content="text/html; charset=gbk" />
</head>
<body>
<h1>WebNet</h1>
该组件是基于RT-Thread的Webnet进行移植修改 为小型嵌入式设备定制作为web server具有低资源占用扩展性好的特点。
<hr>
<h2>WebNet 功能</h2>
<ul>
<li>支持 HTTP 1.0/1.1 规范
<li>支持 AUTH 基本认证
<li>支持 CGI 事件处理
<li>支持 ASP 变量替换
<li>支持 SSI 文件嵌入
<li>支持 INDEX 目录显示
<li>支持 ALIAS 别名
<li>支持 upload 文件上传
</ul>
<hr>
<h3>1. AUTH Test</h3>
WebNet 基本认证功能设计成与 URL 目录相挂钩,在使用时可以<b>根据目录划分访问权限</b>
<br/><br/>
<a href="/admin/">基本认证功能测试:用户名及密码为 <b>admin:admin</b></a>
<br/><br/>
<hr>
<h3>2. CGI Test</h3>
WebNet CGI 功能可以让用户执行指定的函数CGI测试
<br/><br/>
<a href="/cgi-bin/hello">> hello world</a>
<br/>
<a href="/cgi-bin/calc">> calc</a>
<br/><br/>
<hr>
<h3>3. ASP Test</h3>
WebNet 实现的是一种简化方式的 ASP 变量替换,即当页面中有<b><% %></b> 标记出现时,将自动替换成用户注册的字符串并输。
<br/><br/>
<a href="/version.asp">ASP 功能测试:访问 version.asp 文件,打印当前 RT-Thread 系统版本号</a>
<br/><br/>
<hr>
<h3>4. SSI Test</h3>
SSI 功能模块可以用于执行服务器上程序或者插入文本内容到网页中页面代码中有 <b>#include virtual="/xxx"</b> 或者 <b>#include file="/xxx"</b> 标记存在时,将自动解析成对应文件中的信息。
<br/><br/>
<a href="/index.shtml">SSI 功能测试:访问 /version.shtml 页面,其中嵌套了 /index.html 页面</a>
<br/><br/>
<hr>
<h3>5. INDEX Test</h3>
INDEX 功能模块可以自动显示目录下文件列表,当访问目录中不存在 index.htm, index.html, index.asp 等文件时,将自动显示这个目录下的文件列表。
<br/><br/>
<a href="/admin/">INDEX 功能测试:访问/admin 目录,列出目录下所有文件</a>
<br/><br/>
<hr>
<h3>6. ALIAS Test</h3>
ALIAS 别名模块可以给当前资源文件夹设置多个别名,可以用于<b>长路径的简化操作</b>,方便用户对资源的访问。
<br/><br/>
<a href="/test/">ALIAS 功能测试:访问 /test 目录会跳转到 /admin 目录</a>
<br/><br/>
<hr>
<h3>7. Upload File Test</h3>
文件上传模块可以用于上传文件到指定的目录,这里上传到根目录下的 /upload 目录。
<br/><br/>
<form name="upload" method="POST" enctype="multipart/form-data" action="/upload">
<input type="file" name="file1" >
<input type="submit" name="submit" value="上传">
</form>
<br/>
<a href="/upload/">点击浏览上传文件的目录</a>
<br/><br/>
</body>
</html>

14
samples/index.shtml Normal file
View File

@ -0,0 +1,14 @@
<html>
<head>
<title> SSI Test </title>
</head>
<body>
<h1> SSI Test</h1>
<font size=\"+2\">The <b>index.html</b> page embedded in the following page</font>
<br/><br/>
<a href="javascript:history.go(-1);">Go back to root</a>
<hr>
<!--#include virtual="/index.html" -->
</body>
</html>

10
samples/version.asp Normal file
View File

@ -0,0 +1,10 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<title> ASP Test </title>
</head>
<body>
<% version %>
</body>
</html>

126
samples/wn_sample.c Normal file
View File

@ -0,0 +1,126 @@
/*
* File : wn_sample.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 transplanted from rt-thread
*/
#include <transform.h>
#include <webnet.h>
#include <wn_module.h>
#define XiUOS_VERSION (1)
#define XiUOS_SUBVERSION (0)
#define XiUOS_REVERSION (0)
static void asp_var_version(struct webnet_session* session)
{
assert(session != NULL);
static const char *version = "<html><body><font size=\"+2\">RT-Thread %d.%d.%d</font><br><br>"
"<a href=\"javascript:history.go(-1);\">Go back to root</a></html></body>";
webnet_session_printf(session, version, XiUOS_VERSION, XiUOS_SUBVERSION, XiUOS_REVERSION);
}
static void cgi_calc_handler(struct webnet_session* session)
{
int a, b;
const char* mimetype;
struct webnet_request* request;
static const char* header = "<html><head><meta http-equiv=\"Content-Type\" content=\"text/html; "
"charset=gb2312\" /><title> calc </title></head>";
static const char* body = "<body><form method=\"post\" action=\"/cgi-bin/calc\"><input type=\"text\" name=\"a\" value=\"%d\"> "
"+ <input type=\"text\" name=\"b\" value=\"%d\"> = %d<br><input type=\"submit\" value=\"\xBC\xC6\xCB\xE3\"></form>"
"<br><a href=\"/index.html\">Go back to root</a></body></html>\r\n";
assert(session != NULL);
request = session->request;
assert(request != NULL);
/* get mimetype */
mimetype = mime_get_type(".html");
a = 1;
b = 1;
/* set http header */
session->request->result_code = 200;
webnet_session_set_header(session, mimetype, 200, "Ok", -1);
webnet_session_write(session, (const uint8*)header, strlen(header));
if (request->query_counter)
{
const char *a_value, *b_value;
a_value = webnet_request_get_query(request, "a");
b_value = webnet_request_get_query(request, "b");
a = atoi(a_value);
b = atoi(b_value);
}
webnet_session_printf(session, body, a, b, a + b);
}
static void cgi_hello_handler(struct webnet_session* session)
{
const char* mimetype;
static const char* status = "<html><head><title> hello </title>"
"</head><body><font size=\"+2\">hello world</font><br><br>"
"<a href=\"javascript:history.go(-1);\">Go back to root</a></body></html>\r\n";
assert(session != NULL);
/* get mimetype */
mimetype = mime_get_type(".html");
/* set http header */
session->request->result_code = 200;
webnet_session_set_header(session, mimetype, 200, "Ok", strlen(status));
webnet_session_write(session, (const uint8*)status,strlen(status));
}
void webnet_test(void)
{
#ifdef WEBNET_USING_CGI
webnet_cgi_register("hello", cgi_hello_handler);
webnet_cgi_register("calc", cgi_calc_handler);
#endif
#ifdef WEBNET_USING_ASP
webnet_asp_add_var("version", asp_var_version);
#endif
#ifdef WEBNET_USING_ALIAS
webnet_alias_set("/test", "/admin");
#endif
#ifdef WEBNET_USING_AUTH
webnet_auth_set("/admin", "admin:admin");
#endif
#ifdef WEBNET_USING_UPLOAD
extern const struct webnet_module_upload_entry upload_entry_upload;
webnet_upload_add(&upload_entry_upload);
#endif
webnet_init();
}
#ifdef ADD_RTTHREAD_FETURES
MSH_CMD_EXPORT(webnet_test, wenbet test);
#endif

167
samples/wn_sample_upload.c Normal file
View File

@ -0,0 +1,167 @@
/*
* File : wn_sample_upload.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 transplanted from rt-thread
*/
#include <transform.h>
#include <webnet.h>
#include <wn_module.h>
#ifdef WEBNET_USING_UPLOAD
/**
* upload file.
*/
static const char * sd_upload = "/webnet";
static const char * upload_dir = "upload"; /* e.g: "upload" */
static int file_size = 0;
const char *get_file_name(struct webnet_session *session)
{
const char *path = NULL, *path_last = NULL;
path_last = webnet_upload_get_filename(session);
if (path_last == NULL)
{
printf("file name err!!\n");
return NULL;
}
path = strrchr(path_last, '\\');
if (path != NULL)
{
path++;
path_last = path;
}
path = strrchr(path_last, '/');
if (path != NULL)
{
path++;
path_last = path;
}
return path_last;
}
static int upload_open(struct webnet_session *session)
{
int fd;
const char *file_name = NULL;
file_name = get_file_name(session);
printf("Upload FileName: %s\n", file_name);
printf("Content-Type : %s\n", webnet_upload_get_content_type(session));
if (webnet_upload_get_filename(session) != NULL)
{
int path_size;
char * file_path;
path_size = strlen(sd_upload) + strlen(upload_dir)
+ strlen(file_name);
path_size += 4;
file_path = (char *)malloc(path_size);
if(file_path == NULL)
{
fd = -1;
goto _exit;
}
sprintf(file_path, "%s/%s/%s", sd_upload, upload_dir, file_name);
printf("save to: %s\r\n", file_path);
fd = open(file_path, O_WRONLY | O_CREAT, 0);
if (fd < 0)
{
webnet_session_close(session);
free(file_path);
fd = -1;
goto _exit;
}
}
file_size = 0;
_exit:
return (int)fd;
}
static int upload_close(struct webnet_session* session)
{
int fd;
fd = (int)webnet_upload_get_userdata(session);
if (fd < 0) return 0;
close(fd);
printf("Upload FileSize: %d\n", file_size);
return 0;
}
static int upload_write(struct webnet_session* session, const void* data, unsigned long length)
{
int fd;
fd = (int)webnet_upload_get_userdata(session);
if (fd < 0) return 0;
printf("write: length %d\n", length);
write(fd, data, length);
file_size += length;
return length;
}
static int upload_done (struct webnet_session* session)
{
const char* mimetype;
static const char* status = "<html><head><title>Upload OK </title>"
"</head><body>Upload OK, file length = %d "
"<br/><br/><a href=\"javascript:history.go(-1);\">"
"Go back to root</a></body></html>\r\n";
/* get mimetype */
mimetype = mime_get_type(".html");
/* set http header */
session->request->result_code = 200;
webnet_session_set_header(session, mimetype, 200, "Ok", strlen(status));
webnet_session_printf(session, status, file_size);
return 0;
}
const struct webnet_module_upload_entry upload_entry_upload =
{
"/upload",
upload_open,
upload_close,
upload_write,
upload_done
};
#endif /* WEBNET_USING_UPLOAD */

204
src/webnet.c Normal file
View File

@ -0,0 +1,204 @@
/*
* File : webnet.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 <stdint.h>
#include <webnet.h>
#include <wn_module.h>
#ifdef ADD_RTTHREAD_FETURES
#if defined(RT_USING_LWIP) && (RT_LWIP_TCPTHREAD_STACKSIZE < 1408)
#error The lwIP tcpip thread stack size(RT_LWIP_TCPTHREAD_STACKSIZE) must more than 1408
#endif
#endif
/*
other os kernel need to notice the point about lwip thread stack.
*/
static uint8 webnet_port = WEBNET_PORT;
static char webnet_root[64] = WEBNET_ROOT;
static bool init_ok = FALSE;
void webnet_set_port(int port)
{
assert(init_ok == FALSE);
webnet_port = port;
}
int webnet_get_port(void)
{
return webnet_port;
}
void webnet_set_root(const char* webroot_path)
{
strncpy(webnet_root, webroot_path, sizeof(webnet_root) - 1);
webnet_root[sizeof(webnet_root) - 1] = '\0';
}
const char* webnet_get_root(void)
{
return webnet_root;
}
/**
* webnet thread entry
*/
static void webnet_thread(void *parameter)
{
int listenfd = -1;
fd_set readset, tempfds;
fd_set writeset, tempwrtfds;
int sock_fd, maxfdp1;
struct sockaddr_in webnet_saddr;
struct timeval rcv_to = {0, 50000};
/* First acquire our socket for listening for connections */
listenfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (listenfd < 0)
{
printf("Create socket failed.");
goto __exit;
}
memset(&webnet_saddr, 0, sizeof(webnet_saddr));
webnet_saddr.sin_family = AF_INET;
webnet_saddr.sin_addr.s_addr = htonl(INADDR_ANY);
webnet_saddr.sin_port = htons(webnet_port); /* webnet server port */
/* Set receive timeout for accept() */
if(setsockopt(listenfd, SOL_SOCKET, SO_RCVTIMEO, (void*)&rcv_to, sizeof(rcv_to)) == -1)
{
printf("Set SO_RCVTIMEO failed, errno=%d\n", errno);
goto __exit;
}
if (bind(listenfd, (struct sockaddr *) &webnet_saddr, sizeof(webnet_saddr)) == -1)
{
printf("Bind socket failed, errno=%d\n", errno);
goto __exit;
}
/* Put socket into listening mode */
if (listen(listenfd, WEBNET_CONN_MAX) == -1)
{
printf("Socket listen(%d) failed.", WEBNET_CONN_MAX);
goto __exit;
}
/* initialize module (no session at present) */
webnet_module_handle_event(NULL, WEBNET_EVENT_INIT);
/* Wait forever for network input: This could be connections or data */
for (;;)
{
/* Determine what sockets need to be in readset */
FD_ZERO(&readset);
FD_ZERO(&writeset);
FD_SET(listenfd, &readset);
/* set fds in each sessions */
maxfdp1 = webnet_sessions_set_fds(&readset, &writeset);
if (maxfdp1 < listenfd + 1)
{
maxfdp1 = listenfd + 1;
}
/* use temporary fd set in select */
tempfds = readset;
tempwrtfds = writeset;
/* Wait for data or a new connection */
sock_fd = select(maxfdp1, &tempfds, &tempwrtfds, 0, 0);
if (sock_fd == 0)
{
continue;
}
/* At least one descriptor is ready */
if (FD_ISSET(listenfd, &tempfds))
{
struct webnet_session* accept_session;
/* We have a new connection request */
accept_session = webnet_session_create(listenfd);
if (accept_session == NULL)
{
/* create session failed, just accept and then close */
int sock;
struct sockaddr cliaddr;
socklen_t clilen;
clilen = sizeof(struct sockaddr_in);
sock = accept(listenfd, &cliaddr, &clilen);
if (sock >= 0)
{
closesocket(sock);
}
}
else
{
/* add read fdset */
FD_SET(accept_session->socket, &readset);
}
}
webnet_sessions_handle_fds(&tempfds, &writeset);
}
__exit:
if (listenfd >= 0)
{
closesocket(listenfd);
}
}
int webnet_init(void)
{
int result = 0;
pthread_t tid;
pthread_attr_t attr;
struct sched_param prio;
prio.sched_priority = WEBNET_PRIORITY;
size_t stack_size = WEBNET_THREAD_STACKSIZE;
pthread_attr_init(&attr);
pthread_attr_setschedparam(&attr, &prio);
pthread_attr_setstacksize(&attr, stack_size);
if (init_ok == TRUE)
{
printf("XiUOS webnet package is already initialized.");
return 0;
}
result = pthread_create(&tid, &attr, webnet_thread, NULL);
if (0 == result)
{
init_ok = TRUE;
printf("XiUOS webnet initialize success.");
}
else
{
printf("XiUOS webnet initialize failed.");
return -1;
}
return 0;
}

85
src/wn_mimetype.c Normal file
View File

@ -0,0 +1,85 @@
/*
* File : wn_mimetype.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 <wn_utils.h>
struct webnet_mime_entry
{
const char* name;
const char* type;
};
static const struct webnet_mime_entry mime_tables[] =
{
{ "any", "application/binary" }, /* default. */
{ "html", "text/html" },
{ "htm", "text/html" },
{ "css", "text/css" },
{ "txt", "text/plain" },
{ "pdf", "application/pdf" },
{ "gif", "image/gif" },
{ "png", "image/png" },
{ "jpeg", "image/jpeg" },
{ "jpg", "image/jpeg" },
{ "svg", "image/svg+xml" },
{ "avi", "video/x-msvideo" },
{ "mp3", "audio/mpeg" },
{ "ogg", "audio/x-oggvorbis" },
{ "wav", "audio/x-wav" },
{ "class", "application/octet-stream" },
{ "js", "application/x-javascript" },
{ "tar", "application/x-tar" },
{ "zip", "application/zip" },
{ "xml", "text/xml" },
{ "json", "application/json" },
{ NULL, NULL }
};
/**
* get mime type according to URL
*/
const char* mime_get_type(const char* url)
{
uint32 index;
index = 0;
if (url == NULL)
{
return mime_tables[0].type;
}
while (mime_tables[index].name != NULL)
{
if (str_end_with(url, mime_tables[index].name))
{
return mime_tables[index].type;
}
index++;
}
/* return text/html as default */
return mime_tables[1].type;
}

581
src/wn_module.c Normal file
View File

@ -0,0 +1,581 @@
/*
* 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);
}

950
src/wn_request.c Normal file
View File

@ -0,0 +1,950 @@
/*
* File : wn_request.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 <string.h>
#include <stdlib.h>
#include <webnet.h>
#include <wn_request.h>
#include <wn_utils.h>
#define POST_BUFSZ_MAX (8 * 1024)
/**
* parse a query
*/
static void _webnet_request_parse_query(struct webnet_request* request)
{
char *ptr, *end_ptr;
uint32 index;
if ((request->query == NULL) || (*request->query == '\0')) return; /* no query */
/* get the query counter */
ptr = request->query;
end_ptr = request->query + strlen(request->query);
request->query_counter = 1;
while (*ptr && ptr <= end_ptr)
{
if (*ptr == '&')
{
while ((*ptr == '&') && (*ptr != '\0')) ptr ++;
if (*ptr == '\0') break;
request->query_counter ++;
}
ptr ++;
}
if (request->query_counter == 0) return; /* no query */
/* allocate query item */
request->query_items = (struct webnet_query_item*) wn_malloc (sizeof(struct webnet_query_item)
* request->query_counter);
if (request->query_items == NULL)
{
request->result_code = 500;
return;
}
/* parse the query */
ptr = request->query;
for (index = 0; index < request->query_counter; index ++)
{
request->query_items[index].name = ptr;
request->query_items[index].value = NULL;
/* get value or goto next item */
while ((*ptr != '&') && (*ptr != '\0'))
{
/* get value */
if (*ptr == '=')
{
*ptr = '\0';
ptr ++;
request->query_items[index].value = ptr;
}
else ptr ++;
}
if (*ptr == '\0')
{
urldecode(request->query_items[index].value, ptr - request->query_items[index].value);
break;
}
if (*ptr == '&')
{
/* make a item */
*ptr = '\0';
urldecode(request->query_items[index].value, ptr - request->query_items[index].value);
ptr ++;
while (*ptr == '&' && *ptr != '\0' && ptr <= end_ptr)ptr ++;
if (*ptr == '\0') break;
}
}
}
/**
* copy string from request and the field_copied set to TRUE
*/
static void _webnet_request_copy_str(struct webnet_request* request)
{
if (request->path != NULL) request->path = wn_strdup(request->path);
if (request->host != NULL)
{
char *ptr;
ptr = request->host;
while (*ptr && *ptr != ':') ptr ++;
if (*ptr == ':') *ptr = '\0';
request->host = wn_strdup(request->host);
}
if (request->cookie != NULL) request->cookie = wn_strdup(request->cookie);
if (request->user_agent != NULL) request->user_agent = wn_strdup(request->user_agent);
if (request->authorization != NULL) request->authorization = wn_strdup(request->authorization);
if (request->accept_language != NULL) request->accept_language = wn_strdup(request->accept_language);
if (request->referer != NULL) request->referer = wn_strdup(request->referer);
if (request->content_type != NULL) request->content_type = wn_strdup(request->content_type);
/* DMR */
if (request->callback) request->callback = wn_strdup(request->callback);
if (request->soap_action) request->soap_action = wn_strdup(request->soap_action);
if (request->sid) request->sid = wn_strdup(request->sid);
/* DAV */
#ifdef WEBNET_USING_DAV
if (request->depth) request->depth = wn_strdup(request->depth);
#endif
#ifdef WEBNET_USING_RANGE
if (request->Range) request->Range = wn_strdup(request->Range);
#endif /* WEBNET_USING_RANGE */
request->field_copied = TRUE;
}
/**
* to check whether a query on the http request.
*/
bool webnet_request_has_query(struct webnet_request* request, char* name)
{
uint32 index;
for (index = 0; index < request->query_counter; index ++)
{
if (strncmp(request->query_items[index].name, name, strlen(name)) == 0)
return TRUE;
}
return FALSE;
}
#ifdef ADD_RTTHREAD_FETURES
RTM_EXPORT(webnet_request_has_query);
#endif
/**
* get query value according to the name
*/
const char* webnet_request_get_query(struct webnet_request* request, char* name)
{
uint32 index;
for (index = 0; index < request->query_counter; index ++)
{
if (strncmp(request->query_items[index].name, name, strlen(name)) == 0 &&
strlen(name) == strlen(request->query_items[index].name))
return request->query_items[index].value;
}
return NULL;
}
#ifdef ADD_RTTHREAD_FETURES
RTM_EXPORT(webnet_request_get_query);
#endif
struct web_method
{
const char *method_str;
enum webnet_method method_value;
};
const struct web_method methods [] = {
{"GET ", WEBNET_GET},
{"POST ", WEBNET_POST},
{"HEADER ", WEBNET_HEADER},
{"HEAD ", WEBNET_HEAD},
{"PUT ", WEBNET_PUT},
{"OPTIONS ", WEBNET_OPTIONS},
{"PROPFIND ", WEBNET_PROPFIND},
{"PROPPATCH ", WEBNET_PROPPATCH},
{"DELETE ", WEBNET_DELETE},
{"CONNECT ", WEBNET_CONNECT},
{"MKCOL ", WEBNET_MKCOL},
{"MOVE ", WEBNET_MOVE},
{"SUBSCRIBE ", WEBNET_SUBSCRIBE},
{"UNSUBSCRIBE ", WEBNET_UNSUBSCRIBE},
{"NOTIFY ", WEBNET_NOTIFY},
{NULL, WEBNET_UNKNOWN},
};
int webnet_request_parse_method(struct webnet_request *request, char* buffer, int length)
{
char *ch;
int index;
char *request_buffer;
const struct web_method *method;
assert(request != NULL);
assert(request->session != NULL);
request_buffer = buffer;
if (strstr(request_buffer, "\r\n") == NULL) return 0;
/* parse method */
for (index = 0; ; index ++)
{
method = &methods[index];
if (method->method_value == WEBNET_UNKNOWN)
{
/* Not implemented for other method */
request->result_code = 501;
return 0;
}
if (str_begin_with(request_buffer, method->method_str))
{
request->method = method->method_value;
request_buffer += strlen(method->method_str);
break;
}
}
/* get path */
ch = strchr(request_buffer, ' ');
if (ch == NULL)
{
/* Bad Request */
request->result_code = 400;
return request_buffer - buffer;
}
*ch++ = '\0';
request->path = wn_strdup(request_buffer);
request_buffer = ch;
/* check path, whether there is a query */
ch = strchr(request->path, '?');
if (ch != NULL)
{
*ch++ = '\0';
while (*ch == ' ') ch ++;
/* copy query and parse query */
request->query = wn_strdup(ch);
/* copy query and parse parameter */
_webnet_request_parse_query(request);
}
/* check protocol */
if (!str_begin_with(request_buffer, "HTTP/1"))
{
/* Not implemented, webnet does not support HTTP 0.9 protocol */
request->result_code = 501;
return request_buffer - buffer;
}
ch = strstr(request_buffer, "\r\n");
*ch ++ = '\0';
*ch ++ = '\0';
request_buffer = ch;
/* move to next phase */
request->session->session_phase = WEB_PHASE_HEADER;
return request_buffer - buffer;
}
int webnet_request_parse_header(struct webnet_request *request, char* buffer, int length)
{
char *ch;
char *request_buffer;
struct webnet_session *session;
assert(request != NULL);
assert(request->session != NULL);
session = request->session;
request_buffer = buffer;
for (;;)
{
if (str_begin_with(request_buffer, "\r\n"))
{
/* end of http request */
request_buffer += 2;
if (request->content_length && request->method == WEBNET_POST)
{
session->session_phase = WEB_PHASE_RESPONSE;
if (!str_begin_with(request->content_type, "multipart/form-data") &&
request->content_length < POST_BUFSZ_MAX)
{
session->session_phase = WEB_PHASE_QUERY;
/* allocate query buffer */
if(request->query != NULL) {
wn_free(request->query);
}
request->query = (char*) wn_malloc(request->content_length + 1);
rt_memset(request->query, 0, request->content_length + 1);
request->query_offset = 0;
}
}
else
{
/* end of http request */
request->result_code = 200;
/* move to next phase */
session->session_phase = WEB_PHASE_RESPONSE;
}
break;
}
if (*request_buffer == '\0')
{
/* not the end of http request */
return request_buffer - buffer;
}
ch = strstr(request_buffer, "\r\n");
if (ch == NULL)
{
/* not the end of http header request line */
return request_buffer - buffer;
}
/* set terminal field */
*ch ++ = '\0';
*ch ++ = '\0';
if (str_begin_with(request_buffer, "Host:"))
{
/* get host */
request_buffer += 5;
while (*request_buffer == ' ') request_buffer ++;
request->host = wn_strdup(request_buffer);
}
else if (str_begin_with(request_buffer, "User-Agent:"))
{
/* get user agent */
request_buffer += 11;
while (*request_buffer == ' ') request_buffer ++;
request->user_agent = wn_strdup(request_buffer);
}
else if (str_begin_with(request_buffer, "Accept-Language:"))
{
/* get accept language */
request_buffer += 16;
while (*request_buffer == ' ') request_buffer ++;
request->accept_language = wn_strdup(request_buffer);
}
else if (str_begin_with(request_buffer, "Content-Length:"))
{
/* get content length */
request_buffer += 15;
while (*request_buffer == ' ') request_buffer ++;
request->content_length = atoi(request_buffer);
}
else if (str_begin_with(request_buffer, "Content-Type:"))
{
/* get content type */
request_buffer += 13;
while (*request_buffer == ' ') request_buffer ++;
request->content_type = wn_strdup(request_buffer);
}
else if (str_begin_with(request_buffer, "Referer:"))
{
/* get referer */
request_buffer += 8;
while (*request_buffer == ' ') request_buffer ++;
request->referer = wn_strdup(request_buffer);
}
#ifdef WEBNET_USING_RANGE
else if (str_begin_with(request_buffer, "Range:"))
{
/* get range */
request_buffer += 6;
while (*request_buffer == ' ') request_buffer ++;
request->Range = wn_strdup(request_buffer);
}
#endif /* WEBNET_USING_RANGE */
#ifdef WEBNET_USING_DAV
else if(str_begin_with(request_buffer, "Depth:"))
{
request_buffer += 6;
while (*request_buffer == ' ') request_buffer ++;
request->depth = wn_strdup(request_buffer);
}
else if (str_begin_with(request_buffer, "Destination:"))
{
request_buffer += 12;
while (*request_buffer == ' ') request_buffer ++;
request->destination = wn_strdup(request_buffer);
}
#endif /* WEBNET_USING_DAV */
#ifdef WEBNET_USING_KEEPALIVE
else if (str_begin_with(request_buffer, "Connection:"))
{
/* set default connection to keep alive */
request->connection = WEBNET_CONN_KEEPALIVE;
/* get connection */
request_buffer += 11;
while (*request_buffer == ' ') request_buffer ++;
if (str_begin_with(request_buffer, "close"))
request->connection = WEBNET_CONN_CLOSE;
else if (str_begin_with(request_buffer, "Keep-Alive"))
request->connection = WEBNET_CONN_KEEPALIVE;
}
#endif
#ifdef WEBNET_USING_COOKIE
else if (str_begin_with(request_buffer, "Cookie:"))
{
/* get cookie */
request_buffer += 7;
while (*request_buffer == ' ') request_buffer ++;
request->cookie = wn_strdup(request_buffer);
}
#endif /* WEBNET_USING_COOKIE */
#ifdef WEBNET_USING_AUTH
else if (str_begin_with(request_buffer, "Authorization: Basic"))
{
/* get authorization */
request_buffer += 20;
while (*request_buffer == ' ') request_buffer ++;
request->authorization = wn_strdup(request_buffer);
}
#endif /* WEBNET_USING_AUTH */
#if WEBNET_CACHE_LEVEL > 0
else if (str_begin_with(request_buffer, "If-Modified-Since:"))
{
/* get If-Modified-Since */
request_buffer += 18;
while (*request_buffer == ' ') request_buffer ++;
request->modified = wn_strdup(request_buffer);
}
#endif /* WEBNET_CACHE_LEVEL > 0 */
#ifdef WEBNET_USING_GZIP
else if (str_begin_with(request_buffer, "Accept-Encoding:"))
{
const char *gzip = strstr(request_buffer, "gzip");
if( (gzip != NULL))
{
request->support_gzip = TRUE;
}
}
#endif /* WEBNET_USING_GZIP */
else if (str_begin_with(request_buffer, "SOAPACTION:"))
{
request_buffer += 11;
while (*request_buffer == ' ') request_buffer ++;
request->soap_action = wn_strdup(request_buffer);
}
else if (str_begin_with(request_buffer, "CALLBACK:"))
{
request_buffer += 9;
while (*request_buffer == ' ') request_buffer ++;
request->callback = wn_strdup(request_buffer);
}
request_buffer = ch;
}
return request_buffer - buffer;
}
int webnet_request_parse_post(struct webnet_request* request, char* buffer, int length)
{
struct webnet_session* session = request->session;
if (request->query && length)
{
if (request->query_offset + length > request->content_length)
length = request->content_length - request->query_offset;
rt_memcpy(&request->query[request->query_offset], buffer, length);
request->query_offset += length;
if (request->query_offset == request->content_length)
{
/* set terminal charater */
buffer[request->content_length] = '\0';
/* parse query */
if (str_begin_with(request->content_type, "application/x-www-form-urlencoded"))
{
_webnet_request_parse_query(request);
}
/* set to http response phase */
request->result_code = 200;
session->session_phase = WEB_PHASE_RESPONSE;
}
}
return length;
}
/**
* parse web request
*/
void webnet_request_parse(struct webnet_request* request, char* buffer, int length)
{
char *ch;
char *request_buffer;
char *content_length;
assert(request != NULL);
assert(request->session != NULL);
content_length = NULL;
request_buffer = buffer;
/* web request begin with method */
if (str_begin_with(request_buffer, "GET "))
{
request->method = WEBNET_GET;
request_buffer += 4;
}
else if (str_begin_with(request_buffer, "POST "))
{
request->method = WEBNET_POST;
request_buffer += 5;
}
else if (str_begin_with(request_buffer, "HEADER "))
{
request->method = WEBNET_HEADER;
request_buffer += 7;
}
else if (str_begin_with(request_buffer, "SUBSCRIBE "))
{
request->method = WEBNET_SUBSCRIBE;
request_buffer += 10;
}
else if (str_begin_with(request_buffer, "UNSUBSCRIBE "))
{
request->method = WEBNET_UNSUBSCRIBE;
request_buffer += 12;
}
else if (str_begin_with(request_buffer, "NOTIFY "))
{
request->method = WEBNET_NOTIFY;
request_buffer += 7;
}
#ifdef WEBNET_USING_DAV
else if (str_begin_with(request_buffer, "PUT "))
{
request->method = WEBNET_PUT;
request_buffer += 4;
}
else if(str_begin_with(request_buffer, "OPTIONS "))
{
request->method = WEBNET_OPTIONS;
request_buffer += 8;
}
else if(str_begin_with(request_buffer, "PROPFIND "))
{
request->method = WEBNET_PROPFIND;
request_buffer += 9;
}
else if(str_begin_with(request_buffer, "PROPPATCH "))
{
request->method = WEBNET_PROPPATCH;
request_buffer += 10;
}
else if (str_begin_with(request_buffer, "DELETE "))
{
request->method = WEBNET_DELETE;
request_buffer += 7;
}
else if (str_begin_with(request_buffer, "MKCOL "))
{
request->method = WEBNET_MKCOL;
request_buffer += 6;
}
else if (str_begin_with(request_buffer, "HEAD "))
{
request->method = WEBNET_HEAD;
request_buffer += 5;
}
#endif /* WEBNET_USING_DAV */
else
{
/* Not implemented for other method */
request->result_code = 501;
return ;
}
/* get path */
request->path = request_buffer;
ch = strchr(request_buffer, ' ');
if (ch == NULL)
{
/* Bad Request */
request->result_code = 400;
return;
}
*ch++ = '\0';
request_buffer = ch;
/* check path, whether there is a query */
ch = strchr(request->path, '?');
if (ch != NULL)
{
*ch++ = '\0';
while (*ch == ' ') ch ++;
/* copy query and parse query */
request->query = wn_strdup(ch);
/* copy query and parse parameter */
_webnet_request_parse_query(request);
}
/* check protocol */
if (!str_begin_with(request_buffer, "HTTP/1"))
{
/* Not implemented, webnet does not support HTTP 0.9 protocol */
request->result_code = 501;
return;
}
ch = strstr(request_buffer, "\r\n");
if (ch == NULL)
{
/* Bad Request */
request->result_code = 400;
return;
}
*ch ++ = '\0';
*ch ++ = '\0';
request_buffer = ch;
for (;;)
{
if (str_begin_with(request_buffer, "\r\n"))
{
/* end of get request */
/* made a string field copy */
_webnet_request_copy_str(request);
/* get content length */
if (content_length != NULL)
request->content_length = atoi(content_length);
if (request->method == WEBNET_POST && request->content_length > 0) /* POST method */
{
char *read_ptr = NULL;
int read_length;
struct webnet_session* session = request->session;
/* skip '\r\n' */
request->query = request_buffer + 2;
/* check whether it's an uploading request */
if (str_begin_with(request->content_type, "multipart/form-data"))
{
/* end of http request */
request->result_code = 200;
session->buffer_offset = (rt_uint16_t)(length -
((uint32)request->query - (uint32)buffer));
/* move the buffer to the session */
if (session->buffer_offset > 0)
{
/*
* NOTIC: the rest of buffer must not be great than session buffer
* Therefore, the size of read buffer can equal to the size of session buffer.
*/
rt_memcpy(session->buffer, request->query, session->buffer_offset);
}
request->query = NULL;
return;
}
if (request->content_length > POST_BUFSZ_MAX)
{
printf("content length too long: %d, discard it.", request->content_length);
/* we can not provide enough buffer for post */
request->query = NULL;
return;
}
/* allocate a new query content and copy the already read content */
read_ptr = (char *)wn_malloc(request->content_length);
if (read_ptr == NULL)
{
printf("No memory for request read buffer!");
request->query = NULL;
return ;
}
/* get the length of already read bytes */
read_length = (int)(length - ((uint32)request->query - (uint32)buffer));
if (read_length > 0)
{
if (read_length > request->content_length) read_length = request->content_length;
rt_memcpy(read_ptr, request->query, read_length);
}
request->query = read_ptr;
read_ptr += read_length;
/* read all of post content */
while (read_length < request->content_length)
{
int read_bytes;
/* read the rest content of POST */
read_bytes = webnet_session_read(session, read_ptr, request->content_length - read_length);
if (read_bytes < 0)
{
/* read failed and session should been closed. */
wn_free(request->query);
request->query = NULL;
return;
}
read_length += read_bytes;
read_ptr += read_bytes;
}
session->buffer_offset = request->content_length;
if (!str_begin_with(request->content_type, "text/xml"))
_webnet_request_parse_query(request);
else
{
/* do other POST action */
}
}
/* end of http request */
request->result_code = 200;
return;
}
if (*request_buffer == '\0')
{
/* end of http request */
request->result_code = 200;
/* made a string field copy */
_webnet_request_copy_str(request);
return;
}
if (str_begin_with(request_buffer, "Host:"))
{
/* get host */
request_buffer += 5;
while (*request_buffer == ' ') request_buffer ++;
request->host = request_buffer;
}
else if (str_begin_with(request_buffer, "User-Agent:"))
{
/* get user agent */
request_buffer += 11;
while (*request_buffer == ' ') request_buffer ++;
request->user_agent = request_buffer;
}
else if (str_begin_with(request_buffer, "Accept-Language:"))
{
/* get accept language */
request_buffer += 16;
while (*request_buffer == ' ') request_buffer ++;
request->accept_language = request_buffer;
}
else if (str_begin_with(request_buffer, "Content-Length:"))
{
/* get content length */
request_buffer += 15;
while (*request_buffer == ' ') request_buffer ++;
content_length = request_buffer;
}
else if (str_begin_with(request_buffer, "Content-Type:"))
{
/* get content type */
request_buffer += 13;
while (*request_buffer == ' ') request_buffer ++;
request->content_type = request_buffer;
}
else if (str_begin_with(request_buffer, "Referer:"))
{
/* get referer */
request_buffer += 8;
while (*request_buffer == ' ') request_buffer ++;
request->referer = request_buffer;
}
#ifdef WEBNET_USING_DAV
else if(str_begin_with(request_buffer, "Depth:"))
{
request_buffer += 6;
while (*request_buffer == ' ') request_buffer ++;
request->depth = request_buffer;
}
#endif /* WEBNET_USING_DAV */
#ifdef WEBNET_USING_KEEPALIVE
else if (str_begin_with(request_buffer, "Connection:"))
{
/* set default connection to keep alive */
request->connection = WEBNET_CONN_KEEPALIVE;
/* get connection */
request_buffer += 11;
while (*request_buffer == ' ') request_buffer ++;
if (str_begin_with(request_buffer, "close"))
request->connection = WEBNET_CONN_CLOSE;
else if (str_begin_with(request_buffer, "Keep-Alive"))
request->connection = WEBNET_CONN_KEEPALIVE;
}
#endif
#ifdef WEBNET_USING_COOKIE
else if (str_begin_with(request_buffer, "Cookie:"))
{
/* get cookie */
request_buffer += 7;
while (*request_buffer == ' ') request_buffer ++;
request->cookie = request_buffer;
}
#endif /* WEBNET_USING_COOKIE */
#ifdef WEBNET_USING_AUTH
else if (str_begin_with(request_buffer, "Authorization: Basic"))
{
/* get authorization */
request_buffer += 20;
while (*request_buffer == ' ') request_buffer ++;
request->authorization = request_buffer;
}
#endif /* WEBNET_USING_AUTH */
#if WEBNET_CACHE_LEVEL > 0
else if (str_begin_with(request_buffer, "If-Modified-Since:"))
{
/* get If-Modified-Since */
request_buffer += 18;
while (*request_buffer == ' ') request_buffer ++;
request->modified = request_buffer;
}
#endif /* WEBNET_CACHE_LEVEL > 0 */
#ifdef WEBNET_USING_GZIP
else if (str_begin_with(request_buffer, "Accept-Encoding:"))
{
const char *end = strstr(request_buffer, "\r\n");
const char *gzip = strstr(request_buffer, "gzip");
if( (gzip != NULL) && (end != NULL) && (gzip < end) )
{
request->support_gzip = TRUE;
}
}
#endif /* WEBNET_USING_GZIP */
else if (str_begin_with(request_buffer, "SOAPACTION:"))
{
request_buffer += 11;
while (*request_buffer == ' ') request_buffer ++;
request->soap_action = request_buffer;
}
else if (str_begin_with(request_buffer, "CALLBACK:"))
{
request_buffer += 9;
while (*request_buffer == ' ') request_buffer ++;
request->callback = request_buffer;
}
ch = strstr(request_buffer, "\r\n");
if (ch == NULL)
{
/* Bad Request */
request->result_code = 400;
return;
}
/* terminal field */
*ch ++ = '\0';
*ch ++ = '\0';
request_buffer = ch;
}
}
struct webnet_request* webnet_request_create()
{
struct webnet_request* request;
request = (struct webnet_request*) wn_malloc (sizeof(struct webnet_request));
if (request != NULL)
{
rt_memset(request, 0, sizeof(struct webnet_request));
request->field_copied = FALSE;
}
return request;
}
void webnet_request_destory(struct webnet_request* request)
{
if (request != NULL)
{
// if (request->field_copied == TRUE)
{
if (request->path != NULL) wn_free(request->path);
if (request->host != NULL) wn_free(request->host);
if (request->cookie != NULL) wn_free(request->cookie);
if (request->user_agent != NULL) wn_free(request->user_agent);
if (request->authorization != NULL) wn_free(request->authorization);
if (request->accept_language != NULL) wn_free(request->accept_language);
if (request->referer != NULL) wn_free(request->referer);
if (request->content_type != NULL) wn_free(request->content_type);
if (request->query != NULL) wn_free(request->query);
if (request->query_items != NULL) wn_free(request->query_items);
if (request->callback) wn_free(request->callback);
if (request->soap_action) wn_free(request->soap_action);
if (request->sid) wn_free(request->sid);
#if (WEBNET_CACHE_LEVEL > 0)
if(request->modified) wn_free(request->modified);
#endif
#ifdef WEBNET_USING_RANGE
if (request->Range) wn_free(request->Range);
#endif /* WEBNET_USING_RANGE */
#ifdef WEBNET_USING_DAV
if (request->depth) wn_free(request->depth);
#endif
}
/* free request memory block */
wn_free(request);
}
}

642
src/wn_session.c Normal file
View File

@ -0,0 +1,642 @@
/*
* File : wn_session.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 <stdint.h>
#include <stdio.h>
#include <string.h>
#include <webnet.h>
#include <wn_module.h>
#include <wn_utils.h>
static struct webnet_session *_session_list = 0;
static void (*webnet_err_callback)(struct webnet_session *session);
/**
* create a webnet session
*
* @param listenfd, the listen file descriptor
*
* @return the created web session
*/
struct webnet_session* webnet_session_create(int listenfd)
{
struct webnet_session* session;
/* create a new session */
session = (struct webnet_session *)wn_malloc(sizeof(struct webnet_session));
if (session != NULL)
{
socklen_t clilen;
memset(session, 0x0, sizeof(struct webnet_session));
session->session_ops = NULL;
clilen = sizeof(struct sockaddr_in);
session->socket = accept(listenfd, (struct sockaddr *) &(session->cliaddr), &clilen);
if (session->socket < 0)
{
wn_free(session);
session = NULL;
return session;
}
else
{
/* keep this session in our list */
session->next = _session_list;
_session_list = session;
}
/* initial buffer length */
session->buffer_length = WEBNET_SESSION_BUFSZ;
}
return session;
}
/**
* read data from a webnet session
*
* @param session, the web session
* @param buffer, the buffer to save read data
* @param length, the maximal length of data buffer to save read data
*
* @return the number of bytes actually read data
*/
int webnet_session_read(struct webnet_session *session, char *buffer, int length)
{
int read_count;
/* Read some data */
read_count = recvfrom(session->socket, buffer, length, 0, NULL, NULL);
if (read_count <= 0)
{
session->session_phase = WEB_PHASE_CLOSE;
return -1;
}
return read_count;
}
#ifdef ADD_RTTHREAD_FETURES
RTM_EXPORT(webnet_session_read);
#endif
/**
* close a webnet session
*
* @param session, the web session
*/
void webnet_session_close(struct webnet_session *session)
{
struct webnet_session *iter;
/* invoke session close */
if (session->session_ops != NULL &&
session->session_ops->session_close != NULL)
{
session->session_ops->session_close(session);
}
/* Either an error or tcp connection closed on other
* end. Close here */
closesocket(session->socket);
/* Free webnet_session */
if (_session_list == session)
_session_list = session->next;
else
{
for (iter = _session_list; iter; iter = iter->next)
{
if (iter->next == session)
{
iter->next = session->next;
break;
}
}
}
if (session->request != NULL)
{
webnet_request_destory(session->request);
session->request = NULL;
}
wn_free(session);
}
/**
* print formatted data to session
*
* @param session, the web session
* @param fmt, the format string
*/
void webnet_session_printf(struct webnet_session *session, const char* fmt, ...)
{
va_list args;
uint32 length;
va_start(args, fmt);
length = vsnprintf((char*)(session->buffer),
sizeof(session->buffer) - 1,
fmt, args);
session->buffer[length] = '\0';
va_end(args);
send(session->socket, session->buffer, length, 0);
}
#ifdef ADD_RTTHREAD_FETURES
RTM_EXPORT(webnet_session_printf);
#endif
/**
* write data to session
*
* @param session, the web session
* @param data, the data will be write to the session
* @param size, the size of data bytes
*
* @return the number of bytes actually written to the session
*/
int webnet_session_write(struct webnet_session *session, const uint8* data, uint32 size)
{
/* send data directly */
send(session->socket, data, size, 0);
return size;
}
#ifdef ADD_RTTHREAD_FETURES
RTM_EXPORT(webnet_session_write);
#endif
/**
* redirect to another local URL
*
* @param session, the web session
* @param url, the new URL link on the local
*
* @return
*/
int webnet_session_redirect(struct webnet_session *session, const char* url)
{
struct webnet_request *request;
assert(session != NULL);
request = session->request;
assert(request != NULL);
/* change the request path to URL */
if (request->path != NULL)
wn_free(request->path);
request->path = wn_strdup(url);
/* handle this URL */
return webnet_module_handle_uri(session);
}
#ifdef ADD_RTTHREAD_FETURES
RTM_EXPORT(webnet_session_redirect);
#endif
/**
* Get physical path according to a virtual path
*
* @param session, the webnet session
* @param virtual_path, the virtual path
* @param full_path, the output full path
*
* @return 0 on convert successfull.
*
* NOTE: the length of full path is WEBNET_PATH_MAX */
int webnet_session_get_physical_path(struct webnet_session *session, const char* virtual_path, char* full_path)
{
int result;
result = 0;
if (full_path == NULL) return -1;
/* made a full path */
snprintf(full_path, WEBNET_PATH_MAX, "%s/%s", webnet_get_root(), virtual_path);
/* normalize path */
str_normalize_path(full_path);
/* check URI valid */
if (!str_begin_with(full_path, webnet_get_root()))
{
/* not found */
result = -1;
}
return result;
}
#ifdef ADD_RTTHREAD_FETURES
RTM_EXPORT(webnet_session_get_physical_path);
#endif
/**
* set the http response header field Status-Line.
*
* @param session the web session
* @param code the http response code
* @param reason_phrase reason phrase string
*/
void webnet_session_set_header_status_line(struct webnet_session *session,
int code,
const char * reason_phrase)
{
char status_line[16]; /* "HTTP/1.1 ### " */
snprintf(status_line, sizeof(status_line) - 1, "HTTP/1.1 %d ", code);
webnet_session_printf(session, status_line);
webnet_session_printf(session, reason_phrase);
webnet_session_printf(session, "\r\n");
webnet_session_printf(session, WEBNET_SERVER);
}
#ifdef ADD_RTTHREAD_FETURES
RTM_EXPORT(webnet_session_set_header_status_line);
#endif
/**
* set the http response header
*
* @param session the web session
* @param mimetype the mime type of http response
* @param code the http response code
* @param title the code title string
* @param length the length of http response content
*/
void webnet_session_set_header(struct webnet_session *session, const char* mimetype, int code, const char* title, int length)
{
static const char* fmt = "HTTP/1.1 %d %s\r\n%s";
static const char* content = "Content-Type: %s\r\nContent-Length: %ld\r\nConnection: %s\r\n\r\n";
static const char* content_nolength = "Content-Type: %s\r\nConnection: %s\r\n\r\n";
static const char* auth = "WWW-Authenticate: Basic realm=%s\r\n";
char *ptr, *end_buffer;
int offset;
ptr = (char*)session->buffer;
end_buffer = (char*)session->buffer + session->buffer_length;
offset = snprintf(ptr, end_buffer - ptr, fmt, code, title, WEBNET_SERVER);
ptr += offset;
if (code == 401)
{
offset = snprintf(ptr, end_buffer - ptr, auth, session->request->host);
ptr += offset;
}
if (length >= 0)
{
offset = snprintf(ptr, end_buffer - ptr, content, mimetype, length,
session->request->connection == WEBNET_CONN_CLOSE? "close" : "Keep-Alive");
ptr += offset;
}
else
{
offset = snprintf(ptr, end_buffer - ptr, content_nolength, mimetype, "close");
ptr += offset;
}
/* get the total length */
length = ptr - (char*)session->buffer;
/* invoke webnet event */
if (webnet_module_handle_event(session, WEBNET_EVENT_RSP_HEADER) == WEBNET_MODULE_CONTINUE)
{
/* write to session */
webnet_session_write(session, session->buffer, length);
}
}
#ifdef ADD_RTTHREAD_FETURES
RTM_EXPORT(webnet_session_set_header);
#endif
static void _webnet_session_handle_read(struct webnet_session* session)
{
int read_length;
uint8 *buffer_ptr;
buffer_ptr = &session->buffer[session->buffer_offset];
/* to read data from the socket */
read_length = webnet_session_read(session, (char*)buffer_ptr, session->buffer_length - session->buffer_offset);
if (read_length > 0) session->buffer_offset += read_length;
if (session->buffer_offset)
{
/* parse web method phase */
if (session->session_phase == WEB_PHASE_METHOD)
{
int length = webnet_request_parse_method(session->request, (char*)&session->buffer[0],
session->buffer_offset);
if (length)
{
if (length < session->buffer_offset)
{
session->buffer_offset -= length;
/* move to the begining of buffer */
memmove(session->buffer, &session->buffer[length], session->buffer_offset);
}
else session->buffer_offset = 0;
}
}
/* parse web request header phase */
if (session->session_phase == WEB_PHASE_HEADER)
{
int length = webnet_request_parse_header(session->request, (char*)&session->buffer[0],
session->buffer_offset);
if (length)
{
if (length < session->buffer_offset)
{
session->buffer_offset -= length;
/* move to the begining of buffer */
memmove(session->buffer, &session->buffer[length], session->buffer_offset);
}
else session->buffer_offset = 0;
}
}
if (session->session_phase == WEB_PHASE_QUERY)
{
int length = webnet_request_parse_post(session->request, (char*)&session->buffer[0],
session->buffer_offset);
if (length)
{
if (length < session->buffer_offset)
{
session->buffer_offset -= length;
/* move to the begining of buffer */
memmove(session->buffer, &session->buffer[length], session->buffer_offset);
}
else session->buffer_offset = 0;
}
}
}
}
static void _webnet_session_handle_write(struct webnet_session* session)
{
}
static void _webnet_session_handle(struct webnet_session* session, int event)
{
switch (event)
{
case WEBNET_EVENT_READ:
_webnet_session_handle_read(session);
/* in the response phase */
if (session->session_phase == WEB_PHASE_RESPONSE)
{
/* remove the default session ops */
session->session_ops = NULL;
session->user_data = 0;
/* to handle response, then let module to handle url */
webnet_module_handle_uri(session);
}
break;
case WEBNET_EVENT_WRITE:
_webnet_session_handle_write(session);
break;
}
}
const struct webnet_session_ops _default_session_ops =
{
_webnet_session_handle,
NULL
};
static void _webnet_session_badrequest(struct webnet_session *session, int code)
{
const char* title;
static const char* fmt = "<html><head><title>%d %s</title></head><body>%d %s</body></html>\r\n";
title = "Unknown";
switch (code)
{
case 304:
title = "Not Modified";
break;
case 400:
title = "Bad Request";
break;
case 401:
title = "Authorization Required";
break;
case 403:
title = "Forbidden";
break;
case 404:
title = "Not Found";
break;
case 405:
title = "Method Not Allowed";
break;
case 500:
title = "Internal Server Error";
break;
case 501:
title = "Not Implemented";
break;
case 505:
title = "HTTP Version Not Supported";
break;
}
#ifdef WEBNET_USING_KEEPALIVE
if (code >= 400)
{
session->request->connection = WEBNET_CONN_CLOSE;
}
#endif
webnet_session_set_header(session, "text/html", code, title, -1);
webnet_session_printf(session, fmt, code, title, code, title);
}
/**
* set the file descriptors
*
* @param readset, the file descriptors set for read
* @param writeset, the file descriptors set for write
*
* @return the maximal file descriptor
*/
int webnet_sessions_set_fds(fd_set *readset, fd_set *writeset)
{
int maxfdp1 = 0;
struct webnet_session *session;
for (session = _session_list; session; session = session->next)
{
if (maxfdp1 < session->socket + 1)
maxfdp1 = session->socket + 1;
FD_SET(session->socket, readset);
if (session->session_event_mask & WEBNET_EVENT_WRITE)
FD_SET(session->socket, writeset);
}
return maxfdp1;
}
void webnet_sessions_set_err_callback(void (*callback)(struct webnet_session *session))
{
webnet_err_callback = callback;
}
/**
* handle the file descriptors request
*
* @param readset, the file descriptors set for read
* @param writeset, the file descriptors set for write
*/
void webnet_sessions_handle_fds(fd_set *readset, fd_set *writeset)
{
struct webnet_session *session, *next_session;
/* Go through list of connected session and process data */
for (session = _session_list; session; session = next_session)
{
/* get next session firstly if this session is closed */
next_session = session->next;
if (FD_ISSET(session->socket, readset))
{
if (session->session_ops == NULL)
{
struct webnet_request *request;
/* destroy old request */
if (session->request != NULL)
{
webnet_request_destory(session->request);
session->request = NULL;
}
/* create request and use the default session ops */
request = webnet_request_create();
if (request)
{
session->request = request;
session->session_phase = WEB_PHASE_METHOD;
/* set the default session ops */
session->session_ops = &_default_session_ops;
session->user_data = NULL;
request->session = session;
request->result_code = 200; /* set the default result code to 200 */
/* handle read event */
session->session_ops->session_handle(session, WEBNET_EVENT_READ);
}
else
{
/* no memory, close this session */
session->session_phase = WEB_PHASE_CLOSE;
}
}
else
{
if (session->session_ops->session_handle)
session->session_ops->session_handle(session, WEBNET_EVENT_READ);
}
/* whether close this session */
if (session->session_ops == NULL || session->session_phase == WEB_PHASE_CLOSE)
{
/* check result code */
if (session->request->result_code != 200)
{
/* do request err callback */
if (webnet_err_callback != NULL)
{
webnet_err_callback(session);
}
else
{
_webnet_session_badrequest(session, session->request->result_code);
}
}
/* close this session */
webnet_session_close(session);
}
}
else if (FD_ISSET(session->socket, writeset))
{
/* handle for write fd set */
if (session->session_ops != NULL &&
session->session_ops->session_handle != NULL)
{
session->session_ops->session_handle(session, WEBNET_EVENT_WRITE);
}
/* whether close this session */
if (session->session_ops == NULL || session->session_phase == WEB_PHASE_CLOSE)
{
/* close this session */
webnet_session_close(session);
}
}
}
}
#ifdef ADD_RTTHREAD_FETURES
#ifdef RT_USING_FINSH
#include <finsh.h>
static void list_webnet(void)
{
struct webnet_session *session;
char client_ip_str[16]; /* ###.###.###.### */
uint32 num = 0;
PrivTaskenterCritical();
for (session = _session_list; session != NULL; session = session->next)
{
strcpy(client_ip_str,
inet_ntoa(*((struct in_addr*)&(session->cliaddr.sin_addr))));
printf("#%u client %s:%u \n",
num++,
client_ip_str,
ntohs(session->cliaddr.sin_port));
if (session->request != NULL)
{
printf("path: %s\n", session->request->path);
}
printf("\r\n");
}
PrivTaskexitCritical();
}
FINSH_FUNCTION_EXPORT(list_webnet, list webnet session);
#ifdef FINSH_USING_MSH
MSH_CMD_EXPORT(list_webnet, list webnet session);
#endif
#endif /* RT_USING_FINSH */
#endif

342
src/wn_utils.c Normal file
View File

@ -0,0 +1,342 @@
/*
* File : wn_utils.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 <ctype.h>
#include <rtthread.h>
#include <webnet.h>
#include <wn_utils.h>
rt_inline int tohex(char c)
{
if (c >= '0' && c <= '9')
return c - '0';
if (c >= 'a' && c <= 'f')
return c - 'a' + 10;
if (c >= 'A' && c <= 'F')
return c - 'A' + 10;
return -1;
}
int str_path_with(const char *s, const char *t)
{
if ((strncasecmp(s, t, strlen(t)) == 0
&& (strlen(s) == strlen(t) || *(s + strlen(t)) == '/'))
||(strlen(t) == 1 && t[0] == '/')) return 1;
return 0;
}
int str_begin_with(const char *s, const char *t)
{
if (strncasecmp(s, t, strlen(t)) == 0) return 1;
return 0;
}
int str_end_with(const char* s, const char* t)
{
const char* se;
register int s_len, t_len;
s_len = strlen(s);
t_len = strlen(t);
if (s_len < t_len) return 0;
se = s + s_len - t_len;
if (strncasecmp(se, t, t_len) == 0) return 1;
return 0;
}
char *str_decode_path(char *path)
{
int x1;
int x2;
char *src = path;
char *dst = path;
char last = *path;
if (last != '/')
return NULL;
while (*++src)
{
if (*src == '%' &&
(x1 = tohex(*(src + 1))) >= 0 &&
(x2 = tohex(*(src + 2))) >= 0)
{
src += 2;
if ((*src = x1 * 16 + x2) == 0) break;
}
if (*src == '\\') *src = '/';
if ((last != '.' && last != '/') || (*src != '.' && *src != '/'))
*dst++ = last = *src;
}
*dst = 0;
return path;
}
static const unsigned char base64_table[65] =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
char *str_base64_encode(const char* src)
{
unsigned char *out, *pos;
const unsigned char *end, *in;
size_t olen;
int len;
len = strlen(src);
olen = len * 4 / 3 + 4; /* 3-byte blocks to 4-byte */
olen += olen / 72; /* line feeds */
olen++; /* nul termination */
out = (unsigned char*)wn_malloc(olen);
if (out == NULL) return NULL;
end = (const unsigned char*)src + len;
in = (const unsigned char*)src;
pos = out;
while (end - in >= 3)
{
*pos++ = base64_table[in[0] >> 2];
*pos++ = base64_table[((in[0] & 0x03) << 4) | (in[1] >> 4)];
*pos++ = base64_table[((in[1] & 0x0f) << 2) | (in[2] >> 6)];
*pos++ = base64_table[in[2] & 0x3f];
in += 3;
}
if (end - in)
{
*pos++ = base64_table[in[0] >> 2];
if (end - in == 1)
{
*pos++ = base64_table[(in[0] & 0x03) << 4];
*pos++ = '=';
}
else
{
*pos++ = base64_table[((in[0] & 0x03) << 4) |
(in[1] >> 4)];
*pos++ = base64_table[(in[1] & 0x0f) << 2];
}
*pos++ = '=';
}
*pos = '\0';
return (char*)out;
}
char* str_normalize_path(char* fullpath)
{
char *dst0, *dst, *src;
src = fullpath;
dst = fullpath;
dst0 = dst;
while (1)
{
char c = *src;
if (c == '.')
{
if (!src[1]) src ++; /* '.' and ends */
else if (src[1] == '/')
{
/* './' case */
src += 2;
while ((*src == '/') && (*src != '\0')) src ++;
continue;
}
else if (src[1] == '.')
{
if (!src[2])
{
/* '..' and ends case */
src += 2;
goto up_one;
}
else if (src[2] == '/')
{
/* '../' case */
src += 3;
while ((*src == '/') && (*src != '\0')) src ++;
goto up_one;
}
}
}
/* copy up the next '/' and erase all '/' */
while ((c = *src++) != '\0' && c != '/') *dst ++ = c;
if (c == '/')
{
*dst ++ = '/';
while (c == '/') c = *src++;
src --;
}
else if (!c) break;
continue;
up_one:
dst --;
if (dst < dst0) return NULL;
while (dst0 < dst && dst[-1] != '/') dst --;
}
*dst = '\0';
/* remove '/' in the end of path if exist */
dst --;
if ((dst != fullpath) && (*dst == '/')) *dst = '\0';
return fullpath;
}
char * urlencode(const char *str, int len, int *new_length)
{
const char hexchars[] = "0123456789ABCDEF";
const char *from, *end;
const char *start;
char *to;
int c;
from = str;
end = str + len;
start = to = (char *) wn_malloc(3 * len + 1);
if(start == NULL)
{
return NULL;
}
while (from < end)
{
c = *from++;
if ( (c < '0' && c != '-' && c != '.')
|| (c == ' ')
|| (c < 'A' && c > '9')
|| (c > 'Z' && c < 'a' && c != '_')
|| (c > 'z') )
{
to[0] = '%';
to[1] = hexchars[c >> 4];
to[2] = hexchars[c & 15];
to += 3;
}
else
{
*to++ = c;
}
}
*to = 0;
if (new_length)
{
*new_length = to - start;
}
return (char *) start;
}
int urldecode(char *str, int len)
{
char *dest = str;
char *data = str;
int value;
int c;
while (len--)
{
if (*data == '+')
{
*dest = ' ';
}
else if (*data == '%' && len >= 2 && isxdigit((int) *(data + 1))
&& isxdigit((int) *(data + 2)))
{
c = ((unsigned char *)(data+1))[0];
if (isupper(c))
c = tolower(c);
value = (c >= '0' && c <= '9' ? c - '0' : c - 'a' + 10) * 16;
c = ((unsigned char *)(data+1))[1];
if (isupper(c))
c = tolower(c);
value += c >= '0' && c <= '9' ? c - '0' : c - 'a' + 10;
*dest = (char)value ;
data += 2;
len -= 2;
}
else
{
*dest = *data;
}
data++;
dest++;
}
*dest = '\0';
return dest - str;
}
#ifdef _WIN32
int strncasecmp ( const char* s1, const char* s2, size_t len )
{
register unsigned int x2;
register unsigned int x1;
register const char* end = s1 + len;
while (1)
{
if ((s1 >= end) )
return 0;
x2 = *s2 - 'A'; if ((x2 < 26u)) x2 += 32;
x1 = *s1 - 'A'; if ((x1 < 26u)) x1 += 32;
s1++; s2++;
if ((x2 != x1))
break;
if ((x1 == (unsigned int)-'A'))
break;
}
return x1 - x2;
}
#endif