I’m trying to convert a `web.config` file to use t...
# box-products
r
I’m trying to convert a
web.config
file to use the
CommandBox
Undertow Server Rules in my
server.json
file. I’ve managed to sort the simple rewrites but I seem to be failing with the regex rules. I have a request to
/44423ee780249169596c419e18f13e15/stocklist/
which I think is being caught by this rule in IIS...
Copy code
<rule name="stocklist filters" enabled="true" stopProcessing="true">
    <match url="^([0-9a-z]+)/stocklist/([^/]+)/?$" />
    <action type="Rewrite" url="?page=stocklist&aId={R:1}&f={R:2}" />
</rule>
My conversion of this IIS rule to Undertow in my
server.json
is...
Copy code
"rules":[
    "regex( '^([0-9a-z]+)/stocklist/([^/]+)/?$' ) -> rewrite( '?page=stocklist&aId={R:1}&f={R:2}' )"
]
...but it seems like the request just sails on pass this rewrite. The situation is a little more complicated as it’s part of a SSO request and callback but I’ve worked through the request and I’m sure this is the point where it’s coming apart. Now plugging that regex into my favourite helper, the request
/44423ee780249169596c419e18f13e15/stocklist/
isn’t being caught by
^([0-9a-z]+)/stocklist/([^/]+)/?$
and it says that the
/
needs to be escaped with
\/
but when I do that, the
\/
is automatically replaced with
/
in the
server.json
when I start the server. Also I’m not sure what the
([^/]+)/?
part does at the end? So in my regex helper, if I use
^\/([0-9a-z]+)\/stocklist\/([^\/]+)\/?$
that will match
/44423ee780249169596c419e18f13e15/stocklist/foo
and if I use
^\/([0-9a-z]+)\/stocklist\/$
it will find
/44423ee780249169596c419e18f13e15/stocklist/
So is IIS more tolerant than Undertow or have I got it all wrong (very possible!)?
t
in you original example you are missing the initial
/
([^/]+)/?$
means match anything that isnt a
/
followed by an optional / upto the end of the url and save it so if you url was
1234/stocklist/wibble
it would match and save
wibble
or if it was
1234/stocklist/foobarbaz/
it would save
foobarbaz
but if it was
1234/stocklist/wibble/blather
it wouldnt save anything
r
That IIS rule is what is currently in production. I’m trying to get this site working locally with CommandBox without IIS. So I took the
match url=
directly from the
web.config
and dropped it into Undertow regex. I agree that starting the regex with
^\/([0-9
would match the incoming string but it’s strange, to me, that the regex is different. For that last part, if there’s no trailing part the whole regex fails to match.
message has been deleted
b
I wouldn't expect forwardslashes to need escaped
Any time you do escape anything, you need TWO backslashes since you're inside JSON
Your capture group place holder beeds to be ${1} I think (check the examples in the docs), but it will need escaped in your server.json so CommandBox itself doesn't try to expand it when it reads the json file
So, \\${1}
r
Good point on the \\ in json - maybe I’ll move to a simple rulesfile.txt - that shouldn’t worry about being escaped?
b
But tge lack of a leading slash is probably why the rule is not getting matched. Webservers differ on that
Regardless, do a Server start --console --trace And you'll have all the debugging you could ever want
Including the full details of the regex inputs and matches
r
Yes, I tried that and got swamped with data and found it hard to pick the interesting stuff out of the hosepipe
b
And yes, moving rules to a text file makes it much more readable
You don't need any JSON or commandbox system settings escaped, plus complex rules can span lines
Simplify 😉
r
Let me try that and see where that leads - thanks for your help - get back to the beach!
b
Remove all rules but the one you're testing
Set your profile to "none" to temp disable internal rules
r
Okay, I’ll look that up
b
Then, hit "enter" a couple times in the console before refreshing the browser
Then you can see just the debugging for that one rule
👍 1
It's the grand Canyon today 😁
🤘🏼 1
r
“Wear sunscreen” 😎
1
😂 1
b
@richard.herbert I'm back from holiday for the next couple days. Did you get this all working or are there still some snags?
r
Thanks for remembering me @bdw429s and I hope you had a good break! I’m still having issues with this. I think it’s being complicated by all the redirects that are being implemented as part of the sso for this app. I’ve even tried to go the
.htaccess
route which is a great alternative given the automatic polling of the file. So if I give you one example of my conversion I’d be grateful for your opinion. This is a rule from the
web.config
file…
Copy code
<rule name="oidc_login_validation/" enabled="true" stopProcessing="true">
    <match url="^oidc_login_validation/?$" />
    <action type="Rewrite" url="/pages/system/oidc_login_validation.cfm?includeHeader=false&page=oidc_login_validation" />
</rule>
…and here is the section from my
rewriteRules.txt
Copy code
regex( pattern='^/oidc_login_validation/?$', case-sensitive=false ) -> rewrite( '/pages/system/oidc_login_validation.cfm?includeHeader=false&page=oidc_login_validation' )
…and this is my
.htaccess
Copy code
RewriteRule ^/oidc_logout_validation/?$    /pages/system/oidc_logout_validation.cfm?includeHeader=false&page=oidc_logout_validation
…which all seems fair. The only part I’m not sure about is the
stopProcessing="true"
and how I should implement that?
I’ve now found
[L]
which I’ve added to stop it processing
I’ve made some more progress but I’m stumped with this…
Copy code
<rule name="redirect to vdp" enabled="true" stopProcessing="true">
    <match url="^redirect/?$" />
    <conditions logicalGrouping="MatchAll" trackAllCaptures="false">
        <add input="{QUERY_STRING}" pattern="raId=(aeta[0-9]+)" />
        <add input="{QUERY_STRING}" pattern="rvId=(aetv[0-9]+)" />
    </conditions>
    <action type="Rewrite" url="/pages/system/redirect.cfm?includeHeader=false&page=redirect" appendQueryString="true" />
</rule>
…this…
Copy code
<rule name="LowerCase Rule" stopProcessing="true">
    <match url="[A-Z]" ignoreCase="false" />
    <conditions logicalGrouping="MatchAll" trackAllCaptures="false">
        <add input="{URL}" pattern="^.*\.(css|js|jpg|jpeg|png|gif|pdf|csv|xml|ttf|eot|svg|woff|ico|woff2|json|swf|xap|cfm)$" ignoreCase="true" negate="true" />
    </conditions>
    <action type="Redirect" url="{ToLower:{URL}}" redirectType="Permanent" />
</rule>
…and this…
Copy code
<rule name="Add trailing slash" stopProcessing="false">
    <match url="(.*[^/])$" />
    <conditions logicalGrouping="MatchAll" trackAllCaptures="false">
        <add input="{URL}" pattern="^.*\.(css|js|jpg|jpeg|png|gif|pdf|csv|xml|ttf|eot|svg|woff|ico|woff2|json|html|cfm|swf|xap|cfc|map|scss)$" ignoreCase="true" negate="true" />
    </conditions>
    <action type="Redirect" url="{R:1}/" redirectType="Permanent" />
</rule>
b
Let's figure out one rewrite engine at a time, lol
The equiv handler for "last" is the
done
handler in CommandBox's Server Rules (Undertow's Predicate Language)
r
For sure - I’m having more success with
htaccess
b
However, unless you have a more generic rule after the fact that may fire, there's usually not a need for the last flag
It seems a lot of people throw it in for whatever reason
Also, be cautious throwing
done
around as your custom rewrites fire before CommandBox internal rules. This was done to give you more power in overriding the internal rules, but it also allows you to short circuit the lockdown rules
i.e. if you put in a rule that rewrites something like
lucee/admin
and then uses the
done
handler, CommandBox automatic CF-lockdown rules will be skipped.
Just something to keep in mind
You'll find the
done
handler listed here under common handlers. Perhaps we can add a note that it's the equiv of the
L
or "last" flag https://commandbox.ortusbooks.com/embedded-server/configuring-your-server/server-rules/rule-language#common-handlers
r
Okay - I must say I like the clean look of the Undertow rules but I’m getting more success with htaccess
b
And here's an example of it in use:
Copy code
"regex( pattern='\.jws$', case-sensitive=false ) -> { set-error( 404 ); done }"
And, if you're using an external text file for your server rule, you can make that a log more readable
Copy code
regex( pattern='\.jws$', case-sensitive=false ) -> {
  set-error( 404 );
  done
}
r
Nice
b
The nature of JSON forces the rules to be one-liners 😕
r
Indeed
I’d certainly like to come back to implementing Undertow rules once I have a functioning system which seems to be closer with htaccess right now
b
This format is interesting as I'm not sure either Tuckey or Undertow's server rules have a concept quite like the conditions block
Copy code
<rule name="redirect to vdp" enabled="true" stopProcessing="true">
    <match url="^redirect/?$" />
    <conditions logicalGrouping="MatchAll" trackAllCaptures="false">
        <add input="{QUERY_STRING}" pattern="raId=(aeta[0-9]+)" />
        <add input="{QUERY_STRING}" pattern="rvId=(aetv[0-9]+)" />
    </conditions>
    <action type="Rewrite" url="/pages/system/redirect.cfm?includeHeader=false&page=redirect" appendQueryString="true" />
</rule>
You're not needing to track the captures, so this could be accomplished with 3 checks like so: (untested)
Copy code
regex( "^redirect/?$" ) 
  and regex( value="%{QUERY_STRING}", pattern="raId=(aeta[0-9]+)" ) 
  and regex( value="%{QUERY_STRING}", pattern="rvId=(aetv[0-9]+)" )
  -> rewrite( "/pages/system/redirect.cfm?includeHeader=false&page=redirect" )
(The rewrite handler should automatically append the query string you provide to the existing one)
This one is also interesting as I'm not sure either Tucky or Undertow have a lowercase function, even though I assume both rewrite engines would allow this to be written as a custom rule.
Copy code
<rule name="LowerCase Rule" stopProcessing="true">
    <match url="[A-Z]" ignoreCase="false" />
    <conditions logicalGrouping="MatchAll" trackAllCaptures="false">
        <add input="{URL}" pattern="^.*\.(css|js|jpg|jpeg|png|gif|pdf|csv|xml|ttf|eot|svg|woff|ico|woff2|json|swf|xap|cfm)$" ignoreCase="true" negate="true" />
    </conditions>
    <action type="Redirect" url="{ToLower:{URL}}" redirectType="Permanent" />
</rule>
It sounds like you're trying to clean up sloppy URLs that don't match the case of the files on a *nix file system. This rule wouldn't really be necessary if you're developing locally on Windows, but I would actually recommend you look at another feature of CommandBox which is forced case-insensitivity which is designed just for this. https://commandbox.ortusbooks.com/embedded-server/configuring-your-server/experimental-features#forcing-case-insensitivity
Copy code
server set runwar.args="--case-sensitive-web-server=false"
And finally this rule
Copy code
<rule name="Add trailing slash" stopProcessing="false">
    <match url="(.*[^/])$" />
    <conditions logicalGrouping="MatchAll" trackAllCaptures="false">
        <add input="{URL}" pattern="^.*\.(css|js|jpg|jpeg|png|gif|pdf|csv|xml|ttf|eot|svg|woff|ico|woff2|json|html|cfm|swf|xap|cfc|map|scss)$" ignoreCase="true" negate="true" />
    </conditions>
    <action type="Redirect" url="{R:1}/" redirectType="Permanent" />
</rule>
I'm sort of curious what the purpose of this rule actually is. Undertow will automatically redirect for you if you hit a folder named
foo
and your URL just says
<http://site.com/foo|site.com/foo>
it will redirect you to
<http://site.com/foo/|site.com/foo/>
so I'm not sure a rule like this is needed unless you're wanting it to kick in for requests that aren't to existing folders. 🤔
r
Just to be clear, this is an inherited project 🙂 but I appreciate the advice. I’ll look that runwar arg
b
Either way, you'd just implement it without the conditions block as two conditions. Something like this
Copy code
regex( "(.*[^/])$" ) 
 and not regex( pattern="^.*\.(css|js|jpg|jpeg|png|gif|pdf|csv|xml|ttf|eot|svg|woff|ico|woff2|json|html|cfm|swf|xap|cfc|map|scss)$", case-sensitive=false )
  -> redirect('%{REQUEST_URL}/')
Note, that will use a 302 until this ticket is completed https://issues.redhat.com/browse/UNDERTOW-1734 So if you need a 301, you'd need to do this:
Copy code
...
  -> { redirect('%{REQUEST_URL}/'); response-code(301) }
Just to be clear, this is an inherited project
Of course 😉 But to convert the rules, we first must understand what they do! 🙂
r
for sure!