diff --git a/README.md b/README.md index 33ec073..2f00d1c 100644 --- a/README.md +++ b/README.md @@ -120,6 +120,7 @@ You can also use wildcards at the beginning and the end of host name, like `*.ba ### Path-based Routing You can have multiple containers proxied by the same `VIRTUAL_HOST` by adding a `VIRTUAL_PATH` environment variable containing the absolute path to where the container should be mounted. For example with `VIRTUAL_HOST=foo.example.com` and `VIRTUAL_PATH=/api/v2/service`, then requests to http://foo.example.com/api/v2/service will be routed to the container. If you wish to have a container serve the root while other containers serve other paths, give the root container a `VIRTUAL_PATH` of `/`. Unmatched paths will be served by the container at `/` or will return the default nginx error page if no container has been assigned `/`. +It is also possible to specify multiple paths with regex locations like `VIRTUAL_PATH=~^/(app1|alternative1)/`. For further details see the nginx documentation on location blocks. This is not compatible with `VIRTUAL_DEST`. The full request URI will be forwarded to the serving container in the `X-Forwarded-Path` header. @@ -139,6 +140,14 @@ $ docker run -d -e VIRTUAL_HOST=example.tld -e VIRTUAL_PATH=/app1/ -e VIRTUAL_DE In this example, the incoming request `http://example.tld/app1/foo` will be proxied as `http://app1/foo` instead of `http://app1/app1/foo`. +#### Per-VIRTUAL_PATH location configuration + +The same options as from [Per-VIRTUAL_HOST location configuration](#Per-VIRTUAL_HOST-location-configuration) are available on a `VIRTUAL_PATH` basis. +The only difference is that the filename gets an additional block `HASH=$(echo -n $VIRTUAL_PATH | sha1sum | awk '{ print $1 }')`. This is the sha1-hash of the `VIRTUAL_PATH` (no newline). This is done filename sanitization purposes. +The used filename is `${VIRTUAL_HOST}_${HASH}_location` + +The filename of the previous example would be `example.tld_8610f6c344b4096614eab6e09d58885349f42faf_location`. + #### DEFAULT_ROOT This environment variable of the nginx proxy container can be used to customize the return error page if no matching path is found. Furthermore it is possible to use anything which is compatible with the `return` statement of nginx. diff --git a/nginx.tmpl b/nginx.tmpl index b1e6249..dd48d4b 100644 --- a/nginx.tmpl +++ b/nginx.tmpl @@ -72,6 +72,8 @@ location {{ .Path }} { {{ if (exists (printf "/etc/nginx/vhost.d/%s_location" .Host)) }} include {{ printf "/etc/nginx/vhost.d/%s_location" .Host}}; + {{ else if (exists (printf "/etc/nginx/vhost.d/%s_%s_location" .Host (sha1 .Path) )) }} + include {{ printf "/etc/nginx/vhost.d/%s_%s_location" .Host (sha1 .Path) }}; {{ else if (exists "/etc/nginx/vhost.d/default_location") }} include /etc/nginx/vhost.d/default_location; {{ end }} diff --git a/test/test_virtual-path/alternate.conf b/test/test_virtual-path/alternate.conf new file mode 100644 index 0000000..541332e --- /dev/null +++ b/test/test_virtual-path/alternate.conf @@ -0,0 +1 @@ +rewrite ^/(web3|alt)/(.*) /$2 break; diff --git a/test/test_virtual-path/bar.conf b/test/test_virtual-path/bar.conf new file mode 100644 index 0000000..e8b0827 --- /dev/null +++ b/test/test_virtual-path/bar.conf @@ -0,0 +1 @@ +add_header X-test bar; diff --git a/test/test_virtual-path/foo.conf b/test/test_virtual-path/foo.conf new file mode 100644 index 0000000..8d8502d --- /dev/null +++ b/test/test_virtual-path/foo.conf @@ -0,0 +1 @@ +add_header X-test f00; \ No newline at end of file diff --git a/test/test_virtual-path/test_custom_conf.py b/test/test_virtual-path/test_custom_conf.py index 68ecd3a..eec149f 100644 --- a/test/test_virtual-path/test_custom_conf.py +++ b/test/test_virtual-path/test_custom_conf.py @@ -4,3 +4,35 @@ def test_default_root_response(docker_compose, nginxproxy): r = nginxproxy.get("http://nginx-proxy.test/") assert r.status_code == 418 +@pytest.mark.parametrize("stub,header", [ + ("nginx-proxy.test/web1", "bar"), + ("foo.nginx-proxy.test", "f00"), +]) +def test_custom_applies(docker_compose, nginxproxy, stub, header): + r = nginxproxy.get(f"http://{stub}/port") + assert r.status_code == 200 + assert "X-test" in r.headers + assert header == r.headers["X-test"] + +@pytest.mark.parametrize("stub,code", [ + ("nginx-proxy.test/foo", 418), + ("nginx-proxy.test/web2", 200), + ("nginx-proxy.test/web3", 200), + ("bar.nginx-proxy.test", 503), +]) +def test_custom_does_not_apply(docker_compose, nginxproxy, stub, code): + r = nginxproxy.get(f"http://{stub}/port") + assert r.status_code == code + assert "X-test" not in r.headers + +@pytest.mark.parametrize("stub,port", [ + ("nginx-proxy.test/web1", 81), + ("nginx-proxy.test/web2", 82), + ("nginx-proxy.test/web3", 83), + ("nginx-proxy.test/alt", 83), +]) +def test_alternate(docker_compose, nginxproxy, stub, port): + r = nginxproxy.get(f"http://{stub}/port") + assert r.status_code == 200 + assert r.text == f"answer from port {port}\n" + diff --git a/test/test_virtual-path/test_custom_conf.yml b/test/test_virtual-path/test_custom_conf.yml index 2369bac..abd9d0c 100644 --- a/test/test_virtual-path/test_custom_conf.yml +++ b/test/test_virtual-path/test_custom_conf.yml @@ -1,3 +1,12 @@ + +foo: + image: web + expose: + - "42" + environment: + WEB_PORTS: "42" + VIRTUAL_HOST: "foo.nginx-proxy.test" + web1: image: web expose: @@ -18,6 +27,15 @@ web2: VIRTUAL_PATH: "/web2/" VIRTUAL_DEST: "/" +web3: + image: web + expose: + - "83" + environment: + WEB_PORTS: "83" + VIRTUAL_HOST: "nginx-proxy.test" + VIRTUAL_PATH: "~ ^/(web3|alt)/" + sut: image: nginxproxy/nginx-proxy:test environment: @@ -25,3 +43,7 @@ sut: volumes: - /var/run/docker.sock:/tmp/docker.sock:ro - ../lib/ssl/dhparam.pem:/etc/nginx/dhparam/dhparam.pem:ro + - ./foo.conf:/etc/nginx/vhost.d/foo.nginx-proxy.test:ro + - ./bar.conf:/etc/nginx/vhost.d/nginx-proxy.test_918d687a929083edd0c7224ee2293e0e7c062ab4_location:ro + - ./alternate.conf:/etc/nginx/vhost.d/nginx-proxy.test_7fb22b74bbdf907425dbbad18e4462565cada230_location:ro +