static tb_bool_t tb_demo_http_session_resp_send(tb_demo_http_session_ref_t session, tb_char_t const* cstr) { // check tb_assert_and_check_return_val(session && session->sock, tb_false); // make the response header tb_long_t size = tb_snprintf( (tb_char_t*)session->data , sizeof(session->data) , "HTTP/1.1 %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" "%s" , session->code , tb_demo_http_session_code_cstr(session->code) , TB_VERSION_SHORT_STRING , session->file? tb_file_size(session->file) : (cstr? tb_strlen(cstr) : 0) , session->keep_alive? "keep-alive" : "close" , cstr? cstr : ""); tb_assert_and_check_return_val(size > 0, tb_false); // end session->data[size] = 0; // send the response header if (!tb_demo_http_session_data_send(session->sock, session->data, size)) return tb_false; // send the response file if exists if (session->file && !tb_demo_http_session_file_send(session->sock, session->file)) return tb_false; // ok return tb_true; }
static tb_bool_t tb_demo_http_session_file_send(tb_socket_ref_t sock, tb_file_ref_t file) { // check tb_assert_and_check_return_val(sock && file, tb_false); // send data tb_hize_t send = 0; tb_hize_t size = tb_file_size(file); tb_long_t wait = 0; while (send < size) { // send it tb_hong_t real = tb_socket_sendf(sock, file, send, size - send); // has data? if (real > 0) { send += real; wait = 0; } // no data? wait it else if (!real && !wait) { // wait it wait = tb_socket_wait(sock, TB_SOCKET_EVENT_SEND, TB_DEMO_TIMEOUT); tb_assert_and_check_break(wait >= 0); } // failed or end? else break; } // ok? return send == size; }
tb_bool_t tb_file_copy(tb_char_t const* path, tb_char_t const* dest) { // check tb_assert_and_check_return_val(path && dest, tb_false); #ifdef TB_CONFIG_POSIX_HAVE_COPYFILE // the full path tb_char_t full0[TB_PATH_MAXN]; path = tb_path_absolute(path, full0, TB_PATH_MAXN); tb_assert_and_check_return_val(path, tb_false); // the dest path tb_char_t full1[TB_PATH_MAXN]; dest = tb_path_absolute(dest, full1, TB_PATH_MAXN); tb_assert_and_check_return_val(dest, tb_false); // attempt to copy it directly if (!copyfile(path, dest, 0, COPYFILE_ALL)) return tb_true; else { // attempt to copy it again after creating directory tb_char_t dir[TB_PATH_MAXN]; if (tb_directory_create(tb_path_directory(dest, dir, sizeof(dir)))) return !copyfile(path, dest, 0, COPYFILE_ALL); } // failed return tb_false; #else tb_int_t ifd = -1; tb_int_t ofd = -1; tb_bool_t ok = tb_false; do { // get the absolute source path tb_char_t data[8192]; path = tb_path_absolute(path, data, sizeof(data)); tb_assert_and_check_break(path); // get stat.st_mode first #ifdef TB_CONFIG_POSIX_HAVE_STAT64 struct stat64 st = {0}; if (stat64(path, &st)) break; #else struct stat st = {0}; if (stat(path, &st)) break; #endif // open source file ifd = open(path, O_RDONLY); tb_check_break(ifd >= 0); // get the absolute source path dest = tb_path_absolute(dest, data, sizeof(data)); tb_assert_and_check_break(dest); // open destinate file and copy file mode ofd = open(dest, O_RDWR | O_CREAT | O_TRUNC, st.st_mode & (S_IRWXU | S_IRWXG | S_IRWXO)); if (ofd < 0) { // attempt to open it again after creating directory tb_char_t dir[TB_PATH_MAXN]; if (tb_directory_create(tb_path_directory(dest, dir, sizeof(dir)))) ofd = open(dest, O_RDWR | O_CREAT | O_TRUNC, st.st_mode & (S_IRWXU | S_IRWXG | S_IRWXO)); } tb_check_break(ofd >= 0); // get file size tb_hize_t size = tb_file_size(tb_fd2file(ifd)); // init write size tb_hize_t writ = 0; // attempt to copy file using `sendfile` #ifdef TB_CONFIG_POSIX_HAVE_SENDFILE while (writ < size) { off_t seek = writ; tb_hong_t real = sendfile(ofd, ifd, &seek, (size_t)(size - writ)); if (real > 0) writ += real; else break; } /* attempt to copy file directly if sendfile failed * * sendfile() supports regular file only after "since Linux 2.6.33". */ if (writ != size) { lseek(ifd, 0, SEEK_SET); lseek(ofd, 0, SEEK_SET); } else { ok = tb_true; break; } #endif // copy file using `read` and `write` writ = 0; while (writ < size) { // read some data tb_int_t real = read(ifd, data, (size_t)tb_min(size - writ, sizeof(data))); if (real > 0) { real = write(ofd, data, real); if (real > 0) writ += real; else break; } else break; } // ok? ok = (writ == size); } while (0); // close source file if (ifd >= 0) close(ifd); ifd = -1; // close destinate file if (ofd >= 0) close(ofd); ofd = -1; // ok? return ok; #endif }
/* ////////////////////////////////////////////////////////////////////////////////////// * main */ tb_int_t tb_demo_asio_aiopd_main(tb_int_t argc, tb_char_t** argv) { // check tb_assert_and_check_return_val(argv[1], 0); // done tb_socket_ref_t sock = tb_null; tb_aiop_ref_t aiop = tb_null; do { // init sock sock = tb_socket_init(TB_SOCKET_TYPE_TCP); tb_assert_and_check_break(sock); // init aiop aiop = tb_aiop_init(16); tb_assert_and_check_break(aiop); // bind if (!tb_socket_bind(sock, tb_null, 9090)) break; // listen sock if (!tb_socket_listen(sock, 20)) break; // addo sock if (!tb_aiop_addo(aiop, sock, TB_AIOE_CODE_ACPT, tb_null)) break; // accept tb_aioe_t list[16]; while (1) { // wait tb_long_t objn = tb_aiop_wait(aiop, list, 16, -1); tb_assert_and_check_break(objn >= 0); // walk list tb_size_t i = 0; for (i = 0; i < objn; i++) { // the aioo tb_aioo_ref_t aioo = list[i].aioo; // check tb_assert_and_check_break(aioo && tb_aioo_sock(aioo)); // acpt? if (list[i].code & TB_AIOE_CODE_ACPT) { // done acpt tb_bool_t ok = tb_false; tb_demo_context_t* context = tb_null; do { // make context context = tb_malloc0_type(tb_demo_context_t); tb_assert_and_check_break(context); // init sock context->sock = tb_socket_accept(tb_aioo_sock(aioo), tb_null, tb_null); tb_assert_and_check_break(context->sock); // init file context->file = tb_file_init(argv[1], TB_FILE_MODE_RO); tb_assert_and_check_break(context->file); // init data context->data = tb_malloc_bytes(TB_DEMO_FILE_READ_MAXN); tb_assert_and_check_break(context->data); // addo sock context->aioo = tb_aiop_addo(aiop, context->sock, TB_AIOE_CODE_SEND, context); tb_assert_and_check_break(context->aioo); // trace tb_trace_i("acpt[%p]: ok", context->sock); // init left context->left = tb_file_size(context->file); // done read tb_long_t real = tb_file_read(context->file, context->data, tb_min((tb_size_t)context->left, TB_DEMO_FILE_READ_MAXN)); tb_assert_and_check_break(real > 0); // save size context->left -= real; // trace // tb_trace_i("read[%p]: real: %ld", context->file, real); // done send context->send = real; real = tb_socket_send(context->sock, context->data + context->real, context->send - context->real); if (real >= 0) { // save real context->real += real; // trace // tb_trace_i("send[%p]: real: %ld", context->sock, real); } else { // trace tb_trace_i("send[%p]: closed", context->sock); break; } // ok ok = tb_true; } while (0); // failed or closed? if (!ok) { // exit context tb_demo_context_exit(aiop, context); break; } } // writ? else if (list[i].code & TB_AIOE_CODE_SEND) { // the context tb_demo_context_t* context = (tb_demo_context_t*)list[i].priv; tb_assert_and_check_break(context); // continue to send it if not finished if (context->real < context->send) { // done send tb_long_t real = tb_socket_send(tb_aioo_sock(aioo), context->data + context->real, context->send - context->real); if (real > 0) { // save real context->real += real; // trace // tb_trace_i("send[%p]: real: %ld", tb_aioo_sock(aioo), real); } else { // trace tb_trace_i("send[%p]: closed", tb_aioo_sock(aioo)); // exit context tb_demo_context_exit(aiop, context); break; } } // finished? read file else if (context->left) { // init context->real = 0; context->send = 0; // done read tb_size_t tryn = 1; tb_long_t real = 0; while (!(real = tb_file_read(context->file, context->data, tb_min((tb_size_t)context->left, TB_DEMO_FILE_READ_MAXN))) && tryn--); if (real > 0) { // save left context->left -= real; // trace // tb_trace_i("read[%p]: real: %ld", context->file, real); // done send context->send = real; real = tb_socket_send(tb_aioo_sock(aioo), context->data, context->send); if (real >= 0) { // save real context->real += real; // trace // tb_trace_i("send[%p]: real: %ld", tb_aioo_sock(aioo), real); } else { // trace tb_trace_i("send[%p]: closed", tb_aioo_sock(aioo)); // exit context tb_demo_context_exit(aiop, context); break; } } else { // trace tb_trace_i("read[%p]: closed", tb_aioo_sock(aioo)); // exit context tb_demo_context_exit(aiop, context); break; } } else { // trace tb_trace_i("read[%p]: closed", tb_aioo_sock(aioo)); // exit context tb_demo_context_exit(aiop, context); break; } } // error? else { tb_trace_i("aioe[%p]: unknown code: %lu", tb_aioo_sock(aioo), list[i].code); break; } } } } while (0); // trace tb_trace_i("end"); // exit socket if (sock) tb_socket_exit(sock); // exit aiop if (aiop) tb_aiop_exit(aiop); // end return 0; }
static tb_bool_t tb_demo_sock_acpt_func(tb_aice_ref_t aice) { // check tb_assert_and_check_return_val(aice && aice->code == TB_AICE_CODE_ACPT, tb_false); // the file path tb_char_t const* path = (tb_char_t const*)aice->priv; tb_assert_and_check_return_val(path, tb_false); // the aicp tb_aicp_ref_t aicp = tb_aico_aicp(aice->aico); tb_assert_and_check_return_val(aicp, tb_false); // acpt ok? if (aice->state == TB_STATE_OK) { // trace tb_trace_i("acpt[%p]: %p", aice->aico, aice->u.acpt.aico); // done tb_bool_t ok = tb_false; tb_demo_context_t* context = tb_null; do { // make context context = tb_malloc0_type(tb_demo_context_t); tb_assert_and_check_break(context); #ifdef TB_DEMO_MODE_SENDF // init file context->file = tb_file_init(path, TB_FILE_MODE_RO | TB_FILE_MODE_ASIO); tb_assert_and_check_break(context->file); // init sock aico context->aico[0] = aice->u.acpt.aico; tb_assert_and_check_break(context->aico[0]); // post sendf from file if (!tb_aico_sendf(context->aico[0], context->file, 0ULL, tb_file_size(context->file), tb_demo_sock_sendf_func, context)) break; #else // init data context->data = tb_malloc_bytes(TB_DEMO_FILE_READ_MAXN); tb_assert_and_check_break(context->file && context->data); // init sock aico context->aico[0] = aice->u.acpt.aico; tb_assert_and_check_break(context->aico[0]); // init file aico context->aico[1] = tb_aico_init(aicp); tb_assert_and_check_break(context->aico[1]); // open file aico if (!tb_aico_open_file_from_path(context->aico[1], path, TB_FILE_MODE_RO)) break; // post read from file if (!tb_aico_read(context->aico[1], context->size, context->data, TB_DEMO_FILE_READ_MAXN, tb_demo_file_read_func, context)) break; #endif // ok ok = tb_true; } while (0); // failed? if (!ok) { // exit context if (context) tb_demo_context_exit(context); } } // failed? else { // exit loop tb_trace_i("acpt[%p]: state: %s", aice->aico, tb_state_cstr(aice->state)); // clos aico if (aice->aico) tb_aico_clos(aice->aico, tb_demo_aico_clos, tb_null); } // ok return tb_true; }