remove deprecated files

This commit is contained in:
NADAL Jean-Baptiste
2019-12-05 19:52:24 +01:00
parent da4dc1e0d4
commit 20e192462a
9 changed files with 210 additions and 3302 deletions

View File

@@ -0,0 +1,22 @@
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 888f65e8..50198586 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -780,7 +780,7 @@ endif()
# top of the build tree rather than in hard-to-find leaf directories.
SET(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}/bin")
SET(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}/lib")
-SET(CMAKE_ARCHIVE_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}/lib")
+#SET(CMAKE_ARCHIVE_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}/lib")
SET(LWS_INSTALL_PATH "${CMAKE_INSTALL_PREFIX}")
@@ -1853,7 +1853,7 @@ endif(LWS_WITH_LIBEV)
if (LWS_WITH_LIBUV)
if (NOT LIBUV_FOUND)
find_path(LIBUV_INCLUDE_DIRS NAMES uv.h)
- find_library(LIBUV_LIBRARIES NAMES uv)
+ find_library(LIBUV_LIBRARIES NAMES uv_a)
if(LIBUV_INCLUDE_DIRS AND LIBUV_LIBRARIES)
set(LIBUV_FOUND 1)
endif()

188
src/main.c Normal file
View File

@@ -0,0 +1,188 @@
/*
* lws-minimal-http-server-eventlib
*
* Written in 2010-2019 by Andy Green <andy@warmcat.com>
*
* This file is made available under the Creative Commons CC0 1.0
* Universal Public Domain Dedication.
*
* This demonstrates a minimal http[s] server that can work with any of the
* supported event loop backends, or the default poll() one.
*
* To keep it simple, it serves stuff from the subdirectory
* "./mount-origin" of the directory it was started in.
* You can change that by changing mount.origin below.
*/
#include <libwebsockets.h>
#include <string.h>
#include <signal.h>
#define LWS_PLUGIN_STATIC
#include "./plugins/protocol_lws_mirror.c"
#include "./plugins/protocol_lws_status.c"
#include "./plugins/protocol_dumb_increment.c"
#include "./plugins/protocol_post_demo.c"
static struct lws_context *context;
static struct lws_protocols protocols[] = {
/* first protocol must always be HTTP handler */
{ "http-only", lws_callback_http_dummy, 0, 0, },
LWS_PLUGIN_PROTOCOL_DUMB_INCREMENT,
LWS_PLUGIN_PROTOCOL_MIRROR,
LWS_PLUGIN_PROTOCOL_LWS_STATUS,
LWS_PLUGIN_PROTOCOL_POST_DEMO,
{ NULL, NULL, 0, 0 } /* terminator */
};
/*
* mount handlers for sections of the URL space
*/
static const struct lws_http_mount mount_ziptest = {
NULL, /* linked-list pointer to next*/
"/ziptest", /* mountpoint in URL namespace on this vhost */
"candide.zip", /* handler */
NULL, /* default filename if none given */
NULL,
NULL,
NULL,
NULL,
0,
0,
0,
0,
0,
0,
LWSMPRO_FILE, /* origin points to a callback */
8, /* strlen("/ziptest"), ie length of the mountpoint */
NULL,
{ NULL, NULL } // sentinel
};
static const struct lws_http_mount mount_post = {
(struct lws_http_mount *)&mount_ziptest, /* linked-list pointer to next*/
"/formtest", /* mountpoint in URL namespace on this vhost */
"protocol-post-demo", /* handler */
NULL, /* default filename if none given */
NULL,
NULL,
NULL,
NULL,
0,
0,
0,
0,
0,
0,
LWSMPRO_CALLBACK, /* origin points to a callback */
9, /* strlen("/formtest"), ie length of the mountpoint */
NULL,
{ NULL, NULL } // sentinel
};
static const struct lws_http_mount mount = {
/* .mount_next */ &mount_post, /* linked-list "next" */
/* .mountpoint */ "/", /* mountpoint URL */
/* .origin */ "./mount-origin", /* serve from dir */
/* .def */ "test.html", /* default filename */
/* .protocol */ NULL,
/* .cgienv */ NULL,
/* .extra_mimetypes */ NULL,
/* .interpret */ NULL,
/* .cgi_timeout */ 0,
/* .cache_max_age */ 0,
/* .auth_mask */ 0,
/* .cache_reusable */ 0,
/* .cache_revalidate */ 0,
/* .cache_intermediaries */ 0,
/* .origin_protocol */ LWSMPRO_FILE, /* files in a dir */
/* .mountpoint_len */ 1, /* char count */
/* .basic_auth_login_file */ NULL,
};
void signal_cb(void *handle, int signum)
{
lwsl_err("%s: signal %d\n", __func__, signum);
switch (signum) {
case SIGTERM:
case SIGINT:
break;
default:
break;
}
lws_context_destroy(context);
}
void sigint_handler(int sig)
{
signal_cb(NULL, sig);
}
int main(int argc, const char **argv)
{
struct lws_context_creation_info info;
const char *p;
int logs = LLL_USER | LLL_ERR | LLL_WARN | LLL_NOTICE
/* for LLL_ verbosity above NOTICE to be built into lws,
* lws must have been configured and built with
* -DCMAKE_BUILD_TYPE=DEBUG instead of =RELEASE */
/* | LLL_INFO */ /* | LLL_PARSER */ /* | LLL_HEADER */
/* | LLL_EXT */ /* | LLL_CLIENT */ /* | LLL_LATENCY */
/* | LLL_DEBUG */;
if ((p = lws_cmdline_option(argc, argv, "-d")))
logs = atoi(p);
lws_set_log_level(logs, NULL);
lwsl_user("LWS minimal http server eventlib | visit http://localhost:7681\n");
lwsl_user(" [-s (ssl)] [--uv (libuv)] [--ev (libev)] [--event (libevent)]\n");
memset(&info, 0, sizeof info); /* otherwise uninitialized garbage */
info.port = 7681;
info.mounts = &mount;
info.error_document_404 = "/404.html";
info.pcontext = &context;
info.protocols = protocols;
info.signal_cb = signal_cb;
info.options =
LWS_SERVER_OPTION_HTTP_HEADERS_SECURITY_BEST_PRACTICES_ENFORCE;
if (lws_cmdline_option(argc, argv, "-s")) {
info.options |= LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT;
info.ssl_cert_filepath = "localhost-100y.cert";
info.ssl_private_key_filepath = "localhost-100y.key";
}
if (lws_cmdline_option(argc, argv, "--uv"))
info.options |= LWS_SERVER_OPTION_LIBUV;
else
if (lws_cmdline_option(argc, argv, "--event"))
info.options |= LWS_SERVER_OPTION_LIBEVENT;
else
if (lws_cmdline_option(argc, argv, "--ev"))
info.options |= LWS_SERVER_OPTION_LIBEV;
else
signal(SIGINT, sigint_handler);
context = lws_create_context(&info);
if (!context) {
lwsl_err("lws init failed\n");
return 1;
}
while (!lws_service(context, 0))
;
lwsl_info("calling external context destroy\n");
lws_context_destroy(context);
return 0;
}

View File

@@ -1,55 +0,0 @@
/*!
* main.cpp
*
* Copyright (c) 2015-2019, NADAL Jean-Baptiste. All rights reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301 USA
*
* @Author: NADAL Jean-Baptiste
* @Date: 08/11/2019
*
*/
// This is an independent project of an individual developer. Dear PVS-Studio, please check it.
// PVS-Studio Static Code Analyzer for C, C++, C#, and Java: http://www.viva64.com
/*-------------------------------- INCLUDES ---------------------------------*/
#include <cstdio>
#include "server/domo-server.h"
/*! ----------------------------------------------------------------------------
* @fn main
*
* @brief Main function of domo-iot daemon.
*/
int main(int argc, char *argv[])
{
int the_return;
DomoServer the_server;
if (!the_server.setup())
{
fprintf(stderr, "Failed to setup the daemon.\n");
return -1;
}
the_return = the_server.loop();
return the_return;
}

View File

@@ -1,968 +0,0 @@
#include "tinyweb.h"
#include "tools.h"
#ifdef __GNUC__
#include <uv.h>
#define _strncmpi strncasecmp
#define strcmpi strcasecmp
#endif // __GNUC__
#include <stdlib.h>
#include <stdio.h>
#include <assert.h>
#include <string.h>
#include <memory.h>
// TinyWeb ajouté et amélioré les fonctions, par lzpong 2016/11/24
// Valeur élevée, meilleures performances du disque et du processeur lors de l'envoi de fichiers, utilisation accrue de la mémoire
#define TW_SEND_SIZE 1024*1024*16
typedef struct tw_file_t {
//uchar flag; //连接的标志
FILE* fp; //文件指针
uchar* buff;//文件发送缓存
unsigned long long fsize;//文件大小
unsigned long long lsize;//文件中要发送的块剩余大小
}tw_file_t;
typedef struct tw_client {
tw_peerAddr pa;//客户端连接的地址
tw_file_t ft;//发往客户端的文件,断点续传记录
WebSocketHandle hd;
tw_reqHeads heads;//Http 头部(如果是http)
membuf_t buf;//http post 分包接收的缓存
}tw_client;
//=================================================
//关闭客户端连接后,释放客户端连接的数据
static void after_uv_close_client(uv_handle_t* client) {
tw_config* tw_conf = (tw_config*)(client->loop->data);
tw_client* clidata = (tw_client*)client->data;
//如果有发送文件
if (clidata->ft.fp)
fclose(clidata->ft.fp);
if (clidata->ft.buff)
free(clidata->ft.buff);
//如果是WebSocket
//if (clidata->pa.flag & 0x2)
membuf_uninit(&clidata->hd.buf);
//http post 分包接收的缓存
membuf_uninit(&clidata->buf);
//关闭连接回调
if (tw_conf->on_close)
tw_conf->on_close(tw_conf->data, client, &clidata->pa);
free(client->data);
free(client);
}
//关闭客户端连接
void tw_close_client(uv_stream_t* client) {
tw_client* clidata = (tw_client*)client->data;
uv_close((uv_handle_t*)client, after_uv_close_client);
}
//发送数据后,free数据关闭客户端连接
static void after_uv_write(uv_write_t* w, int err) {
tw_client* clidata = (tw_client*)w->handle->data;
if (w->data)
free(w->data); //sended data need free
//长连接就不关闭了
if (!err && clidata->ft.fp)
tw_http_send_file(w->handle, NULL, NULL, NULL, NULL);
else if (!(clidata->pa.flag & 0x1)){
if (w->handle->flags & 0x01)
printf("after_uv_write sk:%zd error: handle Has been closed\n", clidata->pa.sk);
else
uv_close((uv_handle_t*)w->handle, after_uv_close_client);
}
free(w);
}
//发送数据到客户端; 如果是短连接,则发送完后会关闭连接
//data待发送数据
//len 数据长度, -1 将自动计算数据长度
//need_copy_data是否需要复制数据
//need_free_data是否需要free数据, 如果need_copy_data非零则忽略此参数
void tw_send_data(uv_stream_t* client, const void* data, size_t len, char need_copy_data, char need_free_data) {
uv_buf_t buf;
uv_write_t* w;
void* newdata = (void*)data;
if (len == (size_t)-1)
len = strlen((char*)data);
if (need_copy_data) {
newdata = malloc(len);
memcpy(newdata, data, len);
}
buf = uv_buf_init((char*)newdata, len);
w = (uv_write_t*)malloc(sizeof(uv_write_t));
w->data = (need_copy_data || need_free_data) ? newdata : NULL;
uv_write(w, client, &buf, 1, after_uv_write); //free w and w->data in after_uv_write()
}
//制造头部 SetCookie 字段和值
//cookie: 缓存区(至少 42+strlen(key)+strlen(val)+strlen(domain)+strlen(path) )
//ckLen: cookie的长度
//expires: 多少秒后过期
//domain: Domain, 域名或IP地址,或NULL
//path: Path, 可以是 heads->path,或NULL
void tw_make_setcookie(char* set_cookie,int ckLen,const char* key,const char* val,int expires,char* domain,char* path) {
int rlen,len=0;
rlen=snprintf(set_cookie, ckLen, "Set-Cookie: %s=%s", key, val);
if (rlen > 0) len = rlen,rlen=0;
if (expires > 0)
rlen = snprintf(set_cookie + len, ckLen - len, "; Max-Age=%d", expires);
if (rlen > 0) len += rlen,rlen=0;
if (domain)
rlen = snprintf(set_cookie + len, ckLen - len, "; Domain=%s", domain);
if (rlen > 0) len += rlen, rlen = 0;
if (path)
rlen = snprintf(set_cookie + len, ckLen - len, "; Path=%s", path);
if (rlen > 0) len += rlen, rlen = 0;
set_cookie[len]='\r';
set_cookie[len+1]='\n';
set_cookie[len+2]=0;
}
//制造头部 delete cookie
void tw_make_delcookie(char* del_cookie, int ckLen, char* key)
{
snprintf(del_cookie, ckLen, "Delete-Cookie: %s\r\n", key);
}
//发送'200 OK' 响应; 不会释放(free)传入的数据(u8data)
//content_typeContent Type 文档类型
//u8datautf-8编码的数据
//content_length数据长度为0或-1时自动计算(strlen)(c_str, end with NULL)
//respone_size获取响应最终发送的数据长度为0表示放不需要取此长度
void tw_send_200_OK(uv_stream_t* client, const char* ext_heads, const char* content_type, const void* u8data, size_t content_length, size_t* respone_size) {
size_t repSize;
const char *type = strchr(content_type, '/');
//有'.' 没有'/' 至少有两个'/' '/'是在开头 '/'是在末尾
//都要重新取文件类型
if (type) {
if (strchr(content_type, '.'))// 有'.'
type = tw_get_content_type(content_type);
else {
type = strchr(type + 1, '/');
if (type)//至少有两个'/'
type = tw_get_content_type(content_type);
else {
type = strchr(content_type, '/');
if (type == content_type || type == (content_type + strlen(content_type) - 1))
type = tw_get_content_type(content_type);
else
type = content_type;
}
}
}//没有'/'
else
type = tw_get_content_type(content_type);
char *data = tw_format_http_respone(client, "200 OK", ext_heads, type, u8data, content_length, &repSize);
tw_send_data(client, data, repSize, 0, 1);//发送后free data
if (respone_size)
*respone_size = repSize;
}
//返回格式华的HTTP响应内容需要free返回数据
//status: "200 OK"
//content_type: 文件类型,如:"text/html" 可以调用tw_get_content_type()得到
//content: any utf-8 data, need html-encode if content_type is "text/html"
//content_length: 0或-1自动计算 content 长度(c_str, end with NULL)
//respone_size: if not NULL,可以获取发送的数据长度 the size of respone will be writen to request
//returns malloc()ed c_str, need free() by caller
char* tw_format_http_respone(uv_stream_t* client, const char* status, const char* ext_heads, const char* content_type, const char* content, size_t content_length, size_t* respone_size) {
size_t totalsize, header_size;
char* respone;
char szDate[30];
getGmtTime(szDate,30,0);
ext_heads == NULL ? ext_heads = "" : 0;
tw_config* tw_conf = (tw_config*)(client->loop->data);
if (content_length == 0 || content_length == (size_t)-1)
content_length = content ? strlen(content) : 0;
totalsize = strlen(status) + strlen(ext_heads) + strlen(content_type) + content_length + 158;
respone = (char*)malloc(totalsize + 1);
header_size = snprintf(respone, totalsize, "HTTP/1.1 %s\r\nDate: %s\r\nServer: TinyWeb\r\nConnection: close\r\nContent-Type:%s; charset=%s\r\nContent-Length:%zd\r\n%s\r\n"
, status, szDate, content_type, tw_conf->charset, content_length, ext_heads);
assert(header_size > 0);
if (content_length)
memcpy(respone + header_size, content, content_length+1);
if (respone_size)
*respone_size = header_size + content_length;
return respone;
}
//发送404响应
static void tw_404_not_found(uv_stream_t* client, const char* pathinfo, const char* ext_heads) {
char* respone;
char buffer[128];
snprintf(buffer, sizeof(buffer), "<h1>404 Not Found</h1><p>%s</p>", pathinfo);
respone = tw_format_http_respone(client, "404 Not Found", ext_heads, "text/html", buffer, -1, NULL);
tw_send_data(client, respone, -1, 0, 1);
}
//发送301响应,路径永久重定位
void tw_301_Moved(uv_stream_t* client, tw_reqHeads* heads, const char* ext_heads) {
size_t len = 76 + strlen(heads->path);
char buffer[1245];
char szDate[30];
ext_heads == NULL ? ext_heads = "" : 0;
getGmtTime(szDate,30,0);
tw_config* tw_conf = (tw_config*)(client->loop->data);
snprintf(buffer, sizeof(buffer), "HTTP/1.1 301 Moved Permanently\r\nDate: %s\r\n"
"Server: TinyWeb\r\nLocation: http://%s%s%s%s\r\nConnection: close\r\n"
"Content-Type:text/html;charset=%s\r\nContent-Length:%zd\r\n%s\r\n"
"<h1>Moved Permanently</h1><p>The document has moved <a href=\"%s%s%s\">here</a>.</p>"
, szDate
, heads->host, heads->path, (heads->query[0]?"?":""), (heads->query[0]?heads->query:"")
, tw_conf->charset, len, ext_heads
, heads->path, (heads->query[0]?"?":""), (heads->query[0]?heads->query:""));
tw_send_data(client, buffer, -1, 1, 1);
}
//发送302响应,路径临时重定位
void tw_302_Moved(uv_stream_t* client, tw_reqHeads* heads, const char* ext_heads) {
char buffer[1245];
char szDate[30];
ext_heads == NULL ? ext_heads = "" : 0;
getGmtTime(szDate,30,0);
tw_config* tw_conf = (tw_config*)(client->loop->data);
snprintf(buffer, sizeof(buffer), "HTTP/1.1 302 Moved Temporarily\r\nDate: %s\r\n"
"Server: TinyWeb\r\nLocation: http://%s%s%s%s\r\nConnection: close\r\n"
"Content-Type:text/html;charset=%s\r\nContent-Length:0\r\n%s\r\n"
, szDate
, heads->host, heads->path, (heads->query[0]?"?":""), (heads->query[0]?heads->query:"")
, tw_conf->charset, ext_heads);
tw_send_data(client, buffer, -1, 1, 1);
}
//http协议发送文件,异步
//file_path: 文件路径
void tw_http_send_file(uv_stream_t* client, tw_reqHeads* heads, const char* ext_heads, const char* content_type, const char* file_path) {
char *respone;
char szDate[30];
tw_config* tw_conf = (tw_config*)(client->loop->data);
tw_client* clidata = (tw_client*)client->data;
tw_file_t* filet = &clidata->ft;
//发送头部
if (!filet->fp && file_path) {
filet->fp = fopen(file_path, "rb");
if (filet->fp) {
#ifdef _WIN64
_fseeki64(filet->fp, 0, SEEK_END);
filet->fsize = _ftelli64(filet->fp);
#else
fseek(filet->fp, 0, SEEK_END);
filet->fsize = ftell(filet->fp);
#endif
if (heads->Range_frm < 0)//(负数:从文件末尾反过来的位置,即fileSize-sizeFrom)
heads->Range_frm = filet->fsize + heads->Range_frm;
if (heads->Range_to <= 0)//(负数:从文件末尾反过来的位置,即fileSize-sizeTo)
heads->Range_to = filet->fsize + heads->Range_to;
if (filet->fsize < (unsigned long long)heads->Range_frm)//开始位置大于文件
heads->Range_frm = filet->fsize;
if (heads->Range_to < heads->Range_frm || (unsigned long long)heads->Range_to>filet->fsize)//Range_to 可能没有,或不正确,表示整个文件大小
heads->Range_to = filet->fsize;
//要下载区段的size
filet->lsize = heads->Range_to - heads->Range_frm;
#ifdef _WIN64
_fseeki64(filet->fp, heads->Range_frm, SEEK_SET);
#else
fseek(filet->fp, heads->Range_frm, SEEK_SET);
#endif
ext_heads == NULL ? ext_heads = "" : 0;
getGmtTime(szDate,30,0);
respone = (char*)malloc(300 + 1);
int respone_size;
if (heads->Range_frm == 0) //200 OK
respone_size = snprintf(respone, 300, "HTTP/1.1 200 OK\r\nDate: %s\r\nServer: TinyWeb\r\nConnection: close\r\nContent-Type:%s;charset=%s\r\nAccept-Range: bytes\r\nContent-Length:%llu\r\n%s\r\n"
, szDate, content_type, tw_conf->charset, filet->fsize,ext_heads);
else //206 Partial Content
respone_size = snprintf(respone, 300, "HTTP/1.1 206 Partial Content\r\nDate: %s\r\nServer: TinyWeb\r\nConnection: close\r\nContent-Type:%s;charset=%s\r\nAccept-Range: bytes\r\nContent-Range: %lld-%lld/%llu\r\nContent-Length:%llu\r\n%s\r\n"
, szDate, content_type, tw_conf->charset, heads->Range_frm, heads->Range_to, filet->fsize, filet->lsize,ext_heads);
tw_send_data(client, respone, respone_size, 0, 1);
}
else
tw_404_not_found(client, heads->path, ext_heads);
}
else { //发送文件
size_t read_size=0;// read_bytes;
if (filet->fp) {
if (filet->lsize > 0) {
if(!filet->buff)
filet->buff = (char*)malloc(TW_SEND_SIZE + 1);
//fread 返回实际读取的单元个数。如果小于count则可能文件结束或读取出错
//可以用ferror()检测是否读取出错用feof()函数检测是否到达文件结尾。如果size或count为0则返回0。
if(filet->lsize>TW_SEND_SIZE)
read_size = fread(filet->buff, sizeof(char), TW_SEND_SIZE, filet->fp);
else
read_size = fread(filet->buff, sizeof(char), filet->lsize, filet->fp);
filet->lsize -= read_size;
tw_send_data(client, filet->buff, read_size, 0, 0);
}
else
{
fclose(filet->fp);
filet->fp = 0;
filet->fsize = 0;
}
}
}
}
//根据扩展名(不区分大小写),返回文件类型 content_type
const char* tw_get_content_type(const char* fileExt) {
const static char* octet = "application/octet-stream";
if (fileExt)
{
//不管什么路径名或者文件名, 只要最后面有点(.),就认为是有扩展名的
const char *p = strrchr(fileExt, '.');
if (p) { // /aaa.txt
fileExt = p + 1;
}
}
else //否则没有扩展名
return octet;
if (strcmpi(fileExt, "htm") == 0 || strcmpi(fileExt, "html") == 0)
return "text/html";
else if (strcmpi(fileExt, "js") == 0)
return "application/javascript";
else if (strcmpi(fileExt, "css") == 0)
return "text/css";
else if (strcmpi(fileExt, "json") == 0)
return "application/json";
else if (strcmpi(fileExt, "log") == 0 || strcmpi(fileExt, "txt") == 0 || strcmpi(fileExt, "ini") == 0
|| strcmpi(fileExt, "config") == 0 || strcmpi(fileExt, "conf") == 0 || strcmpi(fileExt, "cfg") == 0
|| strcmpi(fileExt, "sh") == 0 || strcmpi(fileExt, "bat") == 0)
return "text/plain";
else if (strcmpi(fileExt, "jpg") == 0 || strcmpi(fileExt, "jpeg") == 0)
return "image/jpeg";
else if (strcmpi(fileExt, "png") == 0)
return "image/png";
else if (strcmpi(fileExt, "gif") == 0)
return "image/gif";
else if (strcmpi(fileExt, "ico") == 0)
return "image/x-icon";
else if (strcmpi(fileExt, "xml") == 0)
return "application/xml";
else if (strcmpi(fileExt, "xhtml") == 0)
return "application/xhtml+xml";
else if (strcmpi(fileExt, "swf") == 0)
return "application/x-shockwave-flash";
else if (strcmpi(fileExt, "svg") == 0)
return "image/svg-xml";
else if (strcmpi(fileExt, "wav") == 0)
return "audio/wav";
else if (strcmpi(fileExt, "mid") == 0 || strcmpi(fileExt, "midi") == 0)
return "audio/midi";
else if (strcmpi(fileExt, "wma") == 0)
return "audio/x-ms-wma";
else if (strcmpi(fileExt, "mp3") == 0)
return "audio/mp3";
else if (strcmpi(fileExt, "3gp") == 0)
return "video/3gpp";
else if (strcmpi(fileExt, "avi") == 0)
return "video/x-msvideo";
else if (strcmpi(fileExt, "mkv") == 0)
return "video/x-matroska";
else if (strcmpi(fileExt, "mp4") == 0)
return "video/mp4";
else if (strcmpi(fileExt, "rmvb") == 0)
return "video/vnd.rn-realvideo";
else if (strcmpi(fileExt, "flv") == 0)
return "flv-application/octet-stream";// "video/x-flv";
else if (strcmpi(fileExt, "apk") == 0)
return "application/vnd.android.package-archive";
else
return octet;
}
//处理客户端请求
//invoked by tinyweb when GET request comes in
//please invoke write_uv_data() once and only once on every request, to send respone to client and close the connection.
//if not handle this request (by invoking write_uv_data()), you can close connection using tw_close_client(client).
//pathinfo: "/" or "/book/view/1"
//query_stirng: the string after '?' in url, such as "id=0&value=123", maybe NULL or ""
static void tw_request(uv_stream_t* client, tw_reqHeads* heads) {
tw_config* tw_conf = (tw_config*)(client->loop->data);
char fullpath[260];//绝对路径(末尾不带斜杠)
snprintf(fullpath, 259, "%s/%s\0", tw_conf->doc_dir, (heads->path[0] == '/' ? heads->path + 1 : heads->path));
//去掉末尾的斜杠
char *p = &fullpath[strlen(fullpath) - 1];
while (*p == '/' || *p == '\\')
*p = 0, p--;
char file_dir = isExist(fullpath);
//判断 文件或文件夹,或不存在
switch (file_dir)
{
case 1://存在:文件
{
char* postfix = strrchr(heads->path, '.');//从后面开始找文件扩展名
if (postfix)
{
postfix++;
p = postfix + strlen(postfix) - 1;
while (*p == '/' || *p == '\\')
*p = 0, p--;
}
tw_http_send_file(client, heads, NULL, tw_get_content_type(postfix), fullpath);
}
break;
case 2://存在:文件夹
{
if (heads->path[strlen(heads->path)-1] != '/') //文件夹要检测末尾'/'
{
int len = strlen(heads->path);
if (len >= sizeof(heads->path)-1)
len=sizeof(heads->path)-2;
heads->path[len] = '/';
heads->path[len+1] = 0;
tw_301_Moved(client, heads, NULL);
break;
}
char tmp[260]; tmp[0] = 0;
char *s = strdup(tw_conf->doc_index);
p = strtok(s, ";");
//是否有默认主页
while (p)
{
snprintf(tmp, 259, "%s/%s", fullpath, p);
if (isFile(tmp)) {
tw_http_send_file(client, heads, NULL, "text/html", tmp);
break;
}
tmp[0] = 0;
p = strtok(NULL, ";");
}
free(s);
//没用默认主页
if (!tmp[0])
{
char* p2=NULL;
uint len;
membuf_t buf;
membuf_init(&buf, 1024 * 2);
char *body = "Welcome to TinyWeb.<br>Directory access forbidden.";
if (tw_conf->dirlist) {
membuf_append(&buf, "<!DOCTYPE html><html><head><title>Index of ");//+path
#ifdef _MSC_VER
if (strnicmp(tw_conf->charset, "utf",3) == 0) {//utf-8
len = strlen(heads->path);
p2 = GB2U8(heads->path, &len);
membuf_append(&buf, p2);
} else
#endif // _MSC_VER
membuf_append(&buf, heads->path);
membuf_append_format(&buf, "</title><meta name=\"renderer\" content=\"webkit\"><meta charset=\"%\">\r\n", tw_conf->charset);
membuf_append(&buf, "</head><body><h1>Index of ");//+path
if (p2) {
membuf_append(&buf, p2);
free(p2);
} else {
membuf_append(&buf, heads->path);
}
membuf_append(&buf, "</h1>\r\n"
"<table>\r\n"
"<thead><tr><th><a href=\"javascript:fssort('type')\">@</a></th><th><a href=\"javascript:fssort('name')\">Name</a></th><th><a href=\"javascript:fssort('size')\">Size</a></th><th><a href=\"javascript:fssort('mtime')\">Last modified</a></th></tr>"
"<tr><th colspan=\"4\"><hr style=\"margin:1px;\"></th></tr></thead>\r\n"
"<tbody id=\"tbody\"></tbody>"
"<tfoot><tr><th colspan=\"4\"><hr></th></tr></tfoot>"
"</table>"
"<address>TinyWeb Server</address>"
"</body></html>\r\n<script type=\"text/javascript\">\r\nvar files=");//+files
body = listDir(fullpath, heads->path);
#ifdef _MSC_VER
if (strnicmp(tw_conf->charset, "utf", 3) == 0) {//utf-8
len = strlen(body);
p2 = GB2U8(body, &len);
free(body);
body = p2;
}
#endif // _MSC_VER
membuf_append(&buf, body);
free(body);
membuf_append(&buf, "; \r\nvar html = \"\", p=files.path[files.path.length-1];\n"
"function fsshow(){var html='';for (var r in files.files){r=files.files[r];html+='<tr><td>'+r.type+\"</td><td><a href='\"+r.name+\"'>\"+r.name+'</td><td>'+r.size+'</td><td>'+r.mtime+'</td></tr>';}document.querySelector('tbody').innerHTML = html;}\n"
"if(p!='/'){files.path+='/';}\n"
"files.files.sort(function(a,b){var n=a.type.localeCompare(b.type);if(n)return n;else return a.name.localeCompare(b.name);});\n"
"fsshow();\n"
"function fssort(n){files.files.sort(function(a,b){if(typeof a[n]=='number')return a[n]-b[n];return a[n].localeCompare(b[n])});fsshow();}\n"
"</script>");
}
else
membuf_append_data(&buf, body, strlen(body));
char *respone = tw_format_http_respone(client, "200 OK", NULL, "text/html", (char*)buf.data, buf.size, NULL);
tw_send_data(client, respone, -1, 0, 1);
membuf_uninit(&buf);
}
}
break;
default://不存在
tw_404_not_found(client, heads->path, NULL);
break;
}
}
//获取http头信息,返回指向 Sec-WebSocket-Key 的指针
static char* tw_get_http_heads(const uv_buf_t* buf, int len, tw_reqHeads* heads) {
char *key=NULL, *start, *head, *p;
char delims[] = "\r\n";
char* data = strstr(buf->base, "\r\n\r\n");
if (data) {
*data = 0;
heads->data = data += 4;
heads->len = len - (data-buf->base);
//是http get/post协议
if (buf->base[0] == 'G' && buf->base[1] == 'E' && buf->base[2] == 'T' && buf->base[3] == ' ') {
heads->method = 1;//GET
}
else if (buf->base[0] == 'P' && buf->base[1] == 'O' && buf->base[2] == 'S' && buf->base[3] == 'T' && buf->base[4] == ' ') {
heads->method = 2;//POST
}
//是http get/post协议
if (heads->method)
{
char *path = "", *query = "";
head = strtok(buf->base, delims);
//search path
path = strchr(head + 3, ' ') + 1;
while (isspace(*path)) path++;
start = strchr(path, ' ');
if (start) *start = 0;
//url含有转义编码字符
if (strstr(path, "%") != 0)
{
url_decode(heads->path);
#ifdef _MSC_VER //Windows下需要转换编码,因为windows系统的编码是GB2312
size_t len = strlen(path);
char *gb = U82GB(path, &(unsigned int)len);
strncpy(path, gb, len);
path[len] = 0;
free(gb);
//linux 下系统和源代码文件编码都是是utf8的就不需要转换
#endif // _MSC_VER
}
//query param
p = strchr(path, '?');
if (p) {
query = p + 1;
*p = 0;
}
//确保开头为'/'
if (*path != '/') {
path--;
*path = '/';
*(path - 1) = 0;
}
//确保结尾不是"/.."
p = strrchr(path, '.');
if (p && *(p + 1) == 0 && *(p - 1) == '.' && *(p - 2) == '/') {
*(p + 1) = '/';
*(p + 2) = 0;
}
////------------尽可能的合并 "../" "/./"
if (strstr(path, "./") != 0)
{
//去掉"/./"
while ((p = strstr(path, "/./")))
memmove(p, p + 2, strlen(p + 2) + 1);
//尽可能的合并"../"
while ((p = strstr(path, "/.."))) {//存在 ..
if ((p - path) <= 1) {
if ((start = strchr(path + 2, '/')))
path = start;
else
*p = 0;
continue;
}
*(p - 1) = 0;
start = strrchr(path, '/');
if (start == NULL)
start = path;
key = strchr(p + 2, '/');
if (key)
p = key;
else
break;
memmove(start, p, strlen(p) + 1);
}
}
snprintf(heads->path,512, "%s", path);
snprintf(heads->query,1500, "%s", query);
key = NULL;
//从第二行开始循环处理 头部
head = strtok(NULL, delims);
while (head)
{
//是否有 Sec-WebSocket-Key
//http upgrade to WebSocket
if (start = strstr(head, "Sec-WebSocket-Key: "))
{
key = start + 19;
}
//search host
else if (start = strstr(head, "Host: "))
{
snprintf(heads->host,260,"%s", start + 6);
}
//Range: bytes=sizeFrom-[sizeTo] (sizeTo 可能没有,或不正确,表示整个文件大小)
// (sizeFrom 为负数,表示从文件末尾反过来的位置,即fileSize-sizeFrom)
//Range: bytes=sizeFrom-[sizeTo],sizeFrom-[sizeTo][,sizeFrom-[sizeTo]] 这种多段不支持,只支持一段
else if (start = strstr(head, "Range: "))
{
start += 7;
start = strstr(start, "bytes=");
if (start)
start += 6;
p= strstr(start + 1, "-");//防止 sizeFrom 为负数
heads->Range_to = 0;
if (p)//可能有 sizeTo
{
heads->Range_frm = strtoll(start, &p, 10);
p++;//跳过 '-'
if(*p)
heads->Range_to = strtoll(p, NULL, 10);
}
else //没有 sizeTo
heads->Range_frm = strtol(start, NULL, 10);
}
//Content-Length: 3543
else if (start = strstr(head, "Content-Length: "))
{
heads->contentLen = atoi(start + 16);
}
//Cookie: xxxxx
else if (start = strstr(head, "Cookie: "))
{
snprintf(heads->cookie,260,"%s", start + 8);
}
//下一行 头部
head = strtok(NULL, delims);
}
if (heads->contentLen < 1)
heads->data = NULL,heads->len=0;
}
}
return key;
}
//on_read_WebSocket
static void on_read_websocket(uv_stream_t* client, char* data, size_t Len) {
tw_config* tw_conf = (tw_config*)(client->loop->data);
tw_client* clidata = (tw_client*)client->data;
WebSocketHandle* hd = &clidata->hd;
if (NULL == hd->buf.data)
membuf_init(&hd->buf, 128);
WebSocketGetData(hd, data, Len);
clidata->pa.flag = bitRemove(clidata->pa.flag, 0x4);
if (hd->isEof)
{
switch (hd->type) {
case 0: //0x0表示附加数据帧
break;
case 1: //0x1表示文本数据帧
clidata->pa.flag |= 0x4;
case 2: //0x2表示二进制数据帧
//接收数据回调
if (tw_conf->on_data)
tw_conf->on_data(tw_conf->data, client, &clidata->pa, &hd->buf);
break;
case 3: case 4: case 5: case 6: case 7: //0x3 - 7暂时无定义为以后的非控制帧保留
//membuf_uninit(hd->buf);
//memset(hd, 0, sizeof(WebSocketHandle));
break;
case 8: //0x8表示连接关闭
*(data + 1) = 0;//无数据
tw_send_data(client, data, 2, 1, 0);
if (hd->buf.size > 2) { //错误信息
if (tw_conf->on_error) {
char errstr[60] = { 0 };
snprintf(errstr, 59, "-0:wserr,%s", hd->buf.data + 2);
//出错信息回调
tw_conf->on_error(tw_conf->data, client, &clidata->pa, 0, errstr);
}
else
fprintf(stderr, "-0:wserr,%s\n", hd->buf.data + 2);
}
break;
case 9: //0x9表示ping
case 10://0xA表示pong
default://0xB - F暂时无定义为以后的控制帧保留
*data += 1;//发送pong
*(data + 1) = 0;//无数据
tw_send_data(client, data, 2, 1, 0);
//memset(hd, 0, sizeof(WebSocketHandle));
break;
}
//释放 membuf , 收到消息时再分配
clidata->hd.dfExt = clidata->hd.isEof = clidata->hd.type = 0;
membuf_uninit(&hd->buf);
}
}
//(循环)读取客户端发送的数据,接收客户的数据
static void on_uv_read(uv_stream_t* client, ssize_t nread, const uv_buf_t* buf) {
tw_config* tw_conf = (tw_config*)(client->loop->data);
tw_client* clidata = (tw_client*)client->data;
membuf_t mbuf;
if (nread > 0) {
assert(clidata);
//WebSocket
if (clidata->pa.flag & 0x2) //WebSocket
on_read_websocket(client, buf->base, nread);
//long-link
else if (clidata->pa.flag & 0x1) { //SOCKET //long-link
//接收数据回调
if (tw_conf->on_data) {
mbuf.data = buf->base;
mbuf.size = nread;
mbuf.buffer_size = buf->len;
tw_conf->on_data(tw_conf->data, client, &clidata->pa, &mbuf);
}
}
//http post继续接收 未收完的数据
else if (clidata->heads.method==2) {
if (nread) {
membuf_append_data(&clidata->buf, buf->base, nread);
clidata->heads.len += nread;
}
if (clidata->heads.len >= clidata->heads.contentLen) {
//所有请求全部回调,返回非0表示已处理
clidata->heads.data = clidata->buf.data;
if (tw_conf->on_request == 0 || 0 == tw_conf->on_request(tw_conf->data, client, &clidata->pa, &clidata->heads))
tw_request(client, &clidata->heads);
}
}
//未知
else { //http 或 未知
char* p, *p2;
//tw_reqHeads heads;
//memset(&heads, 0, sizeof(tw_reqHeads));
p = tw_get_http_heads(buf,nread, &clidata->heads);//get Sec-WebSocket-Key ?
if (p) { //WebSocket 握手
clidata->pa.flag |= 3;//long-link & WebSocket
p2 = WebSocketHandShak(p);
tw_send_data(client, p2, -1, 1, 0);
free(p2);
}
else if (clidata->heads.method) { //HTTP
if (!clidata->heads.path && clidata->heads.path[0] != '/'){//路径没有 '/' 开头
tw_301_Moved(client, &clidata->heads, NULL);
}
else {//http post 数据
if (clidata->heads.len >= clidata->heads.contentLen) {
//所有请求全部回调,返回非0表示已处理
if (tw_conf->on_request == 0 || 0 == tw_conf->on_request(tw_conf->data, client, &clidata->pa, &clidata->heads))
tw_request(client, &clidata->heads);
}
else {//跟随头部的 post 数据未发送完
membuf_init(&clidata->buf, 128);
if (clidata->heads.len)
membuf_append_data(&clidata->buf, clidata->heads.data, clidata->heads.len);
}
}
}
else { //SOCKET
clidata->pa.flag |= 1;//long-link
//接收数据回调
if (tw_conf->on_data) {
mbuf.data = buf->base;
mbuf.size = nread;
mbuf.buffer_size = buf->len;
tw_conf->on_data(tw_conf->data, client, &clidata->pa, &mbuf);
}
}
}
}
else if (nread <= 0) {//在任何情况下出错, read 回调函数 nread 参数都<0出错原因可能是 EOF(遇到文件尾)
if (nread != UV_EOF) {
if (tw_conf->on_error) {
char errstr[60] = { 0 };
snprintf(errstr, 59, "%d:%s,%s", (int)nread, uv_err_name((int)nread), uv_strerror((int)nread));
//出错信息回调
tw_conf->on_error(tw_conf->data, client, &clidata->pa, nread, errstr);
}
else
fprintf(stderr, "%d:%s,%s\n", (int)nread, uv_err_name((int)nread), uv_strerror((int)nread));
}
//关闭连接. 读取长度为0,或是错误值,都应该关闭连接
tw_close_client(client);
}
//每次使用完要释放
if (buf->base)
free(buf->base);
}
//为每次读取数据分配内存缓存
static void on_uv_alloc(uv_handle_t* handle, size_t suggested_size, uv_buf_t* buf) {
buf->base = (char*)calloc(1, suggested_size);
buf->len = suggested_size;
}
//获取客户端的 socket,ip,port
static char tw_getPeerAddr(uv_stream_t* client, tw_peerAddr* pa)
{
struct sockaddr_in peeraddr[2], hostaddr[2]; //Windows 下不声明成数组(且数组长度大于1),getpeername会失败(errno=10014)
int addrlen = sizeof(peeraddr);
//memset(pa, 0, sizeof(PeerAddr));
//客户端的地址
if (client->type == UV_TCP) {
#ifdef WIN32
pa->sk = ((uv_tcp_t*)client)->socket;
#else
addrlen /= 2;
#if defined(__APPLE__)
if (client->select)
pa->sk = client->select;
else
#endif
pa->sk = client->io_watcher.fd;
#endif
int er = uv_tcp_getpeername((uv_tcp_t*)client, (struct sockaddr*)peeraddr, &addrlen);
if (er < 0)
memset(peeraddr, 0, addrlen);
er = uv_tcp_getsockname((uv_tcp_t*)client, (struct sockaddr*)hostaddr, &addrlen);
if (er < 0)
memset(hostaddr, 0, addrlen);
}
else if (client->type == UV_UDP) {
#ifdef WIN32
pa->sk = ((uv_udp_t*)client)->socket;
#else
pa->sk = client->io_watcher.fd;
#endif
int er = uv_udp_getsockname((uv_udp_t*)client, (struct sockaddr*)peeraddr, &addrlen);
if (er < 0)
memset(peeraddr, 0, addrlen);
er = uv_udp_getsockname((uv_udp_t*)client, (struct sockaddr*)hostaddr, &addrlen);
if (er < 0)
memset(hostaddr, 0, addrlen);
}
else
return 1;
//网络字节序转换成主机字符序
uv_ip4_name(peeraddr, pa->ip, sizeof(pa->ip));
pa->port = ntohs(peeraddr[0].sin_port);
uv_ip4_name(hostaddr, pa->fip, sizeof(pa->ip));
pa->fport = ntohs(hostaddr[0].sin_port);
return 0;
}
//客户端接入
static void tw_on_connection(uv_stream_t* server, int status) {
//assert(server == (uv_stream_t*)&_server);
tw_client* cli;
if (status == 0) {
//建立客户端信息,在关闭连接时释放 see after_uv_close_client
uv_tcp_t* client = (uv_tcp_t*)calloc(1, sizeof(uv_tcp_t));
//创建客户端的数据缓存块,在关闭连接时释放 see after_uv_close_client
cli = client->data = calloc(1, sizeof(tw_client));
uv_tcp_init(server->loop, client);//将客户端放入loop
//接受客户,保存客户端信息
uv_accept(server, (uv_stream_t*)client);
client->close_cb = after_uv_close_client;
//取客户端 socket,ip,port;
tw_getPeerAddr((uv_stream_t*)client, &cli->pa);
//开始读取客户端数据
uv_read_start((uv_stream_t*)client, on_uv_alloc, on_uv_read);
//客户端接入回调
tw_config* tw_conf = (tw_config*)(server->loop->data);
if (tw_conf->on_connect)
tw_conf->on_connect(tw_conf->data, (uv_stream_t*)client, &cli->pa);
}
}
//==================================================================================================
//TinyWeb 线程开始运行
static void tw_run(uv_loop_t* loop) {
tw_config* tw_conf = (tw_config*)loop->data;
printf("TinyWeb v1.2.2 is started, listening on %s:%d\n", tw_conf->ip, tw_conf->port);
uv_run(loop, UV_RUN_DEFAULT);
uv_stop(loop);
if (!uv_loop_close(loop) && loop != uv_default_loop()) {
uv_loop_delete(loop);
}
printf("TinyWeb v1.2.2 is stopped, listening on %s:%d\n", tw_conf->ip, tw_conf->port);
free(tw_conf->doc_dir);
free(tw_conf->doc_index);
free(tw_conf->charset);
free(tw_conf);
}
//start web server, start with the config
//loop: if is NULL , it will be uv_default_loop()
//conf: the server config
int tinyweb_start(uv_loop_t* loop, tw_config* conf) {
int ret;
assert(conf != NULL);
if (conf->ip == NULL || (conf->ip != NULL && conf->ip[0] == '*'))
conf->ip = "0.0.0.0";
struct sockaddr_in addr;
uv_ip4_addr(conf->ip, conf->port, &addr);
tw_config* tw_conf = calloc(1, sizeof(tw_config));
memcpy(tw_conf, conf, sizeof(tw_config));
//设置主目录(末尾不带斜杠)
if (conf->doc_dir)
tw_conf->doc_dir = strdup(conf->doc_dir);
else
tw_conf->doc_dir = strdup("./");
printf("WebRoot port:%d Dir:%s\n",tw_conf->port, tw_conf->doc_dir);
//设置默认主页(分号间隔)
if (conf->doc_index && strcmpi(conf->doc_index, "") != 0)
tw_conf->doc_index = strdup(conf->doc_index);
else
tw_conf->doc_index = strdup("index.htm;index.html");
//设置more编码
if (conf->charset)
tw_conf->charset = strdup(conf->charset);
else
tw_conf->charset = strdup("utf-8");
if (loop == NULL)
loop = uv_default_loop();
ret = uv_tcp_init(loop, &tw_conf->_server);
if (ret < 0)
return ret;
ret = uv_tcp_bind(&tw_conf->_server, (const struct sockaddr*) &addr, 0);
if (ret < 0)
return ret;
ret = uv_listen((uv_stream_t*)&tw_conf->_server, 8, tw_on_connection);
if (ret < 0)
return ret;
loop->data = tw_conf;
//开始线程
uv_thread_t hare_id;
uv_thread_create(&hare_id, (uv_thread_cb)tw_run, loop);
return 0;
}
static void on_close_cb(uv_handle_t* handle) {
}
//stop TinyWeb
//当执行uv_stop之后uv_run并不能马上退出而是要等待其内部循环的下一个iteration到来时才会退出
//如果提前free掉loop就会导致loop失效。当然也可以sleep几十毫秒然后再close但这么搞不太雅观。
//uv_stop以后不能马上执行uv_loop_close()
//貌似关闭及释放loop等资源不是很完善的样子
void tinyweb_stop(uv_loop_t* loop)
{
if (loop == NULL)
loop = uv_default_loop();
uv_stop(loop);
if (loop->data)
uv_close((uv_handle_t*)&((tw_config*)loop->data)->_server, on_close_cb);
uv_loop_close(loop);
}

View File

@@ -1,197 +0,0 @@
#pragma once
#ifndef __TINYWEB_H__
#define __TINYWEB_H__
#ifdef _MSC_VER
//去掉 warning C4996: '***': The POSIX name for this item is deprecated.Instead, use the ISO C++ conformant name...
#ifndef _CRT_NONSTDC_NO_DEPRECATE
#define _CRT_NONSTDC_NO_DEPRECATE
#endif
#ifndef _CRT_SECURE_NO_WARNINGS
#define _CRT_SECURE_NO_WARNINGS
#endif
#include <libuv\include\uv.h>
#pragma comment(lib, "libuv.lib")
#pragma comment(lib, "ws2_32.lib")
#pragma comment(lib, "IPHLPAPI.lib")
#pragma comment(lib, "Psapi.lib")
#pragma comment(lib, "Userenv.lib")
# if defined(WIN32) && !defined(snprintf)
# define snprintf _snprintf
# endif
#else //__GNUGC__
#include <uv.h>
#endif
#include "tools.h"
#if TinyWeb_Function_Description //TinyWeb功能说明
auth lzpong 2016/11/24
Fonctionnalités basées sur la bibliothèque multiplateforme libuv
0. Encodage de document de réglage d assistance, utf-8 par défaut
1.Support via HTTP: GET / POST
2.Support Socket, connexion WebSocket
3. Assistance renvoyant une page d erreur 404
4. Assistance pour spécifier le répertoire racine (le répertoire dans lequel se trouve le programme par défaut)
5.Accès aux fichiers au format arbitraire (avec / sans extension, téléchargement de fichier)
Soutenir l accès statique aux pages Web: html / htm
Soutenir d autres fichiers statiques: js, css, png, jpeg / jpg, gif, ico, txt, xml, json, journal, wam, wav, mp3, mp4, apk, etc.
c. Prend en charge dautres formats de fichier, le type de fichier par défaut est: "application / octet-stream"
d. Accès aux fichiers de support sans extension
Paramètres de demande de prise en charge de plage pour télécharger des fichiers volumineux (plage: octets = tailleFrom- [tailleVers], prise en charge du calcul inverse négatif)
6. Soutenez la page dindex par défaut (index.html / index.htm), vous pouvez personnaliser les paramètres.
7. Liste de répertoires d assistance
8. Ne pas autoriser l accès aux fichiers ou dossiers racine
9. Rappel d assistance
Rappel après réception de la demande HTTP (cette fonction facilite le retour du programme à la fonction personnalisée), et la réponse http ordinaire est exécutée lorsque le rappel échoue ou renvoie 0
b. rappel de données WebSocket
Rappel de données c.socket
10.Support x64, supporte plus de 2G gros fichiers
11.Support cookie / setcookie
12. Prise en charge de l ajout d'informations d'en-tête personnalisées
13.Support POST données plus volumineuses (soutien post-contenu http envoyé par sous-traitance)
=============== stable, futur
14. en-tête http de soutien (http get)
#endif
#ifdef __cplusplus
extern "C" {
#endif
typedef struct tw_peerAddr {
uchar flag;//flag ([0 ~ 7]: [0] Reste connecté ou non [1] WebSocket [2] Cadre WebSocket ou
ushort port;
ushort fport;
size_t sk;
char ip[17];
char fip[17];
}tw_peerAddr;
typedef struct tw_reqHeads {
uchar method;//0:Socket 1:GET 2:POST
char host[260]; //IP:port, domain
char path[512]; //Chemin
char query[1500];//Paramètre
char* data; //Les données
char cookie[260];//cookie
size_t contentLen;//Content Lenth
size_t len; //Longueur des données reçues
long long Range_frm, Range_to;
}tw_reqHeads;
// Configuration du service
typedef struct tw_config {
//private data:
uv_tcp_t _server;
//public data:
uchar dirlist:1; // Autoriser ou non la liste des répertoires
char* doc_dir; // Répertoire racine Web, chemin absolu, avec la barre oblique '\' à la fin (uninx est '/'); répertoire dans lequel se trouve le fichier de programme par défaut
char* doc_index;// Nom du fichier de page d'accueil par défaut, séparé par des virgules; par défaut "index.html, index.htm"
char* ip; //服务的IP地址 is only ipV4, can be NULL or "" or "*", which means "0.0.0.0"
ushort port; // L'adresse IP du service est uniquement ipV4, peut être NULL ou "" ou "*", ce qui signifie "0.0.0.0"
char* charset; // Encodage de document (utf-8 par défaut)
// Les données
void* data;// Données utilisateur, telles que les pointeurs d'objet
// Accès client
char (*on_connect)(void* data, uv_stream_t* client, tw_peerAddr* pa);
// Retour non nul signifie que la demande de traitement a été traitée.
// Return 0 signifie qu'il n'y a pas de demande de traitement appropriée, il trouvera automatiquement le fichier / dossier, s'il n'est pas trouvé, envoie une réponse 404
// Cette fonction est pratique pour que le programme revienne à la fonction personnalisée.
// les membres chefs n'ont pas besoin d'être libres
// pa-> flag: octet de drapeau ([0 ~ 7]: [0] Rester connecté ou non [1] WebSocket [2] Si cadre de texte WebSocket
char (*on_request)(void* data, uv_stream_t* client, tw_peerAddr* pa, tw_reqHeads* heads);
// Les données de socket ou WebSocket peuvent être jugées par buf-> flag ou pa-> flag
// les membres de buf n'ont pas besoin d'être libres
// pa-> flag: octet de drapeau ([0 ~ 7]: [0] Rester connecté ou non [1] WebSocket [2] Si cadre de texte WebSocket
char (*on_data)(void* data, uv_stream_t* client, tw_peerAddr* pa, membuf_t* buf);
// Le socket a détecté une erreur (le lien peut être cassé à ce moment)
// Format du message d'erreur: "% d:% s,% s"
// pa-> flag: octet de drapeau ([0 ~ 7]: [0] Rester connecté ou non [1] WebSocket [2] Si cadre de texte WebSocket
char (*on_error)(void* data, uv_stream_t* client, tw_peerAddr* pa,int errcode, char* errstr);
// Le socket est fermé (le lien a été déconnecté pour le moment)
// indicateur: octet d'indicateur ([0 ~ 7]: [0] s'il faut conserver la connexion <la connexion non longue est http> [1] s'il faut WebSocket
char (*on_close)(void* data, uv_stream_t* client, tw_peerAddr* pa);
} tw_config;
//start web server, start with the config
//loop: if is NULL , it will be uv_default_loop()
//conf: the server config
// Si la valeur renvoyée n'est pas 0, cela indique le code d'erreur. Utilisez uv_err_name (n) et uv_strerror (n) pour afficher la chaîne de motif.
int tinyweb_start(uv_loop_t* loop, tw_config* conf);
//stop TinyWeb
//loop: if is NULL , it will be &uv_default_loop()
void tinyweb_stop(uv_loop_t* loop);
//=================================================
// Crée les champs et les valeurs de l'en-tête SetCookie
// set_cookie: zone de cache (au moins 42 + strlen (domaine) = strlen (chemin))
// ckLen: longueur de set_cookie
// expire: combien de secondes vont expirer
// domaine: domaine, nom de domaine ou adresse IP
// chemin: chemin, peut être heads-> chemin
void tw_make_setcookie(char* set_cookie, int ckLen, const char* key, const char* val, int expires, char* domain, char* path);
// créer un cookie d'en-tête
void tw_make_delcookie(char* del_cookie, int ckLen, char* key);
// Renvoie le format du contenu de la réponse HTTP (nécessite des données de retour gratuites)
// status: statut http, tel que: "200 OK"
// ext_heads: chaînes d'en-tête supplémentaires, telles que: "head1: this-is-head1 \ r \ nSetCookie: TINY_SSID = Tiny1531896250879; Expires = ... \ r \ n"
// contenu_type: type de fichier, tel que: "text / html"; vous pouvez l'obtenir en appelant tw_get_content_type ()
// contenu: utiliser les données de format de codage UTF-8, en particulier la réponse du type de fichier html
// longueur_contenu: 0 ou -1 calcule automatiquement la longueur du contenu (c_str, se termine par NULL)
// respone_size: si non NULL, vous pouvez obtenir la longueur des données envoyées, la taille de respone sera écrite pour demander
// retourne malloc () ed c_str, need free () par appelant
char* tw_format_http_respone(uv_stream_t* client, const char* status, const char* ext_heads, const char* content_type, const char* content, size_t content_length, size_t* respone_size);
// Retourne le type de fichier content_type en fonction de l'extension
// Vous pouvez passer le chemin / nom du fichier / extension
const char* tw_get_content_type(const char* fileExt);
// Envoie des données au client, s'il s'agit d'une connexion courte, la connexion sera fermée après l'envoi
// data: données à envoyer
// len: longueur des données, -1 calculera automatiquement la longueur des données
// need_copy_data: s'il faut copier des données
// need_free_data: si des données libres sont nécessaires, si need_copy_data est différent de zéro, ce paramètre est ignoré
void tw_send_data(uv_stream_t* client, const void* data, size_t len, char need_copy_data, char need_free_data);
// Envoie une réponse '200 OK', les données entrantes (u8data) ne seront pas libérées
// contenu_type: type de document Type de contenu
// u8data: données codées UTF-8
// longueur_contenu: longueur des données, calculée automatiquement lorsque la valeur est 0 ou -1 (strlen) (c_str, se termine par NULL)
// respone_size: Récupère la longueur des données envoyées par la réponse. Une valeur de 0 signifie que vous n'avez pas besoin de prendre cette longueur
void tw_send_200_OK(uv_stream_t* client, const char* ext_heads, const char* content_type, const void* u8data, size_t content_length, size_t* respone_size);
// fichier d'envoi de protocole http, asynchrone
// chemin_fichier: chemin du fichier
void tw_http_send_file(uv_stream_t* client, tw_reqHeads* heads, const char* ext_heads, const char* content_type, const char* file_path);
// Envoie une réponse 301, le chemin est déplacé de façon permanente
void tw_301_Moved(uv_stream_t* client, tw_reqHeads* heads, const char* ext_heads);
// Envoyer une réponse 302, le chemin est temporairement déplacé
void tw_302_Moved(uv_stream_t* client, tw_reqHeads* heads, const char* ext_heads);
// ferme la connexion du client
void tw_close_client(uv_stream_t* client);
#ifdef __cplusplus
} // extern "C"
#endif
#endif //__TINYWEB_H__

File diff suppressed because it is too large Load Diff

View File

@@ -1,263 +0,0 @@
#pragma once
#ifndef __TOOLS_H__
#define __TOOLS_H__
#ifdef __cplusplus
extern "C"
{
#endif
typedef unsigned char uchar;
typedef unsigned short ushort;
typedef unsigned int uint;
typedef unsigned long ulong;
typedef long long llong;
typedef unsigned long long ullong;
#define bitAdd(a, b) ((a) | (b))
#define bitHas(a, b) ((a) & (b))
#define bitRemove(a, b) ((a) & ~(b))
#define Min(a, b) (((a) > (b)) ? (b) : (a))
#define Max(a, b) (((a) > (b)) ? (a) : (b))
//-----------------------------------------------------------------------------------membuf c-str win/unix
typedef struct membuf_t
{
uchar *data;
size_t size;
size_t buffer_size;
} membuf_t;
// -----------------------------------------------------------------------------------
void membuf_init(membuf_t *buf, size_t initial_buffer_size);
void membuf_uninit(membuf_t *buf);
size_t membuf_append(membuf_t *buf, const char *str);
size_t membuf_append_data(membuf_t *buf, const void *data, size_t size);
size_t membuf_append_format(membuf_t *buf, const char *fmt, ...);
void membuf_insert(membuf_t *buf, size_t offset, void *data, size_t size);
void membuf_move(membuf_t *buf, size_t offset, size_t size);
void membuf_clear(membuf_t *buf, size_t maxSize);
void membuf_reserve(membuf_t *buf, size_t extra_size);
void membuf_trunc(membuf_t *buf);
#define _INLINE static inline
//-----------------------------------------------------------------------------------membuf c-str win/unix
_INLINE size_t membuf_append_byte(membuf_t *buf, uchar b)
{
return membuf_append_data(buf, &b, sizeof(b));
}
_INLINE size_t membuf_append_int(membuf_t *buf, int i)
{
return membuf_append_data(buf, &i, sizeof(i));
}
_INLINE size_t membuf_append_uint(membuf_t *buf, uint ui)
{
return membuf_append_data(buf, &ui, sizeof(ui));
}
_INLINE size_t membuf_append_long(membuf_t *buf, long i)
{
return membuf_append_data(buf, &i, sizeof(i));
}
_INLINE size_t membuf_append_ulong(membuf_t *buf, ulong ui)
{
return membuf_append_data(buf, &ui, sizeof(ui));
}
_INLINE size_t membuf_append_short(membuf_t *buf, short s)
{
return membuf_append_data(buf, &s, sizeof(s));
}
_INLINE size_t membuf_append_ushort(membuf_t *buf, ushort us)
{
return membuf_append_data(buf, &us, sizeof(us));
}
_INLINE size_t membuf_append_float(membuf_t *buf, float f)
{
return membuf_append_data(buf, &f, sizeof(f));
}
_INLINE size_t membuf_append_double(membuf_t *buf, double d)
{
return membuf_append_data(buf, &d, sizeof(d));
}
_INLINE size_t membuf_append_ptr(membuf_t *buf, void *ptr)
{
return membuf_append_data(buf, &ptr, sizeof(ptr));
}
//-----------------------------------------------------------------------------------File / folder detection win/unix
//获取工作目录路径,不带'/'
char *getWorkPath();
//获取程序文件所在路径,不带'/'
char *getProcPath();
//建立目录,递归建立 (mod: linux系统需要,权限模式,, windows系统不需要)
int makeDir(const char *path, int mod);
//路径是否存在(0不存在 1存在:文件 2存在:文件夹)
char isExist(const char *path);
//是否文件(1:是文件 0:非文件/不存在)
char isFile(const char *path);
//是否目录(1:是目录 0;非目录/不存在)
char isDir(const char *path);
//返回列表目录Json字符串,need free the return
//{"path":"/","files":[{"name":"file.txt","mtime":"2014-04-18 23:24:05","size":463,"type":"F"}]}
char *listDir(const char *fullpath, const char *reqPath);
//----------------------------------------------------------------------------------- Transcoding win/unix
//GB2312 to unicode(need free return) 返回字串(需要释放)长度为:实际长度+2,返回长度小于0为失败, 末尾\0\0占一字节
char *GB2U(char *pszGbs, uint *aLen);
//unicode to utf8(need free return) 返回字串(需要释放)长度为:实际长度+1,返回长度小于0为失败, 末尾\0占一字节
char *U2U8(char *wszUnicode, uint *aLen);
//utf8 to unicode(need free return) 返回字串(需要释放)长度为:实际长度+2,返回长度小于0为失败, 末尾\0\0占一字节
char *U82U(char *szU8, uint *aaLen);
//unicode to GB2312(need free return) 返回字串(需要释放)长度为:实际长度+1,返回长度小于0为失败, 末尾\0占一字节
char *U2GB(char *wszUnicode, uint *aLen);
//GB2312 to utf8(need free return) 返回字串(需要释放)长度为:实际长度+1,返回长度小于0为失败, 末尾\0占一字节
char *GB2U8(char *pszGbs, uint *aLen);
//utf8 to GB2312(need free return) 返回字串(需要释放)长度为:实际长度+1,返回长度小于0为失败, 末尾\0占一字节
char *U82GB(char *szU8, uint *aLen);
char *enc_u82u(char *data, uint *len);
char *enc_u2u8(char *data, uint *len);
//-----------------------------------------------------------------------------------Base64 encode decode win/unix
//Base64编码,需要释放返回值(need free return)
char *base64_Encode(const uchar *bytes_to_encode, uint in_len);
//Base64解码,需要释放返回值(need free return)
char *base64_Decode(const char *encoded_string);
//-----------------------------------------------------------------------------------SHA1计算摘要 win/unix
typedef struct SHA1_CONTEXT
{
char bFinal : 1; //是否计算完成
uint h0, h1, h2, h3, h4;
uint nblocks;
uint count;
uchar buf[64]; //返回SHA1结果不是字符串,是定长的20字节数据,中间可能有'\0',,要作为字符输出:printf("%02X ", sh.buf[i]);
} SHA1_CONTEXT;
//初始化/重置结构体
void hash1_Reset(SHA1_CONTEXT *hd);
//使用长度为 inlen 的 inbuf 内容更新消息摘要。
void hash1_Write(SHA1_CONTEXT *hd, uchar *inbuf, size_t inlen);
/*结束计算并返回摘要。
*句柄准备用于新的循环,但是向句柄添加字节将破坏返回的缓冲区。
*返回表示摘要的20个字节。
*/
void hash1_Final(SHA1_CONTEXT *hd);
//-----------------------------------------------------------------------------------url encode decode win/unix
//url编码 (len为url的长度)
char *url_encode(const char *url, uint *len);
//url解码
char *url_decode(char *url);
//-----------------------------------------------------------------------------------WebSocket win/unix
//WebSocket以文本传输的时候都为UTF - 8编码是WebSocket协议允许的唯一编码
//服务端收数据用
typedef struct WebSocketHandle
{
membuf_t buf; //原始帧数据
//data[0]
uchar isEof : 1; //是否是结束帧 data[0]>>7
uchar dfExt : 3; //是否有扩展定义 (data[0]>>4) & 0x7
/*控制码/帧类型 type 的定义data[0] & 0xF
0x0表示附加数据帧
0x1表示文本数据帧
0x2表示二进制数据帧
0x3-7暂时无定义为以后的非控制帧保留
0x8表示连接关闭
0x9表示ping
0xA表示pong
0xB-F暂时无定义为以后的控制帧保留 */
uchar type : 4;
} WebSocketHandle;
//传入http头,返回WebSocket握手Key,非http升级ws则返回NULL
//需要释放返回值(need free return)
char *WebSocketHandShak(const char *key);
//从帧中取得实际数据 (帧不应超过32位大小)
ulong WebSocketGetData(WebSocketHandle *handle, char *data, ulong len);
//转换为一个WebSocket帧,无mask (need free return)
//op:控制码/帧类型 0x0表示附加数据帧 0x1表示文本数据帧 ...
char *WebSocketMakeFrame(uchar *data, ulong *len, uchar op);
//-----------------------------------------------------------------------------------工具函数/杂项 win/unix
//获取IPv4地址 (第一个IPv4)
const char *GetIPv4();
//获取IPv6地址 (第一个IPv6)
const char *GetIPv6();
//获取网卡地址
const char *GetMacAddr();
typedef struct tm_u
{
int tm_sec; /*秒 seconds after the minute - [0,59] */
int tm_min; /*分 minutes after the hour - [0,59] */
int tm_hour; /*时 hours since midnight - [0,23] */
int tm_mday; /*天 day of the month - [1,31] */
int tm_mon; /*月 months since January - [1,12] */
int tm_year; /*年 years since 1900 */
int tm_wday; /*星期 days since Sunday - [0,6 0:周日] */
int tm_yday; /*年中的天数 days since January 1 - [0,365] */
int tm_isdst; /*夏令时标志 daylight savings time flag */
llong tm_vsec; /*时间戳 seconds from 1900/1/1 0:0:0 */
int tm_usec; /*微妙 microseconds */
} tm_u;
//获取当前时间信息
tm_u GetLocaTime();
//获取当天已逝去的秒数
size_t GetDaySecond();
//获取格林制GMT时间: "Wed, 18 Jul 2018 06:02:42 GMT"
//szDate: 存放GMT时间的缓存区(至少 char[30]),外部传入
//szLen: szDate的长度大小
//addSecond: 当前时间加上多少秒
char *getGmtTime(char *szDate, int szLen, int addSecond);
//字符串转换成时间戳(秒),字符串格式为:"2016-08-03 06:56:36"
llong str2stmp(const char *strTime);
//时间戳(秒)转换成字符串,字符串格式为:"2016-08-03 06:56:36"
char *stmp2str(llong t, char *str, int strlen);
//从头比较字符串,返回相同的长度,不区分大小写
size_t strinstr(const char *s1, const char *s2);
//int32 转二进制字符串
char *u2b(uint n);
//int64 转二进制字符串
char *u2b64(ullong n);
#ifdef __cplusplus
} // extern "C"
#endif
#endif //__TOOLS_H__

View File

@@ -1,57 +0,0 @@
/*!
* web-server.cpp
*
* Copyright (c) 2015-2019, NADAL Jean-Baptiste. All rights reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301 USA
*
* @Author: NADAL Jean-Baptiste
* @Date: 14/11/2019
*
*/
// This is an independent project of an individual developer. Dear PVS-Studio, please check it.
// PVS-Studio Static Code Analyzer for C, C++, C#, and Java: http://www.viva64.com
/*------------------------------- INCLUDES ----------------------------------*/
#include <event2/event.h>
#include "exit-handler.h"
/*! ----------------------------------------------------------------------------
* @fn ExitHandler
*
* @brief Constructor of the Exit Handler
*/
ExitHandler::ExitHandler(struct event_base *an_evt_loop) : m_evt_loop(an_evt_loop)
{
}
/*! ----------------------------------------------------------------------------
* @fn handleGet
*
* @brief Exit Handler HandleGet method
*/
bool ExitHandler::handleGet(CivetServer *a_server, struct mg_connection *a_conn)
{
mg_printf(a_conn,
"HTTP/1.1 200 OK\r\nContent-Type: "
"text/plain\r\nConnection: close\r\n\r\n");
mg_printf(a_conn, "Bye!\n");
event_base_loopbreak(m_evt_loop);
return true;
}

View File

@@ -1,47 +0,0 @@
/*!
* web-server.h
*
* Copyright (c) 2015-2019, NADAL Jean-Baptiste. All rights reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301 USA
*
* @Author: NADAL Jean-Baptiste
* @Date: 14/11/2019
*
*/
#ifndef _EXIT_HANDLER_H
#define _EXIT_HANDLER_H
/*------------------------------- INCLUDES ----------------------------------*/
#include "CivetServer.h"
/*---------------------------------- Deps -----------------------------------*/
/*--------------------------------- CLASS ----------------------------------*/
class ExitHandler : public CivetHandler
{
public:
ExitHandler(struct event_base *an_evt_loop);
bool handleGet(CivetServer *a_server, struct mg_connection *a_conn);
private:
struct event_base *m_evt_loop;
};
#endif /* _EXIT_HANDLER_H */