2

My mirroring works just fine, and I am correctly getting the original request body in the mirror server.

However I need to access a response header called Location from the original upstream and forward it to my mirror. Typically the response header from upstream will be accessed using this variable: $upstream_http_<*header name*>, in my case $upstream_http_location.

However I want to access this in my mirror so I can forward the response header from my upstream to my mirror.

How do I do this? Here's my current configuration, but it doesn't work and I don't see the header DAV-response in my mirror. Is there another way to access all $upstream_http headers in the mirror block?

I have the following request mirroring setup in nginx:

location /ops/opendata/DataAccessViewer/ {
  mirror /davmirror;
  proxy_pass  https://<upstream>/;
  proxy_redirect  https://<upstream>/ /; 
}

location /davmirror {
    internal;
    proxy_pass https://<mirror>;
    proxy_set_header DAV-Original-URI $request_uri;  # <<<<-- works!
    proxy_set_header DAV-tier internals; # <<<<-- works!
    proxy_set_header DAV-response $upstream_http_location; # <<<-- DOESNT WORK!
}

Update

Open to alternative solutions to achieve this using nginx. I am aware of other non-nginx workarounds and in-fact using those as fallbacks at the moment. Ideally we would want this to be nginx solution.

Update This question seems to suggest that nginx is actually waiting on upstream response before resolving mirror?

Update

Verified that upstream does actually include a 'Location' Header:

enter image description here

Here's all the headers I receive in the mirror. Note that DAV-response is missing

enter image description here

Shaunak
  • 17,377
  • 5
  • 53
  • 84
  • is it even possible? Mirror is for mirroring request and doesn't do anything for responses. $upstream_http_* is for headers which are set by upstream response. when You are mirroring the request how do you want to add a header from response which is not available. – Yaser Kalali Aug 24 '23 at 13:31
  • is it possible is actually a valid question I am looking an answer for. Certain variables such as $request_uri are available, so if there's a way to make the mirror wait for a upstream response, then in theory reading upstream response headers should be possible. So looking for any possible solutions from more experienced folks. – Shaunak Aug 26 '23 at 03:26
  • @YaserKalali check this question pls: https://stackoverflow.com/questions/51644141/how-to-make-nginx-mirror-module-not-wait-for-response Seems to suggest nginx does actually wait on the upstream response? Am a interpreting it incorrectly? – Shaunak Aug 26 '23 at 03:34

2 Answers2

0

based on Krishan's answer,another approach is that you can try using the proxy_next_upstream directive,so in this case I'll capture the response headers from the upstream server and the proxy_next_upstream directive specifies which responses should trigger a retry to the next upstream server so by including invalid_header, I'll ensure that the response headers are captured!and then, in the mirror location block, I'll use the stored response header using the $http_dav_response variable!

location /ops/opendata/DataAccessViewer/ {
  proxy_pass  https://<upstream>/;
  proxy_redirect  https://<upstream>/ /;

  proxy_next_upstream error timeout invalid_header http_500 http_502 http_503 http_504;
  proxy_set_header DAV-Response $upstream_http_location;  # Store the response header in a variable
}

location /davmirror {
    internal;
    proxy_pass https://<mirror>;
    proxy_set_header DAV-Original-URI $request_uri;
    proxy_set_header DAV-tier internals;
    proxy_set_header DAV-Response $http_dav_response;  #just use the stored response header in the mirror block
}
Freeman
  • 9,464
  • 7
  • 35
  • 58
  • thanks for the response. I verified that upstream does actually include a 'Location' header. Updated the question with screenshot. `DAV-response $sent_http_location;` doesn't have any effect. I still don't see that header on the mirror side. Any clue? – Shaunak Aug 29 '23 at 03:00
  • "there might be an issue with how you're setting up the mirror block" - Is there any other configuration I need to check? All the configuration I have for the mirror is posted in the question. Do I need to check any other configs? – Shaunak Aug 29 '23 at 03:01
  • @Shaunak I'll update my answer plz check it out ... – Freeman Aug 29 '23 at 08:12
  • thanks for working through this with me, but none of those approaches are working! Check Krishan's answer, any clue why it doesn't like that config? – Shaunak Aug 30 '23 at 02:52
  • @Shaunak I updated my answer base on Krishan Saini's idea. – Freeman Aug 30 '23 at 10:08
  • thanks. I like where this is going. How will the proxy_next_upstream to use the /davmirror location block next? Also will this stop my request going to original upstream and eventual response from original upstream? I need that to keep working. – Shaunak Sep 01 '23 at 02:35
  • Nginx docs on http://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_next_upstream say: "One should bear in mind that passing a request to the next server is only possible if nothing has been sent to a client yet. That is, if an error or timeout occurs in the middle of the transferring of a response, fixing this is impossible." – Shaunak Sep 01 '23 at 02:37
0

Alright, so the issue here is a bit of head-scratcher. You see, Nginx's mirror is a bit limited when it comes to waiting for original request to complete. This is probably why $upstream_http_location ain't wokring in you'r mirror block.

But hey, there's a workaround you can try, though its not super clean. Have you heard of the post_action directive? It's not well documented. But it letys you do stuff after your original proxy_pass is done and the response headers are revievd.

Here's how you could tweak your config:

location /ops/opendata/DataAccessViewer/ {
  proxy_pass  https://<upstream>/;
  proxy_redirect  https://<upstream>/ /;
  post_action @davmirror;
}

location @davmirror {
  proxy_pass https://<mirror>;
  proxy_set_header DAV-Original-URI $request_uri;
  proxy_set_header DAV-tier internals;
  proxy_set_header DAV-response $upstream_http_location;
}

So, @davmirrorwill only kick in after the original proxy_pass gets its response. That way, $upstream_http_location should be populated and you can forward it to your mirror.

Again, it's not the most elegant solution. If you're looking for something more robust, you might have to go for app-level logic or some custom module.

Krishan Saini
  • 428
  • 1
  • 4
  • 8
  • Interesting approach! I have been suspecting this is the case of mirror not waiting on upstream response and so it's not reading responses. However when I try the post_action like you suggested, nginx now says the the config is invalid: `"proxy_pass" cannot have URI part in location given by regular expression, or inside named location, or inside "if" statement, or inside "limit_except" block in /etc/nginx/conf.d/proxy/database_services.conf:167` Any clue how to get around this? – Shaunak Aug 30 '23 at 02:51
  • Do I need to somehow use rewrite now? Sorry not fluent with that config, and appreciate any help – Shaunak Aug 30 '23 at 02:58