13 Star 69 Fork 22

johnsonyl/cpps

加入 Gitee
与超过 1400万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
文件
克隆/下载
cpps_socket_httpserver.cpp 18.76 KB
一键复制 编辑 原始数据 按行查看 历史
johnsonyl 提交于 2021-02-18 14:08 +08:00 . 2021-02-18 更新
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610
#include "cpps_socket_httpserver.h"
#include "cpps_socket_httpserver_request.h"
#include "cpps_socket_httpserver_controller.h"
#include "cpps_socket_httpserver_session.h"
#include "cpps_socket_httpserver_cachefile.h"
#include "cpps_socket_server_client.h"
#include "http_parser.h"
namespace cpps {
void cpps_string_real_tolower(std::string& s);
bool cpps_io_file_exists(std::string path);
std::string cpps_io_getfileext(std::string str);
std::string cpps_io_readfile(std::string filepath);
std::string cpps_getcwd();
cpps_integer cpps_time_gettime();
cpps_integer cpps_io_last_write_time(std::string path);
cpps_socket_httpserver::cpps_socket_httpserver()
{
c = NULL;
http_running = false;
}
cpps_socket_httpserver::~cpps_socket_httpserver()
{
stop();
}
void cpps_socket_httpserver::setcstate(cpps::C* cstate)
{
c = cstate;
}
cpps_socket_httpserver* cpps_socket_httpserver::setoption(cpps::object opt)
{
if (cpps::type(opt["ip"]) == CPPS_TSTRING) http_option.option_ip = object_cast<std::string>(opt["ip"]);
http_option.exceptionfunc = opt["exceptionfunc"];
http_option.notfoundfunc = opt["notfoundfunc"];
server_option.option_ip = http_option.option_ip;
return this;
}
cpps_socket_httpserver* cpps_socket_httpserver::listen(cpps::C* cstate, cpps::usint16 port)
{
setcstate(cstate);
cpps_socket_server::listen(cstate, port);
if (!sever_running) return this;
createuuidfunc = cpps::object::globals(c)["__socket_httpserver_createuuid"];
if(!createuuidfunc.isfunction())
throw cpps_error(__FILE__, __LINE__, cpps_error_normalerror, "__socket_httpserver_createuuid not found...\r\n");
SESSION_ENABLED = cpps::object::globals(c)["SESSION_ENABLED"].tobool();
SESSION_ENGINE = cpps::object::globals(c)["SESSION_ENGINE"].tostring();
SESSION_FILE_PATH = cpps::object::globals(c)["SESSION_FILE_PATH"].tostring();
SESSION_COOKIE_NAME = cpps::object::globals(c)["SESSION_COOKIE_NAME"].tostring();
SESSION_COOKIE_PATH = cpps::object::globals(c)["SESSION_COOKIE_PATH"].tostring();
SESSION_COOKIE_DOMAIN = cpps::object::globals(c)["SESSION_COOKIE_DOMAIN"].tostring();
SESSION_COOKIE_SECURE = cpps::object::globals(c)["SESSION_COOKIE_SECURE"].tobool();
SESSION_COOKIE_HTTPONLY = cpps::object::globals(c)["SESSION_COOKIE_HTTPONLY"].tobool();
SESSION_EXPIRE_AT_BROWSER_CLOSE = cpps::object::globals(c)["SESSION_EXPIRE_AT_BROWSER_CLOSE"].tobool();
SESSION_SAVE_EVERY_REQUEST = cpps::object::globals(c)["SESSION_SAVE_EVERY_REQUEST"].tobool();
SESSION_COOKIE_AGE = cpps::object::globals(c)["SESSION_COOKIE_AGE"].toint();
http_running = true;
return this;
}
unsigned char ToHex(unsigned char x)
{
return x > 9 ? x + 55 : x + 48;
}
unsigned char FromHex(unsigned char x)
{
unsigned char y = 0;
if (x >= 'A' && x <= 'Z') y = x - 'A' + 10;
else if (x >= 'a' && x <= 'z') y = x - 'a' + 10;
else if (x >= '0' && x <= '9') y = x - '0';
return y;
}
std::string urlencode(std::string str)
{
std::string strTemp = "";
size_t length = str.length();
for (size_t i = 0; i < length; i++)
{
if (isalnum((unsigned char)str[i]) ||
(str[i] == '-') ||
(str[i] == '_') ||
(str[i] == '.') ||
(str[i] == '~'))
strTemp += str[i];
else if (str[i] == ' ')
{
strTemp += '%';
strTemp += '2';
strTemp += '0';
}
else
{
strTemp += '%';
strTemp += ToHex((unsigned char)str[i] >> 4);
strTemp += ToHex((unsigned char)str[i] % 16);
}
}
return strTemp;
}
std::string urldecode(std::string str)
{
std::string strTemp = "";
size_t length = str.length();
for (size_t i = 0; i < length; i++)
{
if (str[i] == '+') strTemp += ' ';
else if (str[i] == '%')
{
unsigned char high = FromHex((unsigned char)str[++i]);
unsigned char low = FromHex((unsigned char)str[++i]);
strTemp += high * 16 + low;
}
else strTemp += str[i];
}
return strTemp;
}
void http_parse_query_str(std::string& uri, PARAMSLIST& list)
{
//a=10&b=20&c=&d=&=10
size_t i = 0;
while (i != std::string::npos && i < uri.size()) {
size_t ii = uri.find('=',i);
if (ii == std::string::npos) {
break;
}
if (ii == i + 1) {
break; //&= is wrong.need keys.
}
std::string field = uri.substr(i, ii - i);
ii++;
size_t iii = uri.find("&", ii);
if (iii == std::string::npos) {
list[field] = urldecode(uri.substr(ii));
break;
}
list[field] = urldecode(uri.substr(ii, iii - ii));
i = iii + 1;
}
}
void cpps_socket_httpserver::generic_handler(struct http_request& req, void* handler)
{
cpps_socket_httpserver* httpserver = (cpps_socket_httpserver*)handler;
cpps_create_class_var(cpps_socket_httpserver_request, httpserver->c, cpps_request_var, cpps_request_ptr);
cpps_request_ptr->server = httpserver;
cpps_request_ptr->socket_index = req.socket_index;
cpps_request_ptr->path = req.path;
cpps_request_ptr->keepalive = req.keepalive;
cpps_request_ptr->support_gzip = req.support_gzip;
if (!req.uri.empty())
{
cpps_request_ptr->uri = req.uri;
http_parse_query_str(req.uri, cpps_request_ptr->getlist);
for (auto item : cpps_request_ptr->getlist)
cpps_request_ptr->paramslist[item.first] = item.second;
}
for (auto item : req.headers)
cpps_request_ptr->input_headerslist.insert(PARAMSLIST::value_type(item.first, item.second));
size_t ibsize = req.body_len;
if (ibsize > 0)
{
cpps_request_ptr->input_buffer.append(req.body_buf, req.body_len);
if (cpps_request_ptr->isformdata()) {
cpps_request_ptr->parse_form_data(cpps_request_ptr->input_buffer);
}
else {
http_parse_query_str(cpps_request_ptr->input_buffer, cpps_request_ptr->postlist);
for (auto item : cpps_request_ptr->postlist)
cpps_request_ptr->paramslist[item.first] = item.second;
}
}
cpps_stack* takestack = httpserver->c->getcallstack()->empty() ? NULL : httpserver->c->getcallstack()->at(httpserver->c->getcallstack()->size() - 1);
try
{
//1.先找路由
cpps_string_real_tolower(cpps_request_ptr->path);
cpps::object func = httpserver->gethandlefunc(cpps_request_ptr->path);
if (cpps::type(func) == CPPS_TFUNCTION)
{
cpps_socket_httpserver_bindsession(httpserver, cpps_request_ptr);
dofunction(httpserver->c, func, cpps_request_var);
return;
}
//2.找controller.
cpps::object controller_object;
const cpps_socket_httpserver_controller controller = cpps_socket_httpserver_parse_controlloer(cpps_request_ptr->path);
if (cpps_request_ptr->path == "/") //找默认controller.
{
controller_object = httpserver->http_default_class_route;
}
else
{
controller_object = httpserver->getcontroller(controller.controllername);
}
if (cpps::type(controller_object) == CPPS_TCLASS)
{
cpps::cpps_cppsclass* cppsclass = cpps_to_cpps_cppsclass(controller_object.value);
if (cppsclass->iscppsclass())
{
std::string method = "index";
if (!controller.method.empty()) method = controller.method;
cpps_domain* leftdomain = NULL;
cpps_regvar *var = cppsclass->getvar(method, leftdomain, true,true);
if (var) //找到method了.
{
//创建变量.
cpps_socket_httpserver_bindsession(httpserver, cpps_request_ptr);
cpps::object cppsclassvar;
newcppsclasvar(httpserver->c, cppsclass, &cppsclassvar.getval());
doclassfunction(httpserver->c, cppsclassvar, var->getval(), cpps_request_var);
return;
}
}
}
//3.找本地文件(cache么?)
std::string filepath = cpps_getcwd() + httpserver->getwwwroot() + cpps_request_ptr->path;
std::string ext = cpps_io_getfileext(cpps_request_ptr->path);
if (ext.empty()) {
filepath += "/index.html";
ext = "html";
}
bool b = cpps_io_file_exists(filepath);
if (b)
{
std::string mime_type = httpserver->get_type(ext);
if (!mime_type.empty()) {
std::string content_type = "Content-Type";
std::string Server = "Server";
std::string ServerName = "Cpps Server";
std::string cache_control = "Cache-Control";
std::string cache_control_maxage = "max-age=2592000, public";
std::string Connection = "Connection";
std::string ConnectionType = "close";
cpps_request_ptr->real_addheader(cache_control, cache_control_maxage);
cpps_request_ptr->real_addheader(content_type, mime_type);
cpps_request_ptr->real_addheader(Server, ServerName);
cpps_request_ptr->real_addheader(Connection, ConnectionType);
cpps_integer last_write_time = cpps_io_last_write_time(filepath);
auto cachefile = httpserver->get_cachefile(cpps_request_ptr->path);
if (cachefile == NULL )
{
std::string content = cpps_io_readfile(filepath);
cachefile = httpserver->create_cachefile(cpps_request_ptr->path, content, last_write_time);
}
if (cachefile->getlast_write_time() != last_write_time) {
std::string content = cpps_io_readfile(filepath);
cachefile->setcontent(content);
}
cpps_request_ptr->append(cachefile->getcontent());
cpps_request_ptr->send(200, "OK");
return;
}
}
//4.找不到执行notfound
func = httpserver->http_option.notfoundfunc;
if (cpps::type(func) == CPPS_TFUNCTION)
{
dofunction(httpserver->c, func, cpps_request_var);
return;
}
//4.没有绑定notfound的话...
cpps_request_ptr->append("sorry,not found.\n");
cpps_request_ptr->send( 404, "NOT FOUND");
}
catch (cpps_error e)
{
//5.脚本异常的话...
std::string errmsg;
char errbuffer[8192];
sprintf(errbuffer, "error: %d : %s file:%s line:%d \nError stack information:\n", e.error(), e.what().c_str(), e.file().c_str(), e.line());
errmsg.append(errbuffer);
std::vector<cpps_stack*>* stacklist = httpserver->c->getcallstack();
for (std::vector<cpps_stack*>::reverse_iterator it = stacklist->rbegin(); it != stacklist->rend(); ++it)
{
cpps::cpps_stack* stack = *it;
sprintf(errbuffer, "file:%s [%d] %s\n", stack->f, stack->l, stack->func);
errmsg.append(errbuffer);
}
cpps_pop_stack_to_here(httpserver->c, takestack); //清栈
cpps::object func = httpserver->http_option.exceptionfunc;
if (cpps::type(func) == CPPS_TFUNCTION)
{
dofunction(httpserver->c, func, cpps_request_var, errmsg);
return;
}
//6.没有找到脚本异常的话...
cpps_request_ptr->append("sorry,not found.\n");
cpps_request_ptr->send(500, "SERVER ERROR");
}
}
void cpps_socket_httpserver::register_handlefunc(std::string path, cpps::object func)
{
cpps_string_real_tolower(path);
http_route_list[path] = func;
}
void cpps_socket_httpserver::register_controller(cpps::object cls, cpps_value defaultset)
{
bool bdefaultset = defaultset.tt == CPPS_TBOOLEAN ? defaultset.value.b : false;
if (cpps::type(cls) == CPPS_TCLASS)
{
cpps::cpps_cppsclass* cppsclass = cpps_to_cpps_cppsclass(cls.value);
if (cppsclass->iscppsclass())
{
std::string s = cppsclass->getclassname();
cpps_string_real_tolower(s);
http_class_route_list[s] = cls;
if(bdefaultset)
http_default_class_route = cls;
}
}
}
bool cpps_socket_httpserver::isrunning()
{
return http_running;
}
void cpps_socket_httpserver::run()
{
cpps_socket_server::run();
update_session();
}
void cpps_socket_httpserver::stop()
{
cpps_socket_server::stop();
for (auto session : session_list)
{
cpps_socket_httpserver_session* sess = session.second;
CPPSDELETE(sess);
}
session_list.clear();
auto it = cachefile_list.begin();
for (; it != cachefile_list.end(); ++it) {
cpps_socket_httpserver_cachefile* file = it->second;
CPPSDELETE( file);
}
cachefile_list.clear();
http_running = false;
}
void cpps_socket_httpserver::add_type(std::string mime, std::string ext)
{
mime_types.insert(http_mime_type::value_type(ext, mime));
}
std::string cpps_socket_httpserver::get_type(std::string ext)
{
return mime_types[ext];
}
cpps::object cpps_socket_httpserver::gethandlefunc(std::string path)
{
cpps::object ret;
auto it = http_route_list.find(path);
if (it != http_route_list.end()) {
ret = it->second;
}
return ret;
}
cpps::object cpps_socket_httpserver::getcontroller(std::string controllername)
{
cpps::object ret;
auto it = http_class_route_list.find(controllername);
if (it != http_class_route_list.end()) {
ret = it->second;
}
return ret;
}
cpps::cpps_socket_httpserver_session* cpps_socket_httpserver::create_seesion(cpps::C* c)
{
std::string sesionid = object_cast<std::string>(dofunction(c, createuuidfunc));
std::string csrf_token = object_cast<std::string>(dofunction(c, createuuidfunc));
if (sesionid.empty()) return NULL;
cpps_socket_httpserver_session* session = CPPSNEW( cpps_socket_httpserver_session)();
session->session_id = sesionid;
session->set_expire(cpps_time_gettime() + SESSION_COOKIE_AGE);
session->set("csrftoken", cpps_value(c,csrf_token));
session_list.insert(http_session_list::value_type(sesionid, session));
return session;
}
cpps::cpps_socket_httpserver_session* cpps_socket_httpserver::get_session(std::string session_id)
{
cpps_socket_httpserver_session* ret = NULL;
auto it = session_list.find(session_id);
if (it != session_list.end())
{
ret = it->second;
}
return ret;
}
cpps::cpps_socket_httpserver_cachefile* cpps_socket_httpserver::create_cachefile(std::string& filepath, std::string& content,cpps_integer last_write_time)
{
cpps_socket_httpserver_cachefile* cachefile = CPPSNEW( cpps_socket_httpserver_cachefile)();
cachefile->setfilepath(filepath);
cachefile->setcontent(content);
cachefile->setlast_write_time(last_write_time);
cachefile_list.insert(http_cachefile_list::value_type(filepath, cachefile));
return cachefile;
}
cpps::cpps_socket_httpserver_cachefile* cpps_socket_httpserver::get_cachefile(std::string filepath)
{
cpps_socket_httpserver_cachefile* ret = NULL;
auto it = cachefile_list.find(filepath);
if (it != cachefile_list.end())
{
ret = it->second;
}
return ret;
}
int header_field_cb(http_parser* p, const char* buf, size_t len)
{
http_request* req = (struct http_request*)p->data;
req->field.clear();
req->field.append(buf, len);
return 0;
}
int header_value_cb(http_parser* p, const char* buf, size_t len)
{
http_request* req = (struct http_request*)p->data;
std::string value;
value.append(buf, len);
if ("Accept-Encoding" == req->field) {
req->support_gzip = value.find("gzip") != std::string::npos;
}
req->headers[req->field] = value;
return 0;
}
int request_url_cb(http_parser* p, const char* buf, size_t len)
{
http_request* req = (struct http_request*)p->data;
std::string url;
url.append(buf, len);
size_t pos = url.find('?');
req->path.append(url.substr(0,pos));
if (pos != std::string::npos)
req->uri.append(url.substr(pos + 1));
return 0;
}
int body_cb(http_parser* p, const char* buf, size_t len)
{
return 0;
}
int headers_complete_cb(http_parser* p)
{
http_request* req = (struct http_request*)p->data;
req->keepalive = http_should_keep_alive(p);
return -1;
}
int message_complete_cb(http_parser* p)
{
return 0;
}
int chunk_header_cb(http_parser* parser)
{
return 0;
}
int chunk_complete_cb(http_parser* p)
{
return 0;
}
void cpps_socket_httpserver::onReadCallback(cpps_socket* sock, ssize_t nread, const uv_buf_t* buf)
{
cpps_socket_server_client* client = (cpps_socket_server_client*)sock;
if (nread > 0)
{
client->readbuffer(buf, nread);
http_request request;
http_parser parser;
request.support_gzip = false;
request.body_len = 0;
parser.data = (void*)&request;
http_parser_init(&parser, HTTP_REQUEST);
http_parser_settings settings = { nullptr, request_url_cb, nullptr, header_field_cb, header_value_cb,
headers_complete_cb, body_cb, message_complete_cb, chunk_header_cb, chunk_complete_cb };
size_t parsed = http_parser_execute(&parser, &settings, client->buffer.c_str(), client->buffer.size());
if (!request.headers["Content-Length"].empty())
{
auto content_length = atoll(request.headers["Content-Length"].c_str());
if ((client->buffer.size() - parsed) < (size_t)content_length)
{
return;
}
request.body_buf = client->buffer.c_str() + parsed + 1;
request.body_len = size_t(content_length);
}
else if ((parsed + 1) != client->buffer.size()) {
return;
}
//request.body.append(request.body_buf, request.body_len);
request.socket_index = client->socket_index;
generic_handler(request, this);
client->buffer.clear();
}
else if (nread < 0)
{
client->close("normal close.", onClsoeCallback);
}
}
void cpps_socket_httpserver::update_session()
{
auto it = session_list.begin();
for (; it != session_list.end();) {
auto session = it->second;
if (cpps_time_gettime() >= session->session_expire || session->needremove) {
CPPSDELETE(session);
it = session_list.erase(it);
}
else {
++it;
}
}
}
void cpps_socket_httpserver::cpps_socket_httpserver_bindsession(cpps_socket_httpserver* httpserver, cpps_socket_httpserver_request* cpps_request_ptr,bool create_session)
{
if (httpserver->SESSION_ENABLED) {
std::string sessionid = cpps_request_ptr->getcookie(httpserver->SESSION_COOKIE_NAME);
cpps_socket_httpserver_session* session = NULL;
if (!sessionid.empty()) {
//check session;
session = httpserver->get_session(sessionid);
if (session) {
session->set_expire(cpps_time_gettime() + httpserver->SESSION_COOKIE_AGE);//加时.
}
}
//不应该直接创建session.应该由后台创建.
if (!session && create_session) {
session = httpserver->create_seesion(httpserver->c);
cpps_request_ptr->setcookie(httpserver->SESSION_COOKIE_NAME, session->session_id, cpps::object::create(httpserver->c, httpserver->SESSION_COOKIE_PATH), cpps::object::create(httpserver->c, httpserver->SESSION_COOKIE_DOMAIN), cpps::object::create(httpserver->c, httpserver->SESSION_COOKIE_AGE));
object csrftoken = session->get("csrftoken", nil);
cpps_request_ptr->setcookie("csrftoken", csrftoken.tostring(), cpps::object::create(httpserver->c, httpserver->SESSION_COOKIE_PATH), cpps::object::create(httpserver->c, httpserver->SESSION_COOKIE_DOMAIN), cpps::object::create(httpserver->c, httpserver->SESSION_COOKIE_AGE));
}
cpps_request_ptr->setsession(session);
}
}
std::string cpps_socket_httpserver::getwwwroot()
{
return _wwwroot ;
}
void cpps_socket_httpserver::setwwwroot(std::string wwwroot)
{
_wwwroot = wwwroot + "/";
}
}
Loading...
马建仓 AI 助手
尝试更多
代码解读
代码找茬
代码优化
C++
1
https://gitee.com/cppscript/cpps.git
git@gitee.com:cppscript/cpps.git
cppscript
cpps
cpps
v1.0.2

搜索帮助