remove deprecated files
This commit is contained in:
22
patches/libwebsockets.patch
Normal file
22
patches/libwebsockets.patch
Normal 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
188
src/main.c
Normal 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;
|
||||||
|
}
|
||||||
55
src/main.cpp
55
src/main.cpp
@@ -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;
|
|
||||||
}
|
|
||||||
@@ -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_type:Content Type 文档类型
|
|
||||||
//u8data:utf-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);
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -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 d’autres 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 d’index 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__
|
|
||||||
1715
src/tinyweb/tools.c
1715
src/tinyweb/tools.c
File diff suppressed because it is too large
Load Diff
@@ -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__
|
|
||||||
@@ -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;
|
|
||||||
}
|
|
||||||
@@ -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 */
|
|
||||||
Reference in New Issue
Block a user