3

I'm trying to configure NGINX/OpenResty to proxy SOAP calls to 2 different servers based on a string present on the SOAP request.

What I'm able to do: I am able to proxy requests to 2 different servers based on the path the SOAP client is calling:

location /pathA {
    proxy_pass http://www.ServerA.com/PathA/;
}
location /pathB {
    proxy_pass http://www.ServerB.com/PathB/;
}

What I can't do:

I can't separate the traffic based on the content of the request. The main reason I believe is that I can't correctly assemble the LUA script to extract the information and later use it to proxy the request.

location / {
    conten_by_lua '
        ngx.req.read_body()
        local match = ngx.re.match(ngx.var.request_body,"STRING TO FIND")
        if match then
            proxy_pass http://www.ServerA.com/PathA/;
        else
            proxy_pass http://www.ServerB.com/PathB/;

how can I achieve this?

I installed OpenResty and LUA is working fine.

I think I read somewhere that if the request is not an HTTP POST ngx.req.read_body() would not work. Is that correct?

Thank you for your help.

Ivan Shatsky
  • 2,360
  • 2
  • 6
  • 17
jedelvalle
  • 31
  • 1
  • 2

2 Answers2

5

You almost did it youself, the only thing I would do differently is to use rewrite_by_lua instead of content_by_lua:

location / {
    set $proxy "";
    rewrite_by_lua '
        ngx.req.read_body()
        local match = ngx.re.match(ngx.var.request_body, "STRING TO FIND")
        if match then
            ngx.var.proxy = "www.ServerA.com"
        else
            ngx.var.proxy = "www.ServerB.com"
        end
    ';
    proxy_pass http://$proxy$uri;
}

If request is not an HTTP POST or has an empty body, ngx.req.read_body() will return nil, so it is better to add an additional check:

location / {
    set $proxy "";
    rewrite_by_lua '
        ngx.req.read_body()
        local body = ngx.var.request_body
        if (body) then
            local match = ngx.re.match(body, "STRING TO FIND")
            if match then
                ngx.var.proxy = "www.ServerA.com"
            else
                ngx.var.proxy = "www.ServerB.com"
            end
        else
            ngx.status = ngx.HTTP_NOT_FOUND
            ngx.exit(ngx.status)
        end
    ';
    proxy_pass http://$proxy$uri;
}

You also can limit allowed methods:

location / {
    limit_except POST { deny all; }
    ...
}

One more thing. With this configuration, if you specify your backends with domain names instead of IP addresses, you'll need a resolver directive in your config. You can use your local name server if you have one, or use something external like Google public DNS (8.8.8.8) or DNS provided for you by your ISP.

Igor Barinov
  • 103
  • 3
Ivan Shatsky
  • 2,360
  • 2
  • 6
  • 17
0

Here is the code snippets I used in openresty to distinguish dev and prod webhook according to posted json body:

server {
    listen 8080;
    resolver 114.114.114.114 8.8.8.8 valid=30s;

    location /webhook {
        limit_except POST { deny all; }

        set $proxy "";

        rewrite_by_lua_block {
            local devWebhook = "myservice.dev"   -- todo
            local prodWebhook = "mysevice.prod" -- todo

            function getFile(file_name)
                local f = assert(io.open(file_name, 'r'))
                local string = f:read("*all")
                f:close()
                return string
            end

            ngx.req.read_body()
            local data = ngx.req.get_body_data()
            if nil == data then
                local file_name = ngx.req.get_body_file()
                if file_name then
                    data = getFile(file_name)
                end
            end

            local json = require("cjson.safe")
            local t    = json.decode(data)
            if not (type(t) == "table" and t["msg"]) then
                ngx.exit(400)
            end               

            local msg = ngx.unescape_uri(t["msg"])
            if not (type(msg) == "string" and #msg > 0) then
                ngx.exit(400)
            end

            if string.find(msg, '"title":".*test product.*"') then
                ngx.var.proxy = devWebhook
            else
                ngx.var.proxy = prodWebhook
            end
        }

        proxy_pass http://$proxy$uri;
    }
}
David
  • 101
  • 2