#include #include #include #include #include #include #include #include #include #include #define __PARSER_TO_HF_PRIVATE(_parser) ((struct http_half_private *)(_parser->data)) static const char * __str_std_header_field_map[] = { [TFE_HTTP_UNKNOWN_FIELD] = NULL, [TFE_HTTP_HOST] = "Host", [TFE_HTTP_REFERER] = "Referer", [TFE_HTTP_USER_AGENT] = "User-Agent", [TFE_HTTP_COOKIE] = "Cookie", [TFE_HTTP_PROXY_AUTHORIZATION] = "Proxy-Authorization", [TFE_HTTP_AUTHORIZATION] = "Authorization", [TFE_HTTP_LOCATION] = "Location", [TFE_HTTP_SERVER] = "Server", [TFE_HTTP_ETAG] = "Etag", [TFE_HTTP_DATE] = "Date", [TFE_HTTP_TRAILER] = "Trailer", [TFE_HTTP_TRANSFER_ENCODING] = "Transfer-Encoding", [TFE_HTTP_VIA] = "Via", [TFE_HTTP_PRAGMA] = "Pragma", [TFE_HTTP_CONNECTION] = "Connection", [TFE_HTTP_CONT_ENCODING] = "Content-Encoding", [TFE_HTTP_CONT_LANGUAGE] = "Content-Language", [TFE_HTTP_CONT_LOCATION] = "Content-Location", [TFE_HTTP_CONT_RANGE] = "Content-Range", [TFE_HTTP_CONT_LENGTH] = "Content-Length", [TFE_HTTP_CONT_TYPE] = "Content-Type", [TFE_HTTP_CONT_DISPOSITION] = "Content-Disposition", [TFE_HTTP_EXPIRES] = "Expires", [TFE_HTTP_ACCEPT_ENCODING] = "Accept-Encoding" }; static enum tfe_http_std_field __str_header_field_to_std_field_id(const char * str_field, size_t len) { /* TODO: store the header text in hash table or rbtree, or use AC multistring search algo. */ for (unsigned int i = 0; i < TFE_DIM(__str_std_header_field_map); i++) { const char * __std_header_field = __str_std_header_field_map[i]; if (__std_header_field == NULL) continue; size_t __compare_length = MIN(strlen(__std_header_field), len); if (evutil_ascii_strncasecmp(__std_header_field, str_field, __compare_length) != 0) continue; return (enum tfe_http_std_field) i; } return TFE_HTTP_UNKNOWN_FIELD; } /* To flush header field and value which stash in evbuffer */ static void __http_half_header_kv_complete(struct http_half_private * hf_private) { size_t sz_evbuf_field = evbuffer_get_length(hf_private->evbuf_header_field); size_t sz_evbuf_value = evbuffer_get_length(hf_private->evbuf_header_value); enum tfe_http_std_field std_field_id; struct http_field_name compact_field; static const char __zero = 0; const char * str_field; const char * str_value; /* No header field or length of header field is zero, ignore this field-value pair. */ if (evbuffer_get_length(hf_private->evbuf_header_field) == 0) { goto __clear_buffer; } /* Write a '\0' for evbuffers */ evbuffer_add(hf_private->evbuf_header_field, &__zero, sizeof(__zero)); evbuffer_add(hf_private->evbuf_header_value, &__zero, sizeof(__zero)); sz_evbuf_field = evbuffer_get_length(hf_private->evbuf_header_field); sz_evbuf_value = evbuffer_get_length(hf_private->evbuf_header_value); /* Convert evbuffer to const char * pair */ str_field = (const char *) evbuffer_pullup(hf_private->evbuf_header_field, sz_evbuf_field); str_value = (const char *) evbuffer_pullup(hf_private->evbuf_header_value, sz_evbuf_value); std_field_id = __str_header_field_to_std_field_id(str_field, sz_evbuf_field); if (std_field_id != TFE_HTTP_UNKNOWN_FIELD) { compact_field.field_id = std_field_id; compact_field.field_name = NULL; } else { compact_field.field_id = TFE_HTTP_UNKNOWN_FIELD; compact_field.field_name = (char *) str_field; } tfe_http_field_write(&hf_private->hf_public, &compact_field, str_value); goto __clear_buffer; __clear_buffer: evbuffer_drain(hf_private->evbuf_header_field, sz_evbuf_field); evbuffer_drain(hf_private->evbuf_header_value, sz_evbuf_value); hf_private->is_evbuf_header_value_set = false; hf_private->is_evbuf_header_field_set = false; } void __hf_public_req_fill_from_private(struct http_half_private * hf_private, struct http_parser * parser) { struct tfe_http_half * hf_public = &hf_private->hf_public; struct tfe_http_req_spec * hf_req_spec = &hf_public->req_spec; /* accept-encoding, host is located in header's K-V structure */ hf_req_spec->method = (enum tfe_http_std_method) parser->method; const static struct http_field_name __host_field_name = { .field_id = TFE_HTTP_HOST, .field_name = NULL }; hf_req_spec->host = (char *) tfe_http_field_read(hf_public, &__host_field_name); /* uri is stored in underlay evbuffer, we need to append a terminal zero */ static const char __zero = 0; evbuffer_add(hf_private->evbuf_uri, &__zero, sizeof(__zero)); hf_req_spec->uri = (char *) evbuffer_pullup(hf_private->evbuf_uri, evbuffer_get_length(hf_private->evbuf_uri)); /* TODO: url is more complex. need to review RFC */ if (hf_req_spec->uri[0] != '\0' && hf_req_spec->uri[0] != '/') { asprintf(&hf_private->url_storage, "%s/%s", hf_req_spec->host, hf_req_spec->uri); } else { asprintf(&hf_private->url_storage, "%s%s", hf_req_spec->host, hf_req_spec->uri); } hf_req_spec->url = hf_private->url_storage; assert(hf_req_spec->url != NULL); } void __hf_public_resp_fill_from_private(struct http_half_private * hf_private, struct http_parser * parser) { struct tfe_http_half * hf_public = &hf_private->hf_public; struct tfe_http_resp_spec * hf_resp_spec = &hf_public->resp_spec; /* Status Code */ hf_resp_spec->resp_code = parser->status_code; /* Content Type */ const static struct http_field_name __cont_encoding_type_name = { .field_id = TFE_HTTP_CONT_TYPE, .field_name = NULL }; hf_resp_spec->content_type = (char *) tfe_http_field_read(hf_public, &__cont_encoding_type_name); /* Content Length */ const static struct http_field_name __cont_encoding_length_name = { .field_id = TFE_HTTP_CONT_LENGTH, .field_name = NULL }; hf_resp_spec->content_length = (char *) tfe_http_field_read(hf_public, &__cont_encoding_length_name); /* Content Encoding */ const static struct http_field_name __cont_encoding_field_name = { .field_id = TFE_HTTP_CONT_ENCODING, .field_name = NULL }; hf_resp_spec->content_encoding = (char *) tfe_http_field_read(hf_public, &__cont_encoding_field_name); } /* ================================================================================================================== * REQUEST PARSER CALLBACKS * ================================================================================================================== */ #define __HF_PRIVATE_CHANGE_STATUS(_status, _now, _to) \ do { assert(_status == _now); _status = _to; } while(0) static int __parser_callback_on_message_begin(struct http_parser * parser) { struct http_half_private * hf_private = __PARSER_TO_HF_PRIVATE(parser); assert(hf_private->evbuf_uri == NULL && hf_private->evbuf_body == NULL); assert(hf_private->evbuf_header_field == NULL && hf_private->evbuf_header_value == NULL); hf_private->evbuf_header_field = evbuffer_new(); hf_private->evbuf_header_value = evbuffer_new(); hf_private->is_evbuf_header_field_set = false; hf_private->is_evbuf_header_value_set = false; hf_private->evbuf_uri = evbuffer_new(); hf_private->evbuf_body = evbuffer_new(); hf_private->body_status = STATUS_INIT; hf_private->message_status = STATUS_READING; /* Never call user's callback, need to defer data */ hf_private->stream_action = ACTION_DEFER_DATA; return 0; } static int __parser_callback_on_uri_field(struct http_parser * parser, const char * at, size_t length) { struct http_half_private * hf_private = __PARSER_TO_HF_PRIVATE(parser); return evbuffer_add(hf_private->evbuf_uri, at, length); } static int __parser_callback_on_header_field(struct http_parser * parser, const char * at, size_t length) { struct http_half_private * hf_private = __PARSER_TO_HF_PRIVATE(parser); if (hf_private->is_evbuf_header_field_set && hf_private->is_evbuf_header_value_set) { __http_half_header_kv_complete(hf_private); } hf_private->is_evbuf_header_field_set = true; return evbuffer_add(hf_private->evbuf_header_field, at, length); } static int __parser_callback_on_header_value(struct http_parser * parser, const char * at, size_t length) { struct http_half_private * hf_private = __PARSER_TO_HF_PRIVATE(parser); hf_private->is_evbuf_header_value_set = true; return evbuffer_add(hf_private->evbuf_header_value, at, length); } static int __parser_callback_on_headers_complete(http_parser * parser) { struct http_half_private * hf_private = __PARSER_TO_HF_PRIVATE(parser); enum tfe_http_direction hf_direction = hf_private->hf_public.direction; if (evbuffer_get_length(hf_private->evbuf_header_field) != 0) { __http_half_header_kv_complete(hf_private); } struct tfe_http_half * hf_public = &hf_private->hf_public; hf_public->major_version = parser->http_major; hf_public->minor_version = parser->http_minor; /* Copy version to session */ if(hf_private->session != NULL) { to_hs_public(hf_private->session)->major_version = hf_public->major_version; to_hs_public(hf_private->session)->minor_version = hf_public->minor_version; } if (hf_direction == TFE_HTTP_REQUEST) { __hf_public_req_fill_from_private(hf_private, parser); } else { __hf_public_resp_fill_from_private(hf_private, parser); } if (hf_private->event_cb && hf_direction == TFE_HTTP_REQUEST) { hf_private->event_cb(hf_private, EV_HTTP_REQ_HDR, NULL, 0, hf_private->event_cb_user); } if (hf_private->event_cb && hf_direction == TFE_HTTP_RESPONSE) { hf_private->event_cb(hf_private, EV_HTTP_RESP_HDR, NULL, 0, hf_private->event_cb_user); } hf_private->stream_action = ACTION_FORWARD_DATA; return 0; } static int __parser_callback_on_body(struct http_parser * parser, const char * at, size_t length) { struct http_half_private * hf_private = __PARSER_TO_HF_PRIVATE(parser); enum tfe_http_direction hf_direction = hf_private->hf_public.direction; enum tfe_http_event ev_body_begin; enum tfe_http_event ev_body_cont; if (hf_direction == TFE_HTTP_REQUEST) { ev_body_begin = EV_HTTP_REQ_BODY_BEGIN; ev_body_cont = EV_HTTP_REQ_BODY_CONT; } else { ev_body_begin = EV_HTTP_RESP_BODY_BEGIN; ev_body_cont = EV_HTTP_RESP_BODY_CONT; } if (hf_private->body_status == STATUS_INIT && hf_private->event_cb) { hf_private->event_cb(hf_private, ev_body_begin, NULL, parser->content_length, hf_private->event_cb_user); } if (hf_private->event_cb && length != 0) { hf_private->event_cb(hf_private, ev_body_cont, (const unsigned char *) at, length, hf_private->event_cb_user); } hf_private->body_status = STATUS_READING; return 0; } static int __parser_callback_on_message_complete(http_parser * parser) { struct http_half_private * hf_private = __PARSER_TO_HF_PRIVATE(parser); enum tfe_http_direction hf_direction = hf_private->hf_public.direction; enum tfe_http_event ev_message_end; enum tfe_http_event ev_body_end; if (hf_direction == TFE_HTTP_REQUEST) { ev_message_end = EV_HTTP_REQ_END; ev_body_end = EV_HTTP_REQ_BODY_END; } else { ev_message_end = EV_HTTP_RESP_END; ev_body_end = EV_HTTP_RESP_BODY_END; } if (hf_private->event_cb && hf_private->body_status == STATUS_READING) { hf_private->event_cb(hf_private, ev_body_end, NULL, 0, hf_private->event_cb_user); } if (hf_private->event_cb) { hf_private->event_cb(hf_private, ev_message_end, NULL, 0, hf_private->event_cb_user); } hf_private->body_status = STATUS_COMPLETE; hf_private->message_status = STATUS_COMPLETE; http_parser_pause(parser, 1); return 0; } static http_parser_settings __http_half_parse_setting = { .on_message_begin = __parser_callback_on_message_begin, .on_url = __parser_callback_on_uri_field, .on_status = NULL, .on_header_field = __parser_callback_on_header_field, .on_header_value = __parser_callback_on_header_value, .on_headers_complete = __parser_callback_on_headers_complete, .on_body = __parser_callback_on_body, .on_message_complete = __parser_callback_on_message_complete, .on_chunk_header = NULL, .on_chunk_complete = NULL }; const char * hf_ops_field_read(const struct tfe_http_half * half, const struct http_field_name * field) { const struct http_half_private * hf_private = to_hf_private(half); assert(hf_private->major == 0 || hf_private->major == 1); struct http_header_private * __header_iter = NULL; struct http_header_private * __header_found = NULL; TAILQ_FOREACH(__header_iter, &hf_private->header_list, next) { if (http_field_name_compare(__header_iter->field, field) != 0) continue; __header_found = __header_iter; break; } return __header_found != NULL ? __header_iter->value : NULL; } int hf_ops_field_write(struct tfe_http_half * half, const struct http_field_name * name, const char * value) { struct http_half_private * hf_private = to_hf_private(half); assert(hf_private->major == 0 || hf_private->major == 1); struct http_header_private * __header = ALLOC(struct http_header_private, 1); __header->field = http_field_name_duplicate(name); __header->value = tfe_strdup(value); TAILQ_INSERT_TAIL(&hf_private->header_list, __header, next); return 0; } struct tfe_http_half * hf_ops_allow_write(const struct tfe_http_half * half) { return (struct tfe_http_half *) half; } const char * hf_ops_field_iterate(const struct tfe_http_half * half, void ** iter, struct http_field_name * field) { struct http_header_private ** __header_iter = (struct http_header_private **) iter; const struct http_half_private * hf_private = to_hf_private(half); if (*__header_iter == NULL) { *__header_iter = TAILQ_FIRST(&hf_private->header_list); } else { *__header_iter = TAILQ_NEXT(*__header_iter, next); } if (*__header_iter == NULL) return NULL; /* Reference of inner data, user should copy it */ field->field_id = (*__header_iter)->field->field_id; field->field_name = (*__header_iter)->field->field_name; return (*__header_iter)->value; } int hf_ops_append_body(struct tfe_http_half * half, char * buff, size_t size, int flag) { struct http_half_private * hf_private = to_hf_private(half); if (hf_private->evbuf_body == NULL) { hf_private->evbuf_body = evbuffer_new(); } return evbuffer_add(hf_private->evbuf_body, buff, size); } void hf_private_destory(struct http_half_private * hf_private) { if (hf_private->parse_object != NULL) { free(hf_private->parse_object); } if (hf_private->event_cb_user_deleter != NULL) { hf_private->event_cb_user_deleter(hf_private->event_cb_user); } free(hf_private); } void hf_ops_free(struct tfe_http_half * half) { return hf_private_destory(to_hf_private(half)); } struct tfe_http_half_ops __http_half_ops = { .ops_http_field_read = hf_ops_field_read, .ops_http_field_write = hf_ops_field_write, .ops_http_allow_write = hf_ops_allow_write, .ops_http_field_iterate = hf_ops_field_iterate, .ops_append_body = hf_ops_append_body, .ops_free = hf_ops_free }; struct http_half_private * hf_private_create(tfe_http_direction ht_dir, short major, short minor) { struct http_half_private * hf_private = ALLOC(struct http_half_private, 1); assert(hf_private != NULL && (major == 0 || major == 1) && (minor == 0 || minor == 1)); assert(ht_dir == TFE_HTTP_REQUEST || ht_dir == TFE_HTTP_RESPONSE); /* PUBLIC */ hf_private->hf_public.direction = ht_dir; hf_private->hf_public.ops = &__http_half_ops; /* PRIVATE */ hf_private->parse_object = (struct http_parser *) malloc(sizeof(struct http_parser)); assert(hf_private->parse_object != NULL); if (ht_dir == TFE_HTTP_REQUEST) { http_parser_init(hf_private->parse_object, HTTP_REQUEST); } else { http_parser_init(hf_private->parse_object, HTTP_RESPONSE); } hf_private->parse_settings = &__http_half_parse_setting; hf_private->parse_object->data = hf_private; TAILQ_INIT(&hf_private->header_list); return hf_private; } void hf_private_set_callback(struct http_half_private * hf_private, hf_private_cb * cb, void * user, void (* user_deleter)(void *)) { hf_private->event_cb = cb; hf_private->event_cb_user = user; hf_private->event_cb_user_deleter = user_deleter; } void hf_private_set_session(struct http_half_private * hf_private, struct http_session_private * hs_private) { hf_private->session = hs_private; } int hf_private_parse(struct http_half_private * hf_private, const unsigned char * data, size_t len) { assert(hf_private->parse_cursor <= len); /* Caculate the memory zones to scan. The zone from data to data + cursor has been scaned * at last construct procedure, so we don't need to scan again. */ const char * __data_with_offset = (const char *) TFE_PTR_ADD(data, hf_private->parse_cursor); size_t __len_with_offset = len - hf_private->parse_cursor; /* Scan the memory zone */ size_t sz_parsed = http_parser_execute(hf_private->parse_object, &__http_half_parse_setting, __data_with_offset, __len_with_offset); /* Nothing happended */ if (sz_parsed == len) { hf_private->parse_cursor += sz_parsed; return 0; } /* The paused parsar indicate the message boundary has been touched, we should return. * resume it to normal status */ if (HTTP_PARSER_ERRNO(hf_private->parse_object) == HPE_PAUSED) { http_parser_pause(hf_private->parse_object, 0); hf_private->parse_cursor += sz_parsed + 1; return 1; } hf_private->parse_errno = HTTP_PARSER_ERRNO(hf_private->parse_object); return -1; } static struct tfe_http_session * hs_ops_allow_write(const struct tfe_http_session * session) { struct http_session_private * hs_private = to_hs_private((struct tfe_http_session *) session); return http_frame_currect_plugin_preempt(hs_private->ht_frame) == 0 ? (struct tfe_http_session *) session : NULL; } void hs_ops_detach(const struct tfe_http_session * session) { struct http_session_private * hs_private = to_hs_private((struct tfe_http_session *) session); return http_frame_currect_plugin_detach(hs_private->ht_frame); } void hs_ops_drop(struct tfe_http_session * session) { return; } void hs_ops_request_set(struct tfe_http_session * session, struct tfe_http_half * req) { struct http_half_private * hf_private = to_hf_private(req); struct http_session_private * hs_private = to_hs_private(session); assert(hs_private->hf_private_req_user != NULL); hs_private->hf_private_req_user = hf_private; } void hs_ops_response_set(struct tfe_http_session * session, struct tfe_http_half * resp) { struct http_half_private * hf_private = to_hf_private(resp); struct http_session_private * hs_private = to_hs_private(session); assert(hs_private->hf_private_resp_user == NULL); hs_private->hf_private_resp_user = hf_private; } struct tfe_http_half * hs_ops_request_create(struct tfe_http_session * session, enum tfe_http_std_method method, const char * uri) { struct http_half_private * hf_req_private = hf_private_create(TFE_HTTP_REQUEST, session->major_version, session->minor_version); hf_req_private->method_or_status = method; hf_req_private->url_storage = tfe_strdup(uri); hf_req_private->is_setup_by_user = true; return to_hf_public(hf_req_private); } struct tfe_http_half * hs_ops_response_create(struct tfe_http_session * session, int resp_code) { struct http_half_private * hf_resp_private = hf_private_create(TFE_HTTP_RESPONSE, session->major_version, session->minor_version); hf_resp_private->method_or_status = resp_code; hf_resp_private->is_setup_by_user = true; return to_hf_public(hf_resp_private); } struct tfe_http_session_ops __http_session_ops = { .ops_allow_write = hs_ops_allow_write, .ops_detach = hs_ops_detach, .ops_drop = hs_ops_drop, .ops_request_set = hs_ops_request_set, .ops_response_set = hs_ops_response_set, .ops_request_create = hs_ops_request_create, .ops_response_create = hs_ops_response_create }; void __construct_request_line(struct http_half_private * hf_private) { } void __construct_response_line(struct http_half_private * hf_private) { enum tfe_http_std_status __resp_code = (enum tfe_http_std_status) hf_private->method_or_status; const char * __str_resp_code = http_std_status_to_string(__resp_code); if (__str_resp_code == NULL) { __str_resp_code = ""; } evbuffer_add_printf(hf_private->evbuf_raw, "HTTP/%d.%d %d %s\r\n", hf_private->major, hf_private->minor, __resp_code, __str_resp_code); } void hf_private_construct(struct http_half_private * hf_private) { assert(hf_private->is_setup_by_user); struct tfe_http_half * hf_public = to_hf_public(hf_private); /* Clear the output buffer */ if (hf_private->evbuf_raw == NULL) { hf_private->evbuf_raw = evbuffer_new(); assert(hf_private->evbuf_raw != NULL); } else { size_t __buf_length = evbuffer_get_length(hf_private->evbuf_raw); evbuffer_drain(hf_private->evbuf_raw, __buf_length); } /* HTTP Request/Response first line */ if (hf_public->direction == TFE_HTTP_REQUEST) __construct_request_line(hf_private); else __construct_response_line(hf_private); /* Headers */ void * iterator = NULL; struct http_field_name field_name{}; for (const char * str_value = tfe_http_field_iterate(hf_public, &iterator, &field_name); str_value != NULL; str_value = tfe_http_field_iterate(hf_public, &iterator, &field_name)) { const char * str_field = http_field_to_string(&field_name); evbuffer_add_printf(hf_private->evbuf_raw, "%s: %s\r\n", str_field, str_value); } /* delimitor between header and body */ evbuffer_add_printf(hf_private->evbuf_raw, "\r\n"); /* add body */ if (hf_private->evbuf_body) { evbuffer_add_buffer(hf_private->evbuf_raw, hf_private->evbuf_body); } return; } struct http_session_private * hs_private_create(struct http_connection_private * hc_private, struct http_half_private * hf_private_req, struct http_half_private * hf_private_resp) { struct http_session_private * __hs_private = ALLOC(struct http_session_private, 1); /* HS-PUBLIC */ __hs_private->hs_public.ops = &__http_session_ops; __hs_private->hs_public.req = hf_private_req != NULL ? to_hf_public(hf_private_req) : NULL; __hs_private->hs_public.resp = hf_private_req != NULL ? to_hf_public(hf_private_resp) : NULL; __hs_private->hs_public.session_id = hc_private->session_id_counter++; /* HS-PRIVATE*/ __hs_private->hc_private = hc_private; return __hs_private; } void __write_access_log(struct http_session_private * hs_private) { /* Prepare to write session access log */ /* Request */ struct http_half_private * request = to_hf_request_private(hs_private); /* Response */ struct http_half_private * response = to_hf_response_private(hs_private); /* Req-Public */ struct tfe_http_req_spec * req_spec = request ? &to_hf_public(request)->req_spec : NULL; /* Resp-Public */ struct tfe_http_resp_spec * resp_spec = response ? &to_hf_public(response)->resp_spec : NULL; /* Method */ const char * __str_method = req_spec ? http_std_method_to_string(req_spec->method) : "-"; /* URL */ const char * __str_url = req_spec ? req_spec->url : "-"; /* Resp code */ char __str_resp_code[TFE_STRING_MAX]; if (resp_spec) { snprintf(__str_resp_code, sizeof(__str_resp_code) - 1, "%d", resp_spec->resp_code); } else { snprintf(__str_resp_code, sizeof(__str_resp_code) - 1, "%s", "-"); } /* Content Type */ const char * __str_cont_type = resp_spec ? resp_spec->content_type : "-"; /* Content Length */ const char * __str_cont_length = resp_spec ? resp_spec->content_length : "-"; /* Content Encoding */ const char * __str_cont_encoding = resp_spec ? resp_spec->content_encoding : "-"; char * __access_log; asprintf(&__access_log, "%s %s %s %s %s %s", __str_method, __str_url, __str_resp_code, __str_cont_type, __str_cont_length, __str_cont_encoding); const struct tfe_stream * stream = hs_private->hc_private->stream; tfe_stream_write_access_log(stream, RLOG_LV_INFO, "%s", __access_log); free(__access_log); } void hs_private_destory(struct http_session_private * hs_private) { __write_access_log(hs_private); free(hs_private); } void hs_private_hf_private_set(struct http_session_private * hs_private, struct http_half_private * hf, enum tfe_http_direction direction) { struct tfe_http_half ** ref_old_half_public; struct http_half_private * old_half_private; if (direction == TFE_HTTP_REQUEST) { ref_old_half_public = &hs_private->hs_public.req; old_half_private = to_hf_private(*ref_old_half_public); } else { ref_old_half_public = &hs_private->hs_public.resp; old_half_private = to_hf_private(*ref_old_half_public); } if (old_half_private != NULL) { hf_private_destory(old_half_private); *ref_old_half_public = to_hf_public(hf); } else { *ref_old_half_public = to_hf_public(hf); } return; } struct http_half_private * hs_private_hf_private_release(struct http_session_private * hs_private, enum tfe_http_direction) { return nullptr; }