HAproxy domain name to backend mapping for path(url) based routing
Asked Answered
B

1

15

Does HAProxy support domain name to backend mapping for path based routing.

Currently it does support maps for vhost:

frontend xyz
   <other_lines>
   use_backend backend1 if { hdr(Host) -i myapp.domain1.com }
   use_backend backend2 if { hdr(Host) -i myapp.domain2.com }

Can be rewritten using maps as:

frontend xyz
   <other_lines>
   use_backend %[req.hdr(host),lower,map_dom(/path/to/map,default)]

With the contents of map file as:

#domainname  backendname
myapp.domain1.com backend1
myapp.domain2.com backend2

But if the routing is based on paths as shown in the example below:

frontend xyz
   acl host_server_myapp hdr(host) -i myapp.domain.com
   acl path_path1 path_beg /path1
   acl path_path2 path_beg /path2
   use_backend backend1 if host_server_myapp path_path1
   use_backend backend2 if host_server_myapp path_path2

Is it possible to have mapping for this usecase? Using base instead of hdr(host) might give the entire path but it will not have the flexibility of domains since base is string comparison. Is there an other way to convert this to haproxy maps.

Beckham answered 16/6, 2016 at 11:49 Comment(3)
There's a map_beg() converter in the docs. It's not really explained, but it seems like you could use that along with the layer 7 path fetch, for something along the lines of use_backend %[path,map_beg(...Mattias
But that will not include the domain name, path will just give the path part of the request.Beckham
Ah, I saw that you were matching on paths, overlooked that you were testing two ACLs, one for path, one for host. There may still be a solution, I'll look at it in more detail to see if this can readily be done.Mattias
M
13

Start with the Layer 7 base fetch --

This returns the concatenation of the first Host header and the path part of the request, which starts at the first slash and ends before the question mark.

...then use map_beg() to match the beginning of the string to the map.

use_backend %[base,map_beg(/etc/haproxy/testmap.map,default)]

If the map file /etc/haproxy/testmap.map has a line matching the prefix, the backend in the map file is used. Otherwise, the backend called default will be used (that's the 2nd argument to map_beg() -- the value to be returned if the map doesn't match).

If the resulting backend doesn't actually exist, HAProxy continues processing the request as if this statement weren't configured at all.

So your map file would look something like this:

example.com/foo     this-backend # note, also matches /foo/ba 
example.com/foo/bar that-backend # note, matches /foo/bar
example.org/foo     some-other-backend

To treat a subdomain as equivalent to the parent domain (e.g., treating example.com and www.example.com to be handled equivalently, without map duplication, as discussed in comments) the regsub() converter could be used to modify the value passed to the map:

use_backend %[base,regsub(^www\.,,i),map_beg(/etc/haproxy/testmap.map,default)]
Mattias answered 17/6, 2016 at 21:38 Comment(6)
This is not fool proof, consider the case where you have backends named backend1 and backend11, backend1 will prefix match both these cases, which is not correct.Beckham
What? No, we're not matching on back-end prefixes. We're matching on request hostname + path prefixes. The back-end name has to be exactly the value in the second column of the map file.Mattias
Sorry ignore that, so if its a base match, it doesn't care about domain, it will work for example.com/foo but fail for www.example.com/foo.Beckham
Yes, but www.example.com and example.com are two different hosts, just as example.com and example.org are two different hosts. You shouldn't be serving identical content from two or more hostnames... You should consider one of them as canonical, and redirect the other one to it. Otherwise, duplicate the map entries or rewrite the host header to exclude www. if you really want the sites to be treated identical, using something like http-request set-header Host %[hdr(host),regsub(^www\.,,i)] if { hdr_beg(host) -i www. } before doing the map lookup.Mattias
Or even modify the interim value on its way to the lookup use_backend %[base,regsub(^www\.,,i),map_beg(/etc/haproxy/testmap.map,default)] which will use the modified hostname only for the map lookup, without changing base.Mattias
+1 but is there any way to make this work with a per-host default? E.g. I want mappings like this: host1.example.com/specialpath1 -> backend1; Any other path on host1.example.com -> backend2; host2.example.com/specialpath2 -> backend3; Any other path on host2.example.com -> backend4.Dogooder

© 2022 - 2024 — McMap. All rights reserved.