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; }
static ngx_int_t ngx_http_tnt_send_once(ngx_http_request_t *r, ngx_http_tnt_ctx_t *ctx, ngx_chain_t *out_chain, const u_char *buf, size_t len) { tp_transcode_t tc; size_t complete_msg_size; tp_transcode_init_args_t args = { .output = (char *)out_chain->buf->start, .output_size = out_chain->buf->end - out_chain->buf->start, .method = NULL, .method_len = 0, .codec = YAJL_JSON_TO_TP, .mf = NULL }; if (tp_transcode_init(&tc, &args) == TP_TRANSCODE_ERROR) { goto error_exit; } if (tp_transcode(&tc, (char *)buf, len) == TP_TRANSCODE_ERROR) { dd("ngx_http_tnt_send:tp_transcode error: %s, code:%d", tc.errmsg, tc.errcode); goto error_exit; } if (tp_transcode_complete(&tc, &complete_msg_size) == TP_TRANSCODE_OK) { out_chain->buf->last = out_chain->buf->start + complete_msg_size; if (tc.batch_size > 1) { ctx->rest_batch_size = ctx->batch_size = tc.batch_size; } } else { goto error_exit; } tp_transcode_free(&tc); return NGX_OK; error_exit: tp_transcode_free(&tc); return NGX_ERROR; } static inline void ngx_http_tnt_cleanup(ngx_http_request_t *r, ngx_http_tnt_ctx_t *ctx) { if (ctx == NULL) { return; } if (ctx->tp_cache != NULL) { ngx_pfree(r->pool, ctx->tp_cache); ctx->tp_cache = NULL; } } static inline ngx_int_t ngx_http_tnt_set_method(ngx_http_tnt_ctx_t *ctx, ngx_http_request_t *r, ngx_http_tnt_loc_conf_t *tlcf) { u_char *start, *pos, *end; if (tlcf->method.data && tlcf->method.len) { ctx->preset_method_len = ngx_min(tlcf->method.len, sizeof(ctx->preset_method)-1); ngx_memcpy(ctx->preset_method, tlcf->method.data, ctx->preset_method_len); } else if (tlcf->http_rest_methods & r->method) { if (r->uri.data == NULL || !r->uri.len) { goto error; } start = pos = (*r->uri.data == '/'? r->uri.data + 1: r->uri.data); end = r->uri.data + r->uri.len; for (;pos != end; ++pos) { if (*pos == '/') { ctx->preset_method_len = ngx_min(sizeof(ctx->preset_method)-1, (size_t)(pos - start)); ngx_memcpy(ctx->preset_method, start, ctx->preset_method_len); break; } } if (!ctx->preset_method[0]) { if (start == end) { goto error; } ctx->preset_method_len = ngx_min(sizeof(ctx->preset_method)-1, (size_t)(end - start)); ngx_memcpy(ctx->preset_method, start, ctx->preset_method_len); } } /* Else -- expect method in the body */ return NGX_OK; error: ctx->preset_method[0] = 0; ctx->preset_method_len = 0; return NGX_ERROR; }