tb_void_t tb_http_status_cler(tb_http_status_t* status, tb_bool_t host_changed) { // check tb_assert_and_check_return(status); // clear status status->code = 0; status->bgzip = 0; status->bdeflate = 0; status->bchunked = 0; status->content_size = -1; status->document_size = -1; status->state = TB_STATE_OK; // clear content type tb_string_clear(&status->content_type); // clear location tb_string_clear(&status->location); // host is changed? clear the alived state if (host_changed) { status->version = 1; status->balived = 0; status->bseeked = 0; } }
tb_xml_node_ref_t tb_xml_reader_attributes(tb_xml_reader_ref_t reader) { // check tb_xml_reader_impl_t* impl = (tb_xml_reader_impl_t*)reader; tb_assert_and_check_return_val(impl && ( impl->event == TB_XML_READER_EVENT_DOCUMENT || impl->event == TB_XML_READER_EVENT_ELEMENT_BEG || impl->event == TB_XML_READER_EVENT_ELEMENT_END || impl->event == TB_XML_READER_EVENT_ELEMENT_EMPTY), tb_null); // init tb_char_t const* p = tb_string_cstr(&impl->element); tb_char_t const* e = p + tb_string_size(&impl->element); // skip name while (p < e && *p && !tb_isspace(*p)) p++; while (p < e && *p && tb_isspace(*p)) p++; // parse attributes tb_size_t n = 0; while (p < e) { // parse name tb_string_clear(&impl->attribute_name); for (; p < e && *p != '='; p++) if (!tb_isspace(*p)) tb_string_chrcat(&impl->attribute_name, *p); if (*p != '=') break; // parse data tb_string_clear(&impl->attribute_data); for (p++; p < e && (*p != '\'' && *p != '\"'); p++) ; if (*p != '\'' && *p != '\"') break; for (p++; p < e && (*p != '\'' && *p != '\"'); p++) tb_string_chrcat(&impl->attribute_data, *p); if (*p != '\'' && *p != '\"') break; p++; // append node if (tb_string_cstr(&impl->attribute_name) && tb_string_cstr(&impl->attribute_data)) { // node tb_xml_node_ref_t prev = n > 0? (tb_xml_node_ref_t)&impl->attributes[n - 1] : tb_null; tb_xml_node_ref_t node = (tb_xml_node_ref_t)&impl->attributes[n]; // init node tb_string_strcpy(&node->name, &impl->attribute_name); tb_string_strcpy(&node->data, &impl->attribute_data); // append node if (prev) prev->next = node; node->next = tb_null; // next n++; } } // ok? return n? (tb_xml_node_ref_t)&impl->attributes[0] : tb_null; }
tb_char_t const* tb_string_ltrim(tb_string_ref_t string) { // check tb_assert_and_check_return_val(string, tb_null); // init tb_char_t* s = (tb_char_t*)tb_string_cstr(string); tb_size_t n = tb_string_size(string); tb_check_return_val(s && n, tb_null); // done tb_char_t* p = s; tb_char_t* e = s + n; while (p < e && tb_isspace(*p)) p++; // strip it if (p < e) { // move it if exists spaces if (p > s) tb_buffer_memmov(string, p - s); } // clear it else tb_string_clear(string); // ok? return tb_string_cstr(string); }
/* ////////////////////////////////////////////////////////////////////////////////////// * implementation */ tb_size_t tb_environment_load(tb_environment_ref_t environment, tb_char_t const* name) { // check tb_assert_and_check_return_val(environment && name, 0); // clear environment first tb_vector_clear(environment); // get values tb_char_t const* values = tb_environment_get_impl(name, tb_null); tb_check_return_val(values, 0); // init value string tb_string_t value; if (tb_string_init(&value)) { // done tb_char_t const* p = values; tb_char_t c = '\0'; while (1) { // the character c = *p++; // make value if (c != ';' && c) tb_string_chrcat(&value, c); else { // save value to environment if (tb_string_size(&value)) tb_vector_insert_tail(environment, tb_string_cstr(&value)); // clear value tb_string_clear(&value); // end? tb_check_break(c); } } // exit value string tb_string_exit(&value); } // exit values if (values) tb_free(values); values = tb_null; // ok? return tb_vector_size(environment); }
/* ////////////////////////////////////////////////////////////////////////////////////// * implementation */ tb_bool_t tb_string_init(tb_string_ref_t string) { // check tb_assert_and_check_return_val(string, tb_false); // init tb_bool_t ok = tb_buffer_init(string); // clear it tb_string_clear(string); // ok? return ok; }
tb_char_t const* tb_string_strcpy(tb_string_ref_t string, tb_string_ref_t s) { // check tb_assert_and_check_return_val(s, tb_null); // done tb_size_t n = tb_string_size(s); if (n) return tb_string_cstrncpy(string, tb_string_cstr(s), n); else { tb_string_clear(string); return tb_null; } }
static tb_void_t tb_filter_chunked_clos(tb_filter_t* filter) { // check tb_filter_chunked_t* cfilter = tb_filter_chunked_cast(filter); tb_assert_and_check_return(cfilter); // clear size cfilter->size = 0; // clear read cfilter->read = 0; // clear line tb_string_clear(&cfilter->line); }
static tb_char_t const* tb_xml_reader_text_parse(tb_xml_reader_impl_t* reader) { // clear text tb_string_clear(&reader->text); // parse text tb_char_t* pc = tb_null; while (tb_stream_need(reader->rstream, (tb_byte_t**)&pc, 1) && pc) { // is end? </ ..> if (pc[0] == '<') return tb_string_cstr(&reader->text); else { tb_string_chrcat(&reader->text, *pc); if (!tb_stream_skip(reader->rstream, 1)) return tb_null; } } return tb_null; }
tb_void_t tb_xml_reader_clos(tb_xml_reader_ref_t reader) { // check tb_xml_reader_impl_t* impl = (tb_xml_reader_impl_t*)reader; tb_assert_and_check_return(impl); // clos the reader stream if (impl->rstream) tb_stream_clos(impl->rstream); impl->rstream = tb_null; // exit the input stream if (impl->istream && impl->bowner) tb_stream_exit(impl->istream); impl->istream = tb_null; // clear level impl->level = 0; // clear owner impl->bowner = tb_false; // clear text tb_string_clear(&impl->text); // clear element tb_string_clear(&impl->element); // clear name tb_string_clear(&impl->element_name); // clear attribute name tb_string_clear(&impl->attribute_name); // clear attribute data tb_string_clear(&impl->attribute_data); // clear attributes tb_long_t i = 0; for (i = 0; i < TB_XML_READER_ATTRIBUTES_MAXN; i++) { tb_xml_node_ref_t node = (tb_xml_node_ref_t)(impl->attributes + i); tb_string_clear(&node->name); tb_string_clear(&node->data); } }
tb_char_t const* tb_string_rtrim(tb_string_ref_t string) { // check tb_assert_and_check_return_val(string, tb_null); // init tb_char_t* s = (tb_char_t*)tb_string_cstr(string); tb_size_t n = tb_string_size(string); tb_check_return_val(s && n, tb_null); // done tb_char_t* e = s + n - 1; while (e >= s && tb_isspace(*e)) e--; // strip it if (e >= s) tb_string_strip(string, e - s + 1); // clear it else tb_string_clear(string); // ok? return tb_string_cstr(string); }
/* ////////////////////////////////////////////////////////////////////////////////////// * parser implementation */ static tb_char_t const* tb_xml_reader_element_parse(tb_xml_reader_impl_t* reader) { // clear element tb_string_clear(&reader->element); // parse element tb_char_t ch = '\0'; tb_size_t in = 0; while ((ch = tb_stream_bread_s8(reader->rstream))) { // append element if (!in && ch == '<') in = 1; else if (in) { if (ch != '>') tb_string_chrcat(&reader->element, ch); else return tb_string_cstr(&reader->element); } } // failed tb_assertf_abort(0, "invalid element: %s from %s", tb_string_cstr(&reader->element), tb_url_get(tb_stream_url(reader->istream))); return tb_null; }
/* ////////////////////////////////////////////////////////////////////////////////////// * main */ tb_int_t tb_demo_stream_async_stream_main(tb_int_t argc, tb_char_t** argv) { // done tb_demo_context_t context = {0}; do { // init option context.option = tb_option_init("astream", "the astream demo", g_options); tb_assert_and_check_break(context.option); // done option if (tb_option_done(context.option, argc - 1, &argv[1])) { // debug and verbose context.debug = tb_option_find(context.option, "debug")? tb_false : tb_true; context.verbose = tb_option_find(context.option, "no-verbose")? tb_false : tb_true; // done url if (tb_option_find(context.option, "url")) { // init event context.event = tb_event_init(); tb_assert_and_check_break(context.event); // init istream context.istream = tb_async_stream_init_from_url(tb_null, tb_option_item_cstr(context.option, "url")); tb_assert_and_check_break(context.istream); // ctrl http if (tb_async_stream_type(context.istream) == TB_STREAM_TYPE_HTTP) { // enable gzip? if (tb_option_find(context.option, "gzip")) { // auto unzip if (!tb_async_stream_ctrl(context.istream, TB_STREAM_CTRL_HTTP_SET_AUTO_UNZIP, 1)) break; // need gzip if (!tb_async_stream_ctrl(context.istream, TB_STREAM_CTRL_HTTP_SET_HEAD, "Accept-Encoding", "gzip,deflate")) break; } // enable debug? if (!tb_async_stream_ctrl(context.istream, TB_STREAM_CTRL_HTTP_SET_HEAD_FUNC, context.debug? tb_demo_istream_head_func : tb_null)) break; // custem header? if (tb_option_find(context.option, "header")) { // init tb_string_t key; tb_string_t val; tb_string_init(&key); tb_string_init(&val); // done tb_bool_t k = tb_true; tb_char_t const* p = tb_option_item_cstr(context.option, "header"); while (*p) { // is key? if (k) { if (*p != ':' && !tb_isspace(*p)) tb_string_chrcat(&key, *p++); else if (*p == ':') { // skip ':' p++; // skip space while (*p && tb_isspace(*p)) p++; // is val now k = tb_false; } else p++; } // is val? else { if (*p != ';') tb_string_chrcat(&val, *p++); else { // skip ';' p++; // skip space while (*p && tb_isspace(*p)) p++; // set header if (tb_string_size(&key) && tb_string_size(&val)) { if (context.debug) tb_printf("header: %s: %s\n", tb_string_cstr(&key), tb_string_cstr(&val)); if (!tb_async_stream_ctrl(context.istream, TB_STREAM_CTRL_HTTP_SET_HEAD, tb_string_cstr(&key), tb_string_cstr(&val))) break; } // is key now k = tb_true; // clear key & val tb_string_clear(&key); tb_string_clear(&val); } } } // set header if (tb_string_size(&key) && tb_string_size(&val)) { if (context.debug) tb_printf("header: %s: %s\n", tb_string_cstr(&key), tb_string_cstr(&val)); if (!tb_async_stream_ctrl(context.istream, TB_STREAM_CTRL_HTTP_SET_HEAD, tb_string_cstr(&key), tb_string_cstr(&val))) break; } // exit tb_string_exit(&key); tb_string_exit(&val); } // keep alive? if (tb_option_find(context.option, "keep-alive")) { if (!tb_async_stream_ctrl(context.istream, TB_STREAM_CTRL_HTTP_SET_HEAD, "Connection", "keep-alive")) break; } // post-data? if (tb_option_find(context.option, "post-data")) { tb_char_t const* post_data = tb_option_item_cstr(context.option, "post-data"); tb_hize_t post_size = tb_strlen(post_data); if (!tb_async_stream_ctrl(context.istream, TB_STREAM_CTRL_HTTP_SET_METHOD, TB_HTTP_METHOD_POST)) break; if (!tb_async_stream_ctrl(context.istream, TB_STREAM_CTRL_HTTP_SET_POST_DATA, post_data, post_size)) break; if (!tb_async_stream_ctrl(context.istream, TB_STREAM_CTRL_HTTP_SET_POST_FUNC, tb_demo_http_post_func)) break; if (context.debug) tb_printf("post: %llu\n", post_size); } // post-file? else if (tb_option_find(context.option, "post-file")) { tb_char_t const* url = tb_option_item_cstr(context.option, "post-file"); if (!tb_async_stream_ctrl(context.istream, TB_STREAM_CTRL_HTTP_SET_METHOD, TB_HTTP_METHOD_POST)) break; if (!tb_async_stream_ctrl(context.istream, TB_STREAM_CTRL_HTTP_SET_POST_URL, url)) break; if (!tb_async_stream_ctrl(context.istream, TB_STREAM_CTRL_HTTP_SET_POST_FUNC, tb_demo_http_post_func)) break; if (context.debug) tb_printf("post: %s\n", url); } } // set range if (tb_option_find(context.option, "range")) { tb_char_t const* p = tb_option_item_cstr(context.option, "range"); if (p) { // the bof tb_hize_t eof = 0; tb_hize_t bof = tb_atoll(p); while (*p && tb_isdigit(*p)) p++; if (*p == '-') { p++; eof = tb_atoll(p); } if (!tb_async_stream_ctrl(context.istream, TB_STREAM_CTRL_HTTP_SET_RANGE, bof, eof)) break; } } // set timeout if (tb_option_find(context.option, "timeout")) { tb_size_t timeout = tb_option_item_uint32(context.option, "timeout"); tb_async_stream_ctrl(context.istream, TB_STREAM_CTRL_SET_TIMEOUT, &timeout); } // print verbose info if (context.verbose) tb_printf("open: %s: ..\n", tb_option_item_cstr(context.option, "url")); // open istream if (!tb_async_stream_open(context.istream, tb_demo_istream_open_func, &context)) { // print verbose info if (context.verbose) tb_printf("open: failed\n"); break; } // wait it tb_event_wait(context.event, -1); } else tb_option_help(context.option); } else tb_option_help(context.option); } while (0); // exit transfer if (context.transfer) tb_async_transfer_exit(context.transfer); context.transfer = tb_null; // exit istream if (context.istream) tb_async_stream_exit(context.istream); context.istream = tb_null; // exit ostream if (context.ostream) tb_async_stream_exit(context.ostream); context.ostream = tb_null; // exit option if (context.option) tb_option_exit(context.option); context.option = tb_null; // exit event if (context.event) tb_event_exit(context.event); context.event = tb_null; return 0; }
/* ////////////////////////////////////////////////////////////////////////////////////// * main */ tb_int_t tb_demo_stream_main(tb_int_t argc, tb_char_t** argv) { // done tb_option_ref_t option = tb_null; tb_stream_ref_t istream = tb_null; tb_stream_ref_t ostream = tb_null; tb_stream_ref_t pstream = tb_null; do { // init option option = tb_option_init("stream", "the stream demo", g_options); tb_assert_and_check_break(option); // done option if (tb_option_done(option, argc - 1, &argv[1])) { // debug & verbose tb_bool_t debug = tb_option_find(option, "debug"); tb_bool_t verbose = tb_option_find(option, "no-verbose")? tb_false : tb_true; // done url if (tb_option_find(option, "url")) { // init istream istream = tb_stream_init_from_url(tb_option_item_cstr(option, "url")); tb_assert_and_check_break(istream); // ctrl http if (tb_stream_type(istream) == TB_STREAM_TYPE_HTTP) { // enable gzip? if (tb_option_find(option, "gzip")) { // auto unzip if (!tb_stream_ctrl(istream, TB_STREAM_CTRL_HTTP_SET_AUTO_UNZIP, 1)) break; // need gzip if (!tb_stream_ctrl(istream, TB_STREAM_CTRL_HTTP_SET_HEAD, "Accept-Encoding", "gzip,deflate")) break; } // enable debug? if (!tb_stream_ctrl(istream, TB_STREAM_CTRL_HTTP_SET_HEAD_FUNC, debug? tb_demo_stream_head_func : tb_null)) break; // custem header? if (tb_option_find(option, "header")) { // init tb_string_t key; tb_string_t val; tb_string_init(&key); tb_string_init(&val); // done tb_bool_t k = tb_true; tb_char_t const* p = tb_option_item_cstr(option, "header"); while (*p) { // is key? if (k) { if (*p != ':' && !tb_isspace(*p)) tb_string_chrcat(&key, *p++); else if (*p == ':') { // skip ':' p++; // skip space while (*p && tb_isspace(*p)) p++; // is val now k = tb_false; } else p++; } // is val? else { if (*p != ';') tb_string_chrcat(&val, *p++); else { // skip ';' p++; // skip space while (*p && tb_isspace(*p)) p++; // set header if (tb_string_size(&key) && tb_string_size(&val)) { if (debug) tb_printf("header: %s: %s\n", tb_string_cstr(&key), tb_string_cstr(&val)); if (!tb_stream_ctrl(istream, TB_STREAM_CTRL_HTTP_SET_HEAD, tb_string_cstr(&key), tb_string_cstr(&val))) break; } // is key now k = tb_true; // clear key & val tb_string_clear(&key); tb_string_clear(&val); } } } // set header if (tb_string_size(&key) && tb_string_size(&val)) { if (debug) tb_printf("header: %s: %s\n", tb_string_cstr(&key), tb_string_cstr(&val)); if (!tb_stream_ctrl(istream, TB_STREAM_CTRL_HTTP_SET_HEAD, tb_string_cstr(&key), tb_string_cstr(&val))) break; } // exit tb_string_exit(&key); tb_string_exit(&val); } // keep alive? if (tb_option_find(option, "keep-alive")) { if (!tb_stream_ctrl(istream, TB_STREAM_CTRL_HTTP_SET_HEAD, "Connection", "keep-alive")) break; } // post-data? if (tb_option_find(option, "post-data")) { tb_char_t const* post_data = tb_option_item_cstr(option, "post-data"); tb_hize_t post_size = tb_strlen(post_data); if (!tb_stream_ctrl(istream, TB_STREAM_CTRL_HTTP_SET_METHOD, TB_HTTP_METHOD_POST)) break; if (!tb_stream_ctrl(istream, TB_STREAM_CTRL_HTTP_SET_POST_DATA, post_data, post_size)) break; if (!tb_stream_ctrl(istream, TB_STREAM_CTRL_HTTP_SET_POST_FUNC, tb_demo_http_post_func)) break; if (debug) tb_printf("post: %llu\n", post_size); } // post-file? else if (tb_option_find(option, "post-file")) { tb_char_t const* url = tb_option_item_cstr(option, "post-file"); if (!tb_stream_ctrl(istream, TB_STREAM_CTRL_HTTP_SET_METHOD, TB_HTTP_METHOD_POST)) break; if (!tb_stream_ctrl(istream, TB_STREAM_CTRL_HTTP_SET_POST_URL, url)) break; if (!tb_stream_ctrl(istream, TB_STREAM_CTRL_HTTP_SET_POST_FUNC, tb_demo_http_post_func)) break; if (debug) tb_printf("post: %s\n", url); } } // set range if (tb_option_find(option, "range")) { tb_char_t const* p = tb_option_item_cstr(option, "range"); if (p) { // the bof tb_hize_t eof = 0; tb_hize_t bof = tb_atoll(p); while (*p && tb_isdigit(*p)) p++; if (*p == '-') { p++; eof = tb_atoll(p); } if (!tb_stream_ctrl(istream, TB_STREAM_CTRL_HTTP_SET_RANGE, bof, eof)) break; } } // set timeout if (tb_option_find(option, "timeout")) { tb_size_t timeout = tb_option_item_uint32(option, "timeout"); if (!tb_stream_ctrl(istream, TB_STREAM_CTRL_SET_TIMEOUT, timeout)) break; } // print verbose info if (verbose) tb_printf("open: %s: ..\n", tb_option_item_cstr(option, "url")); // open istream if (!tb_stream_open(istream)) { // print verbose info if (verbose) tb_printf("open: %s\n", tb_state_cstr(tb_stream_state(istream))); break; } // print verbose info if (verbose) tb_printf("open: ok\n"); // init ostream if (tb_option_find(option, "more0")) { // the path tb_char_t const* path = tb_option_item_cstr(option, "more0"); // init ostream = tb_stream_init_from_file(path, TB_FILE_MODE_RW | TB_FILE_MODE_CREAT | TB_FILE_MODE_BINARY | TB_FILE_MODE_TRUNC); // print verbose info if (verbose) tb_printf("save: %s\n", path); } else { // the name tb_char_t const* name = tb_strrchr(tb_option_item_cstr(option, "url"), '/'); if (!name) name = tb_strrchr(tb_option_item_cstr(option, "url"), '\\'); if (!name) name = "/stream.file"; // the path tb_char_t path[TB_PATH_MAXN] = {0}; if (tb_directory_current(path, TB_PATH_MAXN)) tb_strcat(path, name); else break; // init file ostream = tb_stream_init_from_file(path, TB_FILE_MODE_RW | TB_FILE_MODE_CREAT | TB_FILE_MODE_BINARY | TB_FILE_MODE_TRUNC); // print verbose info if (verbose) tb_printf("save: %s\n", path); } tb_assert_and_check_break(ostream); // the limit rate tb_size_t limitrate = 0; if (tb_option_find(option, "limitrate")) limitrate = tb_option_item_uint32(option, "limitrate"); // save it tb_hong_t save = 0; tb_demo_context_t context = {0}; context.verbose = verbose; if ((save = tb_transfer_done(istream, ostream, limitrate, tb_demo_stream_save_func, &context)) < 0) break; } else tb_option_help(option); } else tb_option_help(option); } while (0); // exit pstream if (pstream) tb_stream_exit(pstream); pstream = tb_null; // exit istream if (istream) tb_stream_exit(istream); istream = tb_null; // exit ostream if (ostream) tb_stream_exit(ostream); ostream = tb_null; // exit option if (option) tb_option_exit(option); option = tb_null; return 0; }
tb_bool_t tb_xml_reader_open(tb_xml_reader_ref_t reader, tb_stream_ref_t stream, tb_bool_t bowner) { // check tb_xml_reader_impl_t* impl = (tb_xml_reader_impl_t*)reader; tb_assert_and_check_return_val(impl && stream, tb_false); // done tb_bool_t ok = tb_false; do { // check tb_assert_and_check_break(!impl->rstream && !impl->istream); // init level impl->level = 0; // init owner impl->bowner = bowner; // init the input stream impl->istream = stream; // init the reader stream impl->rstream = stream; // open the reader stream if be not opened if (!tb_stream_is_opened(impl->rstream) && !tb_stream_open(impl->rstream)) break; // clear text tb_string_clear(&impl->text); // clear element tb_string_clear(&impl->element); // clear name tb_string_clear(&impl->element_name); // clear attribute name tb_string_clear(&impl->attribute_name); // clear attribute data tb_string_clear(&impl->attribute_data); // clear attributes tb_long_t i = 0; for (i = 0; i < TB_XML_READER_ATTRIBUTES_MAXN; i++) { tb_xml_node_ref_t node = (tb_xml_node_ref_t)(impl->attributes + i); tb_string_clear(&node->name); tb_string_clear(&node->data); } // ok ok = tb_true; } while (0); // failed? close it if (!ok) tb_xml_reader_clos(reader); // ok? return ok; }
/* chunked_data * * head data tail * ea5\r\n ..........\r\n e65\r\n..............\r\n 0\r\n\r\n * ---------------------- ------------------------- --------- * chunk0 chunk1 end */ static tb_long_t tb_filter_chunked_spak(tb_filter_t* filter, tb_static_stream_ref_t istream, tb_static_stream_ref_t ostream, tb_long_t sync) { // check tb_filter_chunked_t* cfilter = tb_filter_chunked_cast(filter); tb_assert_and_check_return_val(cfilter && istream && ostream, -1); tb_assert_and_check_return_val(tb_static_stream_valid(istream) && tb_static_stream_valid(ostream), -1); // the idata tb_byte_t const* ip = tb_static_stream_pos(istream); tb_byte_t const* ie = tb_static_stream_end(istream); // trace tb_trace_d("[%p]: isize: %lu, beof: %d", cfilter, tb_static_stream_size(istream), filter->beof); // find the eof: '\r\n 0\r\n\r\n' if ( !filter->beof && ip + 6 < ie && ie[-7] == '\r' && ie[-6] == '\n' && ie[-5] == '0' && ie[-4] == '\r' && ie[-3] == '\n' && ie[-2] == '\r' && ie[-1] == '\n') { // is eof filter->beof = tb_true; } // the odata tb_byte_t* op = (tb_byte_t*)tb_static_stream_pos(ostream); tb_byte_t* oe = (tb_byte_t*)tb_static_stream_end(ostream); tb_byte_t* ob = op; // parse chunked head and chunked tail if (!cfilter->size || cfilter->read >= cfilter->size) { // walk while (ip < ie) { // the charactor tb_char_t ch = *ip++; // trace tb_trace_d("[%p]: character: %x", cfilter, ch); // check tb_assert_and_check_return_val(ch, -1); // append char to line if (ch != '\n') tb_string_chrcat(&cfilter->line, ch); // is line end? else { // check tb_char_t const* pb = tb_string_cstr(&cfilter->line); tb_size_t pn = tb_string_size(&cfilter->line); tb_assert_and_check_return_val(pb, -1); // trace tb_trace_d("[%p]: line: %s", cfilter, tb_string_cstr(&cfilter->line)); // strip '\r' if exists if (pb[pn - 1] == '\r') tb_string_strip(&cfilter->line, pn - 1); // is chunked tail? only "\r\n" if (!tb_string_size(&cfilter->line)) { // reset size cfilter->read = 0; cfilter->size = 0; // trace tb_trace_d("[%p]: tail", cfilter); // continue continue ; } // is chunked head? parse size else { // parse size cfilter->size = tb_s16tou32(pb); // trace tb_trace_d("[%p]: size: %lu", cfilter, cfilter->size); // clear data tb_string_clear(&cfilter->line); // is eof? "0\r\n\r\n" if (!cfilter->size) { // trace tb_trace_d("[%p]: eof", cfilter); // is eof filter->beof = tb_true; // continue to spak the end data continue ; } // ok break; } } } } // check tb_assert_and_check_return_val(cfilter->read <= cfilter->size, -1); // read chunked data tb_size_t size = tb_min3(ie - ip, oe - op, cfilter->size - cfilter->read); if (size) { // copy data tb_memcpy((tb_byte_t*)op, ip, size); ip += size; op += size; // update read cfilter->read += size; } // update stream tb_static_stream_goto(istream, (tb_byte_t*)ip); tb_static_stream_goto(ostream, (tb_byte_t*)op); // trace tb_trace_d("[%p]: read: %lu, size: %lu, beof: %u, ileft: %lu", cfilter, cfilter->read, cfilter->size, filter->beof, tb_static_stream_left(istream)); // ok return (op - ob); }