nginx / unit

NGINX Unit - universal web app server - a lightweight and versatile open source server that simplifies the application stack by natively executing application code across eight different programming language runtimes.
https://unit.nginx.org
Apache License 2.0
5.27k stars 322 forks source link

rewrite works twice when passing to application target and $host used in target's name #1058

Open veremax opened 5 months ago

veremax commented 5 months ago

Hello! I am using Unit 1.31.1 with php 8.2 on Debian.

Seems, that I caught error in Unit's rewrite engine.

Rewrite running twice when passing to application target and $host variable is used in target name.

Example:

{
    "match": {
        "uri": [ "*.css", "*.js", "*.html" ]
    },
    "action": {
        "share": "/var/www/${host}/public${uri}",
        "chroot": "/var/www/${host}/public/",
        "response_headers": {
            "Content-Type": "${response_header_content_type}; charset=utf-8",
            "Cache-Control": "`${ uri.endsWith('.html') ? 'max-age=0' : 'max-age=86400, immutable' }`"
        },
        "fallback": {
            "rewrite": "/404${uri}",
            "pass": "applications/php82/${host}-40x"
        }
    }
}

If I request some non-existent file /abc.html, php target gets /404/404/abc.html as $_SERVER['REQUEST_URI'].

This is a log:

2024/01/12 00:25:59 [notice] 1946#1952 *10 "fallback" taken
2024/01/12 00:25:59 [notice] 1946#1952 *10 URI rewritten to "/404/abc.html"
2024/01/12 00:25:59 [notice] 1946#1952 *10 URI rewritten to "/404/404/abc.html"
2024/01/12 00:25:59 [info] 1960#1960 "php82" application started

If I remove variable $host from application target name and hardcode the host name, all works OK.

tippexs commented 5 months ago

Good Morning and sorry for letting you wait for so long! Thanks for reporting this to us! I was able to reproduce this by using an application and pass configuration. With just return this bug will not kick in. My configuration looks like:

{
  "listeners": {
    "*:8080": {
      "pass": "routes"
    }
  },
  "routes": [
    {
      "match": {
        "uri": [
          "*.css",
          "*.js",
          "*.html"
        ]
      },
      "action": {
        "share": "/var/www/${host}/public${uri}",
        "chroot": "/var/www/${host}/public/",
        "response_headers": {
          "Content-Type": "${response_header_content_type}; charset=utf-8",
          "Cache-Control": "`${ uri.endsWith('.html') ? 'max-age=0' : 'max-age=86400, immutable' }`"
        },
        "fallback": {
          "rewrite": "/404${uri}",
          "return": "applications/${host}-40x"
        }
      }
    }
  ],
  "applications": {
    "php82": {
      "type": "php",
      "targets": {
        "foo.com-40x": {
          "root": "/var/www/html/",
          "script": "index.php"
        }
      }
    }
  },
  "settings": {
    "http": {
      "log_route": true
    }
  }
}

The request

curl -v -H "Host: foo.com" 127.1:8080/abc.html

Output

root@215398a0f3e4:/var/www# curl -v -H "Host: foo.com" 127.1:8080/abc.html
*   Trying 127.0.0.1:8080...
* Connected to 127.1 (127.0.0.1) port 8080 (#0)
> GET /abc.html HTTP/1.1
> Host: foo.com
> User-Agent: curl/7.74.0
> Accept: */*
>
2024/01/15 08:37:25 [notice] 408#428 *101 http request line "GET /abc.html HTTP/1.1"
2024/01/15 08:37:25 [notice] 408#428 *101 "routes/0" selected
2024/01/15 08:37:25 [notice] 408#428 *101 "fallback" taken
2024/01/15 08:37:25 [notice] 408#428 *101 URI rewritten to "/404/abc.html"
2024/01/15 08:37:25 [notice] 408#428 *101 URI rewritten to "/404/404/abc.html"
* Mark bundle as not supporting multiuse
< HTTP/1.1 200 OK
< X-Powered-By: PHP/8.2.14
< Content-type: text/html; charset=UTF-8
< Server: Unit/1.31.1
< Date: Mon, 15 Jan 2024 08:37:25 GMT
< Transfer-Encoding: chunked
<
array(20) {
  ["SERVER_SOFTWARE"]=>
  string(11) "Unit/1.31.1"
  ["SERVER_PROTOCOL"]=>
  string(8) "HTTP/1.1"
  ["PHP_SELF"]=>
  string(10) "/index.php"
  ["SCRIPT_NAME"]=>
  string(10) "/index.php"
  ["SCRIPT_FILENAME"]=>
  string(23) "/var/www/html/index.php"
  ["DOCUMENT_ROOT"]=>
  string(13) "/var/www/html"
  ["REQUEST_METHOD"]=>
  string(3) "GET"
  ["REQUEST_URI"]=>
  string(17) "/404/404/abc.html"
  ["QUERY_STRING"]=>
  string(0) ""
  ["REMOTE_ADDR"]=>
  string(9) "127.0.0.1"
  ["SERVER_ADDR"]=>
  string(9) "127.0.0.1"
  ["SERVER_NAME"]=>
  string(7) "foo.com"
  ["SERVER_PORT"]=>
  string(2) "80"
  ["HTTP_HOST"]=>
  string(7) "foo.com"
  ["HTTP_USER_AGENT"]=>
  string(11) "curl/7.74.0"
  ["HTTP_ACCEPT"]=>
  string(3) "*/*"
  ["REQUEST_TIME_FLOAT"]=>
  float(1705307845.475887)
  ["REQUEST_TIME"]=>
  int(1705307845)
  ["argv"]=>
  array(0) {
  }
  ["argc"]=>
  int(0)
}
* Connection #0 to host 127.1 left intact

I have used the latest PHP Unit Docker container to test this configuration. We will investigate!

veremax commented 5 months ago

@tippexs Hello and thank you for your reply! Glad that this bug is reproduceable.

You suggestion to use "return" has one side effect - it is a full redirection with changing the visible page address in the browser. Now I've configured this part by making separate php app targets (and php scripts) for 403, 404 and so on. But I am waiting for your investigation's results!