Score:0

Lighttpd URL and host matching and include-file spanning (is it possible?)

pk flag

I have an interesting condition resolution problem to resolve and have not had luck with on-line searching and looking through documentation for lighttpd yet. Many of those searches led to similar questions asked here and useful answers (for those questio so let's see how this one runs:

I have lighttpd running on a gateway router (OpenWRT, Or Turris OS if you prefer as it's a Turris Omnia) and it has a number of domains pointing its way which it farms out, as a revers proxy to servers on the LAN side of that gateway.

The general config being, in pro forma, like:

$HTTP["host"] =~ "(a.com|b.com|c.com)$" {
    proxy.server  = ( "" => ( ( "host" => "..." ) ) )
    ...
} else $HTTP["host"] =~ "(d.org|e.org)$" {
    proxy.server  = ( "" => ( ( "host" => "..." ) ) )
    ...
} else $HTTP["host"] =~ "(f.net|g.net)$" {
    proxy.server  = ( "" => ( ( "host" => "..." ) ) )
    ...
}

That has been working a dream for ages.

Now I would like a particular path, common to all these sites to be served from this router directly.

In pro forma again:

$HTTP["url"] =~ "^/topdir/subir/" {
    server.document-root = "/www/sharedstuff"
}

And I can combine this admirably as follows (and it works):

$HTTP["url"] =~ "^/topdir/subir/" {
    server.document-root = "/www/sharedstuff"
} else {
   $HTTP["host"] =~ "(a.com|b.com|c.com)$" {
       proxy.server  = ( "" => ( ( "host" => "..." ) ) )
       ...
   } else $HTTP["host"] =~ "(d.org|e.org)$" {
       proxy.server  = ( "" => ( ( "host" => "..." ) ) )
       ...
   } else $HTTP["host"] =~ "(f.net|g.net)$" {
       proxy.server  = ( "" => ( ( "host" => "..." ) ) )
       ...
   }
}

Sweet.

BUT, here's the problem I'm trying to solve. I'd like ideally to encapsulate the $HTTP["url"] condition in one included file and the $HTTP["host"] condition in another such that I can:

include "/etc/lighttpd/conf.d/shared.conf"      # contains the `$HTTP["url"]` constraint
include "/etc/lighttpd/conf.d/distributed.conf" # contains the `$HTTP["host"]` constraint

I wonder if I'm hoping for too much here. As I can't think or find of a way to do it.

I imagine if shared.conf contained some statement such that there existed a config statement like:

$HTTP["url"] =~ "^/topdir/subir/" {
    server.document-root = "/www/sharedstuff"
    ignore-all-subsequent-host-match-conditions 
}

Another creative, if naive and impossible, idea is if we could rewrite $HTTP["host"] akin to:

$HTTP["host"] = "null.net"

So that subsequent matches like $HTTP["host"] =~ "(a.com|b.com|c.com)$" all fail, and the request stays local.

Here are some options explored thus far:

Server variables

No go as these are evaluated when configuration is loaded not when requests are processed.

https://redmine.lighttpd.net/projects/1/wiki/docs_configuration#Using-variables

Request Headers

setenv.add-request-header looks attractive:

https://redmine.lighttpd.net/projects/lighttpd/wiki/Docs_ModSetEnv

If in shared.conf we set a custom request header perhaps we can test for it with a $REQUEST_HEADER["header"] in distributed.conf:

https://redmine.lighttpd.net/projects/1/wiki/docs_configuration#Conditional-Configuration

But I have not had any success with that. It seems that a conditional like this:

$REQUEST_HEADER["my_header"] == "value_I_set" {
   # Do not act as a reverse proxy 
} else $HTTP["host"] =~ "(a.com|b.com|c.com)$" {
    proxy.server  = ( "" => ( ( "host" => "..." ) ) )
    ...
} else $HTTP["host"] =~ "(d.org|e.org)$" {
    proxy.server  = ( "" => ( ( "host" => "..." ) ) )
    ...
} else $HTTP["host"] =~ "(f.net|g.net)$" {
    proxy.server  = ( "" => ( ( "host" => "..." ) ) )
    ...
}

Simply does not work, and I cannot really divine why. It's hard to see what's going on but if I log output on conditional handling it seems that $REQUEST_HEADER["my_header"] is always blank even for a URL where in shared.conf this matched:

$HTTP["url"] =~ "^/topdir/subir/" {
    setenv.add-request-header = ("my_header" => "value_I_set")
}

It seems the condition doesn't test request headers set by setenv, so much as those delivered.

Score:0
cn flag

One possible config solution is to put your shared config after the $HTTP["host"] conditions, and in your case, to overwrite the proxy config

$HTTP["url"] =~ "^/topdir/subir/" {
    server.document-root = "/www/sharedstuff"
    proxy.server = ()
}

Another solution, more flexible and powerful: lighttpd mod_magnet allows you to write arbitrarily complex logic in a few lines of lua. You could have your "shared" config handle certain requests (in your custom lua script) before lighttpd mod_proxy.

BTW, does the following naive solution also work? Shared config:

$HTTP["url"] =~ "^/topdir/subir/" {
    server.document-root = "/www/sharedstuff"
}

in shared.conf included in lighttpd.conf as

include "/etc/lighttpd/conf.d/shared.conf"      # contains the `$HTTP["url"]` constraint
else {
    include "/etc/lighttpd/conf.d/distributed.conf" # contains the `$HTTP["host"]` constraint
}
Bernd Wechner avatar
pk flag
Thanks, mod_magnet is always an option, though I tend to hesitate from using it until cornered. And given the fallback here is simply to keep both the configs in one file (not ideal but liveable) I haven't felt a pressing need to try mod_magnet here. It is an awesome fallback for very particular needs though yes.
Bernd Wechner avatar
pk flag
To clarify the first suggestion, though, it looks interesting and surprising to me. But Is it the case that if the `$HTTP["url"]` test is made after the `$HTTP["host"]` without an `else` that it would match and reset the `proxy.server` to `()` after it was set in the `$HTTP["host"]` section? The key learning being that without `else` all matching conditions are used and later ones can alter earlier settings? Well, at least `proxy.server`? A great insight if so, thanks!
cn flag
@BerndWechner that "great insight" is documented: [lighttpd Conditional Configuration Merging](https://wiki.lighttpd.net/Docs_Configuration#Conditional-Configuration-Merging)
Bernd Wechner avatar
pk flag
Documentation at times provides great insights indeed. While I read the doc regularly and first when stuck, it's certainly not always the case that one finds what one is looking for. But thanks very much for the tip and the doc pointer, greatly appreciated!
mangohost

Post an answer

Most people don’t grasp that asking a lot of questions unlocks learning and improves interpersonal bonding. In Alison’s studies, for example, though people could accurately recall how many questions had been asked in their conversations, they didn’t intuit the link between questions and liking. Across four studies, in which participants were engaged in conversations themselves or read transcripts of others’ conversations, people tended not to realize that question asking would influence—or had influenced—the level of amity between the conversationalists.