mirror of
https://gitee.com/acl-dev/acl.git
synced 2024-12-05 05:18:53 +08:00
509 lines
11 KiB
C++
509 lines
11 KiB
C++
#include "stdafx.h"
|
|
#include "configure.h"
|
|
#include "http_servlet.h"
|
|
|
|
static std::vector<chat_client*> __clients;
|
|
|
|
http_servlet::http_servlet(acl::socket_stream* stream, acl::session* session)
|
|
: acl::HttpServlet(stream, session)
|
|
, logined_(false)
|
|
{
|
|
|
|
}
|
|
|
|
http_servlet::~http_servlet(void)
|
|
{
|
|
if (!username_.empty())
|
|
oneLogout(username_);
|
|
}
|
|
|
|
bool http_servlet::doError(acl::HttpServletRequest&,
|
|
acl::HttpServletResponse& res)
|
|
{
|
|
res.setStatus(400);
|
|
res.setContentType("text/html; charset=");
|
|
// 发送 http 响应头
|
|
if (res.sendHeader() == false)
|
|
return false;
|
|
|
|
// 发送 http 响应体
|
|
acl::string buf;
|
|
buf.format("<root error='some error happened!' />\r\n");
|
|
(void) res.getOutputStream().write(buf);
|
|
return false;
|
|
}
|
|
|
|
bool http_servlet::doOther(acl::HttpServletRequest&,
|
|
acl::HttpServletResponse& res, const char* method)
|
|
{
|
|
res.setStatus(400);
|
|
res.setContentType("text/html; charset=");
|
|
// 发送 http 响应头
|
|
if (res.sendHeader() == false)
|
|
return false;
|
|
// 发送 http 响应体
|
|
acl::string buf;
|
|
buf.format("<root error='unkown request method %s' />\r\n", method);
|
|
(void) res.getOutputStream().write(buf);
|
|
return false;
|
|
}
|
|
|
|
bool http_servlet::doGet(acl::HttpServletRequest& req,
|
|
acl::HttpServletResponse& res)
|
|
{
|
|
return doPost(req, res);
|
|
}
|
|
|
|
bool http_servlet::doPost(acl::HttpServletRequest& req,
|
|
acl::HttpServletResponse& res)
|
|
{
|
|
// 如果需要 http session 控制,请打开下面注释,且需要保证
|
|
// 在 master_service.cpp 的函数 thread_on_read 中设置的
|
|
// memcached 服务正常工作
|
|
/*
|
|
const char* sid = req.getSession().getAttribute("sid");
|
|
if (*sid == 0)
|
|
req.getSession().setAttribute("sid", "xxxxxx");
|
|
sid = req.getSession().getAttribute("sid");
|
|
*/
|
|
|
|
// 如果需要取得浏览器 cookie 请打开下面注释
|
|
/*
|
|
|
|
*/
|
|
|
|
res.setContentType("text/html; charset=utf-8") // 设置响应字符集
|
|
.setKeepAlive(req.isKeepAlive()) // 设置是否保持长连接
|
|
.setContentEncoding(true) // 自动支持压缩传输
|
|
.setChunkedTransferEncoding(true); // 采用 chunk 传输方式
|
|
|
|
acl::string path;
|
|
path << var_cfg_html_path << "/" << "user.html";
|
|
|
|
acl::string buf;
|
|
if (acl::ifstream::load(path, &buf) == false)
|
|
{
|
|
logger_error("load %s error %s", path.c_str(), acl::last_serror());
|
|
return doError(req, res);
|
|
}
|
|
|
|
// 发送 http 响应体,因为设置了 chunk 传输模式,所以需要多调用一次
|
|
// res.write 且两个参数均为 0 以表示 chunk 传输数据结束
|
|
bool ret = res.write(buf) && res.write(NULL, 0);
|
|
if (ret == false)
|
|
remove(req.getSocketStream());
|
|
return ret;
|
|
}
|
|
|
|
bool http_servlet::doWebsocket(acl::HttpServletRequest& req,
|
|
acl::HttpServletResponse&)
|
|
{
|
|
acl::socket_stream& ss = req.getSocketStream();
|
|
acl::websocket in(ss), out(ss);
|
|
|
|
while (true)
|
|
{
|
|
if (in.read_frame_head() == false)
|
|
{
|
|
remove(ss);
|
|
printf("read_frame_head error\r\n");
|
|
return false;
|
|
}
|
|
|
|
bool ret;
|
|
unsigned char opcode = in.get_frame_opcode();
|
|
|
|
switch (opcode)
|
|
{
|
|
case acl::FRAME_PING:
|
|
ret = doPing(in, out);
|
|
break;
|
|
case acl::FRAME_PONG:
|
|
ret = doPong(in, out);
|
|
break;
|
|
case acl::FRAME_CLOSE:
|
|
ret = doClose(in, out);
|
|
break;
|
|
case acl::FRAME_TEXT:
|
|
case acl::FRAME_BINARY:
|
|
ret = doMsg(in, out);
|
|
break;
|
|
case acl::FRAME_CONTINUATION:
|
|
ret = false;
|
|
printf("1-invalid opcode: 0x%x\r\n", opcode);
|
|
break;
|
|
default:
|
|
printf("2-invalid opcode: 0x%x\r\n", opcode);
|
|
ret = false;
|
|
break;
|
|
}
|
|
|
|
if (ret == false)
|
|
{
|
|
printf("remove one, opcode: 0x%x\r\n", opcode);
|
|
remove(ss);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// XXX: NOT REACHED
|
|
return false;
|
|
}
|
|
|
|
bool http_servlet::doPing(acl::websocket& in, acl::websocket& out)
|
|
{
|
|
unsigned long long len = in.get_frame_payload_len();
|
|
if (len == 0)
|
|
{
|
|
if (out.send_frame_pong((const void*) NULL, 0) == false)
|
|
return false;
|
|
else
|
|
return true;
|
|
}
|
|
|
|
out.reset().set_frame_fin(true)
|
|
.set_frame_opcode(acl::FRAME_PONG)
|
|
.set_frame_payload_len(len);
|
|
|
|
char buf[8192];
|
|
while (true)
|
|
{
|
|
int ret = in.read_frame_data(buf, sizeof(buf) - 1);
|
|
if (ret == 0)
|
|
break;
|
|
if (ret < 0)
|
|
{
|
|
printf("read_frame_data error\r\n");
|
|
return false;
|
|
}
|
|
|
|
buf[ret] = 0;
|
|
printf("read: [%s]\r\n", buf);
|
|
if (out.send_frame_data(buf, ret) == false)
|
|
{
|
|
printf("send_frame_data error\r\n");
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool http_servlet::doPong(acl::websocket& in, acl::websocket&)
|
|
{
|
|
unsigned long long len = in.get_frame_payload_len();
|
|
if (len == 0)
|
|
return true;
|
|
|
|
char buf[8192];
|
|
while (true)
|
|
{
|
|
int ret = in.read_frame_data(buf, sizeof(buf) - 1);
|
|
if (ret == 0)
|
|
break;
|
|
if (ret < 0)
|
|
{
|
|
printf("read_frame_data error\r\n");
|
|
return false;
|
|
}
|
|
|
|
buf[ret] = 0;
|
|
printf("read: [%s]\r\n", buf);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool http_servlet::doClose(acl::websocket&, acl::websocket&)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
bool http_servlet::doMsg(acl::websocket& in, acl::websocket& out)
|
|
{
|
|
acl::string tbuf((size_t) in.get_frame_payload_len() + 1);
|
|
acl::socket_stream& conn = out.get_stream();
|
|
|
|
char buf[8192];
|
|
while (true)
|
|
{
|
|
int ret = in.read_frame_data(buf, sizeof(buf) - 1);
|
|
if (ret == 0)
|
|
break;
|
|
if (ret < 0)
|
|
{
|
|
printf("read_frame_data error\r\n");
|
|
return false;
|
|
}
|
|
|
|
tbuf.append(buf, ret);
|
|
}
|
|
|
|
std::vector<acl::string>& tokens = tbuf.split2("|");
|
|
const acl::string& cmd = tokens[0];
|
|
|
|
while (!logined_)
|
|
{
|
|
if (cmd.equal("login", false))
|
|
{
|
|
if (doLogin(conn, tokens) == false)
|
|
return false;
|
|
else
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
printf("invalid cmd: %s, tbuf: %s\r\n",
|
|
cmd.c_str(), tbuf.c_str());
|
|
return doStatus(conn, "please login first!");
|
|
}
|
|
}
|
|
|
|
if (cmd.equal("login", false))
|
|
return doStatus(conn, "already logined");
|
|
if (cmd.equal("chat", false))
|
|
{
|
|
if (doChat(conn, tokens) == false)
|
|
return false;
|
|
else
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
printf("unknown cmd: %s, msg: %s\r\n",
|
|
cmd.c_str(), tbuf.c_str());
|
|
acl::string info;
|
|
info.format("unknown cmd: %s", cmd.c_str());
|
|
return doStatus(conn, info);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool http_servlet::doStatus(acl::socket_stream& conn, const char* info)
|
|
{
|
|
acl::string buf;
|
|
buf.format("status|%s", info);
|
|
|
|
acl::websocket out(conn);
|
|
out.set_frame_fin(true)
|
|
.set_frame_opcode(acl::FRAME_TEXT)
|
|
.set_frame_payload_len(buf.size());
|
|
return out.send_frame_data(buf.c_str(), buf.size());
|
|
}
|
|
|
|
bool http_servlet::doLogin(acl::socket_stream& conn,
|
|
const std::vector<acl::string>& tokens)
|
|
{
|
|
// format: login|user
|
|
if (tokens.size() != 2)
|
|
{
|
|
printf("format: login|user\r\n");
|
|
return false;
|
|
}
|
|
|
|
const acl::string& user = tokens[1];
|
|
chat_client* client = find(user);
|
|
if (client != NULL)
|
|
{
|
|
if (&client->get_conn() == &conn)
|
|
{
|
|
printf("user: %s has beean already logined\r\n",
|
|
user.c_str());
|
|
return doStatus(conn, "already logined");
|
|
}
|
|
else
|
|
{
|
|
printf("ERROR, user: %s has beean already logined\r\n",
|
|
user.c_str());
|
|
const char* status = "login|error";
|
|
acl::websocket out(conn);
|
|
out.set_frame_fin(true)
|
|
.set_frame_opcode(acl::FRAME_TEXT)
|
|
.set_frame_payload_len(strlen(status));
|
|
out.send_frame_data(status);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
client = new chat_client(user, conn);
|
|
__clients.push_back(client);
|
|
username_ = user;
|
|
|
|
printf("status: ok, cmd: login, user: %s\r\n", user.c_str());
|
|
|
|
logined_ = true;
|
|
|
|
acl::string buf("users");
|
|
|
|
for (std::vector<chat_client*>::iterator it = __clients.begin();
|
|
it != __clients.end(); ++it)
|
|
{
|
|
client = *it;
|
|
buf << "|" << (*it)->get_name();
|
|
oneLogin(**it, user);
|
|
}
|
|
|
|
acl::websocket out(conn);
|
|
out.set_frame_fin(true)
|
|
.set_frame_opcode(acl::FRAME_TEXT)
|
|
.set_frame_payload_len(buf.size());
|
|
|
|
return out.send_frame_data(buf.c_str(), buf.size());
|
|
}
|
|
|
|
void http_servlet::oneLogin(const acl::string& user)
|
|
{
|
|
for (std::vector<chat_client*>::iterator it = __clients.begin();
|
|
it != __clients.end(); ++it)
|
|
{
|
|
if (user.equal((*it)->get_name()))
|
|
continue;
|
|
oneLogin(**it, user);
|
|
}
|
|
}
|
|
|
|
void http_servlet::oneLogin(chat_client& client, const acl::string& user)
|
|
{
|
|
acl::string buf;
|
|
buf << "login|" << user;
|
|
acl::websocket out(client.get_conn());
|
|
out.set_frame_fin(true)
|
|
.set_frame_opcode(acl::FRAME_TEXT)
|
|
.set_frame_payload_len(buf.size());
|
|
out.send_frame_data(buf.c_str(), buf.size());
|
|
}
|
|
|
|
void http_servlet::oneLogout(const acl::string& user)
|
|
{
|
|
for (std::vector<chat_client*>::iterator it = __clients.begin();
|
|
it != __clients.end(); ++it)
|
|
{
|
|
if (user.equal((*it)->get_name()))
|
|
continue;
|
|
oneLogout(**it, user);
|
|
}
|
|
}
|
|
|
|
void http_servlet::oneLogout(chat_client& client, const acl::string& user)
|
|
{
|
|
acl::string buf;
|
|
buf << "logout|" << user;
|
|
acl::websocket out(client.get_conn());
|
|
out.set_frame_fin(true)
|
|
.set_frame_opcode(acl::FRAME_TEXT)
|
|
.set_frame_payload_len(buf.size());
|
|
out.send_frame_data(buf.c_str(), buf.size());
|
|
}
|
|
|
|
bool http_servlet::doChat(acl::socket_stream& from_conn,
|
|
const std::vector<acl::string>& tokens)
|
|
{
|
|
// format: chat|msg|to_user
|
|
if (tokens.size() != 3)
|
|
{
|
|
printf("format: chat|msg|to_user\r\n");
|
|
return false;
|
|
}
|
|
|
|
chat_client* from = find(from_conn);
|
|
if (from == NULL)
|
|
{
|
|
printf("not found from: %p\r\n", &from_conn);
|
|
(void) doStatus(from_conn, "not logined!");
|
|
return false;
|
|
}
|
|
|
|
const acl::string& msg = tokens[1];
|
|
const acl::string& to_user = tokens[2];
|
|
|
|
if (to_user.equal("all", false))
|
|
{
|
|
for (std::vector<chat_client*>::iterator it = __clients.begin();
|
|
it != __clients.end(); ++it)
|
|
{
|
|
doChat(from->get_name(), **it, msg);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
chat_client* to_client = find(to_user);
|
|
if (to_client == NULL)
|
|
{
|
|
printf("no exist to_user: %s\r\n", to_user.c_str());
|
|
return true;
|
|
}
|
|
|
|
doChat(from->get_name(), *to_client, msg);
|
|
return true;
|
|
}
|
|
|
|
bool http_servlet::doChat(const acl::string& from_user,
|
|
chat_client& to_client, const acl::string& msg)
|
|
{
|
|
acl::string buf;
|
|
buf.format("chat|%s|%s", from_user.c_str(), msg.c_str());
|
|
|
|
acl::websocket out(to_client.get_conn());
|
|
out.set_frame_fin(true)
|
|
.set_frame_opcode(acl::FRAME_TEXT)
|
|
.set_frame_payload_len(buf.size());
|
|
|
|
if (out.send_frame_data(buf.c_str(), buf.size()) == false)
|
|
{
|
|
printf("----close socket: %d----\r\n",
|
|
to_client.get_conn().sock_handle());
|
|
shutdown(to_client.get_conn().sock_handle(), SHUT_RDWR);
|
|
//close(to_client.get_conn().sock_handle());
|
|
}
|
|
|
|
printf("status: ok, cmd: chat, msg: %s, to_user: %s\r\n",
|
|
msg.c_str(), to_client.get_name().c_str());
|
|
|
|
return true;
|
|
}
|
|
|
|
chat_client* http_servlet::find(const char* user)
|
|
{
|
|
for (std::vector<chat_client*>::iterator it = __clients.begin();
|
|
it != __clients.end(); ++it)
|
|
{
|
|
printf("user: %s, %s\r\n", user, (*it)->get_name().c_str());
|
|
if (*(*it) == user)
|
|
return *it;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
chat_client* http_servlet::find(acl::socket_stream& conn)
|
|
{
|
|
for (std::vector<chat_client*>::iterator it = __clients.begin();
|
|
it != __clients.end(); ++it)
|
|
{
|
|
acl::socket_stream* ss = &(*it)->get_conn();
|
|
if (ss == &conn)
|
|
return *it;
|
|
}
|
|
|
|
printf("not found connection: %p\r\n", &conn);
|
|
return NULL;
|
|
}
|
|
|
|
void http_servlet::remove(acl::socket_stream& conn)
|
|
{
|
|
for (std::vector<chat_client*>::iterator it = __clients.begin();
|
|
it != __clients.end(); ++it)
|
|
{
|
|
acl::socket_stream* ss = &(*it)->get_conn();
|
|
if (ss == &conn)
|
|
{
|
|
printf("removed: %s\r\n", (*it)->get_name().c_str());
|
|
__clients.erase(it);
|
|
return;
|
|
}
|
|
}
|
|
|
|
printf("not found connection: %p\r\n", &conn);
|
|
}
|