static inline size_t ngx_http_tnt_get_output_size( ngx_http_request_t *r, ngx_http_tnt_ctx_t *ctx, ngx_http_tnt_loc_conf_t *tlcf, ngx_buf_t *request_b) { (void)ctx; size_t output_size = ngx_http_tnt_overhead(); if (r->method & NGX_HTTP_POST || ((r->method & NGX_HTTP_PUT || r->method & NGX_HTTP_DELETE) && r->headers_in.content_length_n)) { output_size += r->headers_in.content_length_n; } if (tlcf->method.len) { output_size += tlcf->method.len; } output_size *= tlcf->in_multiplier; if (request_b != NULL) { output_size += request_b->last - request_b->start; } return output_size; }
static ngx_int_t ngx_http_tnt_send_reply(ngx_http_request_t *r, ngx_http_upstream_t *u, ngx_http_tnt_ctx_t *ctx) { tp_transcode_t tc; ngx_int_t rc; ngx_http_tnt_loc_conf_t *tlcf; ngx_buf_t *output; size_t output_size; tlcf = ngx_http_get_module_loc_conf(r, ngx_http_tnt_module); output_size = (ctx->tp_cache->end - ctx->tp_cache->start + ngx_http_tnt_overhead()) * tlcf->out_multiplier; output = ngx_http_tnt_create_mem_buf(r, u, output_size); if (output == NULL) { return NGX_ERROR; } if (ctx->batch_size > 0 && ctx->rest_batch_size == ctx->batch_size) { *output->pos = '['; ++output->pos; } tp_transcode_init_args_t args = { .output = (char *)output->pos, .output_size = output->end - output->pos, .method = NULL, .method_len = 0, .codec = TP_REPLY_TO_JSON, .mf = NULL }; rc = tp_transcode_init(&tc, &args); if (rc == TP_TRANSCODE_ERROR) { crit("[BUG] failed to call tp_transcode_init(output)"); return NGX_ERROR; } rc = tp_transcode(&tc, (char *)ctx->tp_cache->start, ctx->tp_cache->end - ctx->tp_cache->start); if (rc == TP_TRANSCODE_OK) { size_t complete_msg_size = 0; rc = tp_transcode_complete(&tc, &complete_msg_size); if (rc == TP_TRANSCODE_ERROR) { crit("[BUG] failed to complete output transcoding"); ngx_pfree(r->pool, output); const ngx_http_tnt_error_t *e = get_error_text(UNKNOWN_PARSE_ERROR); output = ngx_http_tnt_set_err(r, e->code, e->msg.data, e->msg.len); if (output == NULL) { goto error_exit; } goto done; } output->last = output->pos + complete_msg_size; } else if (rc == TP_TRANSCODE_ERROR) { crit("[BUG] failed to transcode output, err: '%s'", tc.errmsg); ngx_pfree(r->pool, output); output = ngx_http_tnt_set_err(r, tc.errcode, (u_char *)tc.errmsg, ngx_strlen(tc.errmsg)); if (output == NULL) { goto error_exit; } } done: tp_transcode_free(&tc); if (ctx->batch_size > 0) { if (ctx->rest_batch_size == 1) { *output->last = ']'; ++output->last; } else if (ctx->rest_batch_size <= ctx->batch_size) { *output->last = ','; ++output->last; } } return ngx_http_tnt_output(r, u, output); error_exit: tp_transcode_free(&tc); return NGX_ERROR; } static ngx_int_t ngx_http_tnt_filter_reply(ngx_http_request_t *r, ngx_http_upstream_t *u, ngx_buf_t *b) { ngx_http_tnt_ctx_t *ctx = ngx_http_get_module_ctx(r, ngx_http_tnt_module); ssize_t bytes = b->last - b->pos; dd("filter_reply -> recv bytes: %i, rest: %i", (int)bytes, (int)ctx->rest); if (ctx->state == READ_PAYLOAD) { ssize_t payload_rest = ngx_min(ctx->payload.e - ctx->payload.p, bytes); if (payload_rest > 0) { ctx->payload.p = ngx_copy(ctx->payload.p, b->pos, payload_rest); bytes -= payload_rest; b->pos += payload_rest; payload_rest = ctx->payload.e - ctx->payload.p; dd("filter_reply -> payload rest:%i", (int)payload_rest); } if (payload_rest == 0) { ctx->payload_size = tp_read_payload((char *)&ctx->payload.mem[0], (char *)ctx->payload.e); if (ctx->payload_size <= 0) { crit("[BUG] tp_read_payload failed, ret:%i", (int)ctx->payload_size); return NGX_ERROR; } ctx->rest = ctx->payload_size - 5 /* - header size */; dd("filter_reply -> got header payload:%i, rest:%i", (int)ctx->payload_size, (int)ctx->rest); ctx->tp_cache = ngx_create_temp_buf(r->pool, ctx->payload_size); if (ctx->tp_cache == NULL) { return NGX_ERROR; } ctx->tp_cache->pos = ctx->tp_cache->start; ctx->tp_cache->memory = 1; ctx->tp_cache->pos = ngx_copy(ctx->tp_cache->pos, &ctx->payload.mem[0], sizeof(ctx->payload.mem) - 1); ctx->payload.p = &ctx->payload.mem[0]; ctx->state = READ_BODY; } else { return NGX_OK; } } ngx_int_t rc = NGX_OK; if (ctx->state == READ_BODY) { ssize_t rest = ctx->rest - bytes, read_on = bytes; if (rest < 0) { rest *= -1; read_on = bytes - rest; ctx->rest = 0; ctx->state = SEND_REPLY; rc = NGX_AGAIN; } else if (rest == 0) { ctx->state = SEND_REPLY; ctx->rest = 0; } else { ctx->rest -= bytes; } ctx->tp_cache->pos = ngx_copy(ctx->tp_cache->pos, b->pos, read_on); b->pos += read_on; dd("filter_reply -> read_on:%i, rest:%i, cache rest:%i, buf size:%i", (int)read_on, (int)ctx->rest, (int)(ctx->tp_cache->end - ctx->tp_cache->pos), (int)(b->last - b->pos)); } if (ctx->state == SEND_REPLY) { rc = ngx_http_tnt_send_reply(r, u, ctx); ctx->state = READ_PAYLOAD; ctx->rest = ctx->payload_size = 0; --ctx->rest_batch_size; if (ctx->rest_batch_size <= 0) { u->length = 0; ctx->rest_batch_size = 0; ctx->batch_size = 0; } ngx_pfree(r->pool, ctx->tp_cache); ctx->tp_cache = NULL; if (b->last - b->pos > 0) { rc = NGX_AGAIN; } } return rc; }