高性能http服务器,利用asio开发的轻量级高性能http服务器

/* //

* includes

*/

#include "tbox/tbox.h"

/* //

* macros

*/

// the httpd port

#define TB_DEMO_HTTPD_PORT (8080)

// the httpd session maximum count

#define TB_DEMO_HTTPD_SESSION_MAXN (100000)

// the httpd session timeout: 15s

#define TB_DEMO_HTTPD_SESSION_TIMEOUT (15000)

// the httpd session buffer maximum size

#define TB_DEMO_HTTPD_SESSION_BUFF_MAXN (8192)

/* //

* types

*/

// the httpd type

typedef struct __tb_demo_httpd_t

{

// the root directory

tb_char_t root[TB_PATH_MAXN];

// the listen port

tb_uint16_t port;

// the listen aico

tb_aico_ref_t aico;

// the aicp

tb_aicp_ref_t aicp;

}tb_demo_httpd_t;

// the httpd session type

typedef struct __tb_demo_httpd_session_t

{

// the aico

tb_aico_ref_t aico;

// the code

tb_size_t code;

// the file

tb_file_ref_t file;

// the path

tb_string_t path;

// the line

tb_string_t line;

// the index

tb_size_t index;

// the cache

tb_buffer_t cache;

// the httpd

tb_demo_httpd_t* httpd;

// the method

tb_size_t method;

// the file offset

tb_hize_t file_offset;

// the content size

tb_hize_t content_size;

// the version

tb_uint32_t version : 1;

// keep-alive?

tb_uint32_t balived : 1;

// the recv and send buffer

tb_byte_t buffer[TB_DEMO_HTTPD_SESSION_BUFF_MAXN];

}tb_demo_httpd_session_t;

/* //

* implementation

*/

static tb_char_t const* tb_demo_httpd_code_cstr(tb_size_t code)

{

// done

tb_char_t const* cstr = tb_null;

switch (code)

{

case TB_HTTP_CODE_OK: cstr = "OK"; break;

case TB_HTTP_CODE_BAD_REQUEST: cstr = "Bad Request"; break;

case TB_HTTP_CODE_NOT_FOUND: cstr = "Not Found"; break;

case TB_HTTP_CODE_NOT_IMPLEMENTED: cstr = "Not Implemented"; break;

case TB_HTTP_CODE_NOT_MODIFIED: cstr = "Not Modified"; break;

case TB_HTTP_CODE_INTERNAL_SERVER_ERROR: cstr = "Internal Server Error"; break;

default: break;

}

// check

tb_assert_abort(cstr);

// ok?

return cstr;

}

static tb_bool_t tb_demo_httpd_aico_clos(tb_aice_ref_t aice)

{

// check

tb_assert_and_check_return_val(aice && aice->aico && aice->code == TB_AICE_CODE_CLOS, tb_false);

// trace

tb_trace_d("aico[%p]: clos: %s", aice->aico, tb_state_cstr(aice->state));

// exit aico

tb_aico_exit(aice->aico);

// ok

return tb_true;

}

static tb_void_t tb_demo_httpd_session_exit(tb_demo_httpd_session_t* session)

{

// check

tb_assert_and_check_return(session);

// clos aico

if (session->aico) tb_aico_clos(session->aico, tb_demo_httpd_aico_clos, tb_null);

session->aico = tb_null;

// exit file

if (session->file) tb_file_exit(session->file);

session->file = tb_null;

// exit cache

tb_buffer_exit(&session->cache);

// exit line

tb_string_exit(&session->line);

// exit path

tb_string_exit(&session->path);

// clear status

session->code = TB_HTTP_CODE_OK;

session->index = 0;

session->httpd = tb_null;

session->method = TB_HTTP_METHOD_GET;

session->version = 1;

session->balived = 0;

session->file_offset = 0;

session->content_size = 0;

// exit it

tb_free(session);

}

static tb_void_t tb_demo_httpd_session_keep(tb_demo_httpd_session_t* session)

{

// check

tb_assert_and_check_return(session);

// exit file

if (session->file) tb_file_exit(session->file);

session->file = tb_null;

// clear cache

tb_buffer_clear(&session->cache);

// clear line

tb_string_clear(&session->line);

// clear path

tb_string_clear(&session->path);

// clear some status

session->code = TB_HTTP_CODE_OK;

session->index = 0;

session->method = TB_HTTP_METHOD_GET;

session->file_offset = 0;

session->content_size = 0;

}

static tb_demo_httpd_session_t* tb_demo_httpd_session_init(tb_demo_httpd_t* httpd, tb_aico_ref_t aico)

{

// check

tb_assert_and_check_return_val(httpd && httpd->aicp && aico, tb_null);

// done

tb_bool_t ok = tb_false;

tb_demo_httpd_session_t* session = tb_null;

do

{

// make session

session = tb_malloc0_type(tb_demo_httpd_session_t);

tb_assert_and_check_break(session);

// init session

session->httpd = httpd;

session->aico = aico;

session->code = TB_HTTP_CODE_OK;

session->version = 1;

session->balived = 0;

session->method = TB_HTTP_METHOD_GET;

session->file_offset = 0;

session->content_size = 0;

// init path

if (!tb_string_init(&session->path)) break;

// init line

if (!tb_string_init(&session->line)) break;

// init cache

if (!tb_buffer_init(&session->cache)) break;

// init timeout

tb_aico_timeout_set(session->aico, TB_AICO_TIMEOUT_SEND, TB_DEMO_HTTPD_SESSION_TIMEOUT);

tb_aico_timeout_set(session->aico, TB_AICO_TIMEOUT_RECV, TB_DEMO_HTTPD_SESSION_TIMEOUT);

// ok

ok = tb_true;

} while (0);

// failed?

if (!ok)

{

// exit session

if (session) tb_demo_httpd_session_exit(session);

session = tb_null;

}

// ok?

return session;

}

static tb_bool_t tb_demo_httpd_session_head_recv(tb_aice_ref_t aice);

static tb_void_t tb_demo_httpd_session_resp_exit(tb_demo_httpd_session_t* session)

{

// keep-alived?

tb_bool_t ok = tb_false;

if (session->balived && session->aico)

{

// keep session

tb_demo_httpd_session_keep(session);

// recv the header

ok = tb_aico_recv(session->aico, session->buffer, sizeof(session->buffer), tb_demo_httpd_session_head_recv, session);

}

// exit session

if (!ok) tb_demo_httpd_session_exit(session);

}

static tb_bool_t tb_demo_httpd_session_resp_send_file(tb_aice_ref_t aice)

{

// check

tb_assert_and_check_return_val(aice && aice->code == TB_AICE_CODE_SENDF, tb_false);

// the session

tb_demo_httpd_session_t* session = (tb_demo_httpd_session_t*)aice->priv;

tb_assert_and_check_return_val(session, tb_false);

// done

tb_bool_t ok = tb_false;

do

{

// ok?

tb_check_break(aice->state == TB_STATE_OK);

// trace

tb_trace_d("resp_send_file[%p]: real: %lu, size: %llu", aice->aico, aice->u.sendf.real, aice->u.sendf.size);

// save offset

session->file_offset += aice->u.sendf.real;

// continue to send it?

if (aice->u.sendf.real < aice->u.sendf.size)

{

// send file

ok = tb_aico_sendf(aice->aico, session->file, session->file_offset, aice->u.sendf.size - aice->u.sendf.real, tb_demo_httpd_session_resp_send_file, session);

}

} while (0);

// finished or closed or failed?

if (!ok)

{

// trace

tb_trace_d("resp_send_file[%p]: state: %s", aice->aico, tb_state_cstr(aice->state));

// exit response

tb_demo_httpd_session_resp_exit(session);

}

// ok

return tb_true;

}

static tb_bool_t tb_demo_httpd_session_resp_send_head(tb_aice_ref_t aice)

{

// check

tb_assert_and_check_return_val(aice && aice->aico && aice->code == TB_AICE_CODE_SEND, tb_false);

// the session

tb_demo_httpd_session_t* session = (tb_demo_httpd_session_t*)aice->priv;

tb_assert_and_check_return_val(session, tb_false);

// done

tb_bool_t ok = tb_false;

do

{

// ok?

tb_check_break(aice->state == TB_STATE_OK);

// trace

tb_trace_d("resp_send_head[%p]: real: %lu, size: %lu", aice->aico, aice->u.send.real, aice->u.send.size);

// check data

tb_assert_and_check_break(aice->u.send.data && aice->u.send.size);

// not finished?

if (aice->u.send.real < aice->u.send.size)

{

// continue to send it

ok = tb_aico_send(aice->aico, aice->u.send.data, aice->u.send.size - aice->u.send.real, tb_demo_httpd_session_resp_send_head, session);

}

// send file if exists

else if (session->file)

{

// send file

ok = tb_aico_sendf(aice->aico, session->file, session->file_offset, tb_file_size(session->file), tb_demo_httpd_session_resp_send_file, session);

}

} while (0);

// not continue?

if (!ok)

{

// trace

tb_trace_d("resp_send_head[%p]: state: %s", aice->aico, tb_state_cstr(aice->state));

// exit response

tb_demo_httpd_session_resp_exit(session);

}

// ok

return tb_true;

}

static tb_bool_t tb_demo_httpd_session_resp_send_done(tb_demo_httpd_session_t* session)

{

// check

tb_assert_and_check_return_val(session, tb_false);

// format the error info

tb_long_t size = tb_snprintf( (tb_char_t*)session->buffer

, sizeof(session->buffer) - 1

, "HTTP/1.%u %lu %s\r\n"

"Server: %s\r\n"

"Content-Type: text/html\r\n"

"Content-Length: %llu\r\n"

"Connection: %s\r\n"

"\r\n"

, session->version

, session->code

, tb_demo_httpd_code_cstr(session->code)

, TB_VERSION_SHORT_STRING

, session->file? tb_file_size(session->file) : 0

, session->balived? "keep-alive" : "close");

tb_assert_and_check_return_val(size > 0, tb_false);

// end

session->buffer[size] = '\0';

// trace

tb_trace_d("response[%p]: %s", session->aico, session->buffer);

// send the error info

return tb_aico_send(session->aico, session->buffer, size, tb_demo_httpd_session_resp_send_head, session);

}

static tb_bool_t tb_demo_httpd_session_reqt_done(tb_demo_httpd_session_t* session)

{

// check

tb_assert_and_check_return_val(session && session->aico, tb_false);

// done

tb_bool_t ok = tb_false;

do

{

// check

tb_check_break(session->code == TB_HTTP_CODE_OK);

tb_assert_and_check_break(session->httpd);

// get?

if (session->method == TB_HTTP_METHOD_GET)

{

// check path

tb_check_break_state(tb_string_size(&session->path), session->code, TB_HTTP_CODE_BAD_REQUEST);

// the path

tb_char_t const* path = tb_string_cstr(&session->path);

tb_check_break_state(!session->file && path, session->code, TB_HTTP_CODE_INTERNAL_SERVER_ERROR);

// the full path

tb_char_t full[TB_PATH_MAXN] = {0};

tb_long_t size = tb_snprintf(full, sizeof(full) - 1, "%s%s%s", session->httpd->root, path[0] != '/'? "/" : "", path);

tb_assert_abort(size > 0);

// end

full[size] = '\0';

// trace

tb_trace_d("reqt_done[%p]: full path: %s", session->aico, full);

// init file

session->file = tb_file_init(full, TB_FILE_MODE_RO | TB_FILE_MODE_BINARY | TB_FILE_MODE_ASIO);

tb_check_break_state(session->file, session->code, TB_HTTP_CODE_NOT_FOUND);

// send the file

if (!tb_demo_httpd_session_resp_send_done(session)) return tb_false;

// ok

ok = tb_true;

}

else

{

// not implemented

session->code = TB_HTTP_CODE_NOT_IMPLEMENTED;

break;

}

} while (0);

// error?

if (!ok)

{

// save code

if (session->code == TB_HTTP_CODE_OK) session->code = TB_HTTP_CODE_INTERNAL_SERVER_ERROR;

// send the error info

if (!tb_demo_httpd_session_resp_send_done(session)) return tb_false;

}

// ok

return tb_true;

}

static tb_bool_t tb_demo_httpd_session_head_done(tb_demo_httpd_session_t* session)

{

// check

tb_assert_and_check_return_val(session, tb_false);

// the line and size

tb_char_t const* line = tb_string_cstr(&session->line);

tb_size_t size = tb_string_size(&session->line);

tb_assert_and_check_return_val(line && size, tb_false);

// the first line?

tb_char_t const* p = line;

if (!session->index)

{

// check protocol

if (tb_stristr(line, "HTTP/1.1")) session->version = 1;

else if (tb_stristr(line, "HTTP/1.0")) session->version = 0;

// bad request?

else

{

// save code

session->code = TB_HTTP_CODE_BAD_REQUEST;

return tb_false;

}

// parse get

if (!tb_strnicmp(line, "GET", 3))

{

// save the method

session->method = TB_HTTP_METHOD_GET;

// skip the method

p += 3;

}

// parse post

else if (!tb_strnicmp(line, "POST", 4))

{

// save the method

session->method = TB_HTTP_METHOD_POST;

// save code

session->code = TB_HTTP_CODE_NOT_IMPLEMENTED;

// skip the method

p += 4;

}

else

{

// trace

tb_trace_e("the method: %s is not supported now!", line);

// save code

session->code = TB_HTTP_CODE_NOT_IMPLEMENTED;

}

// get or post? parse the path

if ( session->method == TB_HTTP_METHOD_GET

|| session->method == TB_HTTP_METHOD_POST)

{

// skip space

while (*p && tb_isspace(*p)) p++;

// append path

while (*p && !tb_isspace(*p)) tb_string_chrcat(&session->path, *p++);

}

}

// key: value?

else

{

// seek to value

while (*p && *p != ':') p++;

tb_assert_and_check_return_val(*p, tb_false);

p++; while (*p && tb_isspace(*p)) p++;

// no value

tb_check_return_val(*p, tb_true);

// parse content-length

if (!tb_strnicmp(line, "Content-Length", 14))

{

// the content size

session->content_size = tb_stou64(p);

}

// parse connection

else if (!tb_strnicmp(line, "Connection", 10))

{

// keep-alive?

session->balived = !tb_stricmp(p, "keep-alive")? 1 : 0;

}

// parse accept-encoding

else if (!tb_strnicmp(line, "Accept-Encoding", 15))

{

}

// parse accept

else if (!tb_strnicmp(line, "Accept", 6))

{

}

// parse cookie

else if (!tb_strnicmp(line, "Cookie", 6))

{

}

// parse range

else if (!tb_strnicmp(line, "Range", 5))

{

// save code

session->code = TB_HTTP_CODE_NOT_IMPLEMENTED;

}

// parse host

else if (!tb_strnicmp(line, "Host", 4))

{

}

}

// ok

return tb_true;

}

static tb_bool_t tb_demo_httpd_session_head_recv(tb_aice_ref_t aice)

{

// check

tb_assert_and_check_return_val(aice && aice->aico && aice->code == TB_AICE_CODE_RECV, tb_false);

// the session

tb_demo_httpd_session_t* session = (tb_demo_httpd_session_t*)aice->priv;

tb_assert_and_check_return_val(session, tb_false);

// done

tb_bool_t state = tb_false;

do

{

// ok?

tb_check_break(aice->state == TB_STATE_OK);

// trace

tb_trace_d("head_recv[%p]: real: %lu, size: %lu", aice->aico, aice->u.recv.real, aice->u.recv.size);

// check

tb_assert_and_check_break(aice->u.recv.data);

// done

tb_long_t ok = 0;

tb_char_t ch = '\0';

tb_char_t const* p = (tb_char_t const*)aice->u.recv.data;

tb_char_t const* e = p + aice->u.recv.size;

while (p < e)

{

// the char

ch = *p++;

// error end?

if (!ch)

{

ok = -1;

break;

}

// append char to line

if (ch != '\n') tb_string_chrcat(&session->line, ch);

// is line end?

else

{

// strip '\r' if exists

tb_char_t const* pb = tb_string_cstr(&session->line);

tb_size_t pn = tb_string_size(&session->line);

if (!pb || !pn)

{

ok = -1;

tb_assert(0);

break;

}

// line end? strip '\r\n'

if (pb[pn - 1] == '\r') tb_string_strip(&session->line, pn - 1);

// trace

tb_trace_d("head_recv[%p]: %s", aice->aico, pb);

// end?

if (!tb_string_size(&session->line))

{

// ok

ok = 1;

break;

}

// done the head request

if (!tb_demo_httpd_session_head_done(session))

{

// error

ok = -1;

break;

}

// clear line

tb_string_clear(&session->line);

// update index

session->index++;

}

}

// continue?

if (!ok)

{

// recv the header

if (!tb_aico_recv(aice->aico, session->buffer, sizeof(session->buffer), tb_demo_httpd_session_head_recv, session)) break;

}

// end?

else if (ok > 0)

{

// trace

tb_trace_d("head_recv[%p]: end, left: %lu", aice->aico, e - p);

// save the left data to the cache

tb_buffer_memncpy(&session->cache, (tb_byte_t const*)p, e - p);

// trace

tb_trace_d("head_recv[%p]: ok", aice->aico);

// done request

if (!tb_demo_httpd_session_reqt_done(session)) break;

}

// failed?

else

{

// trace

tb_trace_e("head_recv[%p]: failed", aice->aico);

break;

}

// ok

state = tb_true;

} while (0);

// closed or failed?

if (!state)

{

// trace

tb_trace_d("head_recv[%p]: state: %s", aice->aico, tb_state_cstr(aice->state));

// exit session

tb_demo_httpd_session_exit(session);

}

// ok

return tb_true;

}

static tb_void_t tb_demo_httpd_exit(tb_demo_httpd_t* httpd)

{

// check

tb_assert_and_check_return(httpd);

// trace

tb_trace_d("exit");

// clear aico

httpd->aico = tb_null;

// exit it

tb_free(httpd);

}

static tb_demo_httpd_t* tb_demo_httpd_init(tb_char_t const* root)

{

// done

tb_bool_t ok = tb_false;

tb_demo_httpd_t* httpd = tb_null;

do

{

// make httpd

httpd = tb_malloc0_type(tb_demo_httpd_t);

tb_assert_and_check_break(httpd);

// init root

if (root) tb_strlcpy(httpd->root, root, sizeof(httpd->root));

else tb_directory_curt(httpd->root, sizeof(httpd->root));

httpd->root[sizeof(httpd->root) - 1] = '\0';

tb_assert_and_check_break(tb_file_info(httpd->root, tb_null));

// init port

httpd->port = TB_DEMO_HTTPD_PORT;

// trace

tb_trace_d("init: %s: %u", httpd->root, httpd->port);

// init aicp

httpd->aicp = tb_aicp();

tb_assert_and_check_break(httpd->aicp);

// init aico

httpd->aico = tb_aico_init(httpd->aicp);

tb_assert_and_check_break(httpd->aico);

// open aico

if (!tb_aico_open_sock_from_type(httpd->aico, TB_SOCKET_TYPE_TCP)) break;

// bind port

if (!tb_socket_bind(tb_aico_sock(httpd->aico), tb_null, httpd->port)) break;

// listen sock

if (!tb_socket_listen(tb_aico_sock(httpd->aico), TB_DEMO_HTTPD_SESSION_MAXN >> 2)) break;

// ok

ok = tb_true;

} while (0);

// failed?

if (!ok)

{

// exit httpd

if (httpd) tb_demo_httpd_exit(httpd);

httpd = tb_null;

}

// ok?

return httpd;

}

static tb_bool_t tb_demo_httpd_acpt(tb_aice_ref_t aice)

{

// check

tb_assert_and_check_return_val(aice && aice->aico && aice->code == TB_AICE_CODE_ACPT, tb_false);

// the httpd

tb_demo_httpd_t* httpd = (tb_demo_httpd_t*)aice->priv;

tb_assert_and_check_return_val(httpd && httpd->aicp, tb_false);

// done

tb_bool_t ok = tb_false;

tb_demo_httpd_session_t* session = tb_null;

do

{

// ok?

tb_check_break(aice->state == TB_STATE_OK);

// check

tb_assert_and_check_break(aice->u.acpt.aico);

// trace

tb_trace_d("acpt[%p]: aico: %p, addr: %u.%u.%u.%u, port: %u", aice->aico, aice->u.acpt.aico, tb_ipv4_u8x4(aice->u.acpt.addr), aice->u.acpt.port);

// init the session

session = tb_demo_httpd_session_init(httpd, aice->u.acpt.aico);

tb_assert_and_check_break(session && session->aico);

// recv the header

if (!tb_aico_recv(session->aico, session->buffer, sizeof(session->buffer), tb_demo_httpd_session_head_recv, session)) break;

// ok

ok = tb_true;

} while (0);

// failed?

if (!ok)

{

// trace

tb_trace_d("acpt[%p]: state: %s", aice->aico, tb_state_cstr(aice->state));

// exit session

if (session) tb_demo_httpd_session_exit(session);

session = tb_null;

// clos aico

if (aice->aico) tb_aico_clos(aice->aico, tb_demo_httpd_aico_clos, tb_null);

}

// ok

return tb_true;

}

static tb_void_t tb_demo_httpd_done(tb_demo_httpd_t* httpd)

{

// check

tb_assert_and_check_return(httpd && httpd->aicp && httpd->aico);

// done listen

if (!tb_aico_acpt(httpd->aico, tb_demo_httpd_acpt, httpd)) return ;

// wait some time

getchar();

}

/* //

* main

*/

tb_int_t main(tb_int_t argc, tb_char_t** argv)

{

// init tbox

if (!tb_init(tb_null, tb_null, 0)) return 0;

// init httpd

tb_demo_httpd_t* httpd = tb_demo_httpd_init(argv[1]);

if (httpd)

{

// done httpd

tb_demo_httpd_done(httpd);

// exit httpd

tb_demo_httpd_exit(httpd);

}

// exit tbox

tb_exit();

return 0;

}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值