tarantool / nginx_upstream_module

Tarantool NginX upstream module (REST, JSON API, websockets, load balancing)
Other
174 stars 18 forks source link

tnt_eval always returns __ngx metadata and respose body all wrapped in JSON array #85

Closed apristen closed 7 years ago

apristen commented 7 years ago

Hi.

I have the following setup in nginx.conf

location /tnt {
      tnt_eval_buffer_size 1m;

      tnt_eval $tnt_http_status $tnt_body {
        tnt_method foo;
        tnt_pass 127.0.0.1:3301;
      }

      return 200 $tnt_body;
}

and in Tarantool code:

function foo(req)
  return
  {
    __ngx = {
      200,
      { ["X-Tarantool"] = "FROM_TNT" }
    }
  },
  req
end

Expected: __ngx metadata used only for add response code and headers, body returned "as is"

{"uri":"/tnt","method":"GET","proto":"HTTP/1.1","headers":{"Host":"localhost","User-Agent":"curl/7.47.0","Accept":"/"}}

Actual: tnt_eval returns JSON array with __ngx metadata and body

[{"__ngx":[200,{"X-Tarantool":"FROM_TNT"}]},{"uri":"/tnt","method":"GET","proto":"HTTP/1.1","headers":{"Host":"localhost","User-Agent":"curl/7.47.0","Accept":"/"}}]

How to set response code and headers with tnt_eval, but return response body "as is" without put all in JSON array?

dedok commented 7 years ago

Hi! Thanks you for this question.

So tnt_eval currently under development it could be changed as well. Here are not possible ways to avoid __ngx = { } in the reply now, even more I'm thinking about that, but I don't know how it could be better right now.

You can use perl module, nginScript module or OpenResty to filter reply. How exactly? tnt_eval works in REWRITE phase, that means that you can have a variables (status and body) in any module which works in CONTENT phase.

I'm thinking that I will give some feature like tnt_output_json at the end of this iteration, iteration is 1 week.

Totktonada commented 7 years ago

I workarounded it using perl module:

upstream tnt {
    server localhost:3301 max_fails=1 fail_timeout=60s;
    keepalive 250000;
}

server {
    server_name tnt_test;

    listen 80 default deferred reuseport so_keepalive=on backlog=65535;
    rewrite ^/api/(.*)/$ /api/$1 permanent;

    location /api {
        # answers check infinity timeout
        tnt_read_timeout 60m;
        tnt_eval $tnt_http_status $tnt_res {
            tnt_method "process_request";
            tnt_http_methods get post put delete;
            tnt_pass_http_request on parse_args;
            tnt_pass tnt;
        }
        default_type application/json;
        return 200 $tnt_body; # postprocessed below
    }
}

# Postprocess body to strip out HTTP status and headers returned by Tarantool
perl_require JSON.pm;
perl_set $tnt_body 'sub {
    my $r = shift;
    my $tnt_res_str = $r->variable("tnt_res");
    my $tnt_http_status = $r->variable("tnt_http_status");
    if ($tnt_http_status == 200) {
        my @tnt_res = @{JSON::decode_json($tnt_res_str)};
        my $tnt_body = $tnt_res[2];
        return JSON::encode_json($tnt_body);
    } else {
        return $tnt_res_str;
    }
}';

Edited 2017-06-26: check http status inside perl_set, a format of response can be different in the case.

dedok commented 7 years ago

Check v2.4.6-rc1. https://github.com/tarantool/nginx_upstream_module#http-headers-and-status