Skip to content

Commit 19bd2fa

Browse files
committed
HRW/HRW4U: Adds SERVER-HEADER & SERVER-URL
1 parent 15cdb35 commit 19bd2fa

21 files changed

Lines changed: 218 additions & 27 deletions

doc/admin-guide/configuration/hrw4u.en.rst

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -181,7 +181,9 @@ cond %{ACCESS:/path} access("/path") File exists
181181
cond %{CACHE} =hit-fresh cache() == "hit-fresh" Cache lookup result status
182182
cond %{CIDR:24,48} =ip cidr(24,48) == "ip" Match masked client IP address
183183
cond %{CLIENT-HEADER:X} =foo inbound.req.X == "foo" Original client request header
184+
cond %{SERVER-HEADER:X} =foo outbound.req.X == "foo" Server request header (sent to origin)
184185
cond %{CLIENT-URL:<C>} =bar inbound.url.<C> == "bar" URL component match, <:ref:`C<admin-plugins-header-rewrite-url-parts>`> is ``host``, ``path`` etc.
186+
cond %{SERVER-URL:<C>} =bar outbound.url.<C> == "bar" Server request URL component (sent to origin)
185187
cond %{COOKIE:foo} =bar {in,out}bound.cookie.foo == "bar" Check a cookie value
186188
cond %{FROM-URL:<C>} =bar from.url.<C> == "bar" Remap ``From URL`` component match, <:ref:`C<admin-plugins-header-rewrite-url-parts>`> is ``host`` etc.
187189
cond %{HEADER:X} =fo {in,out}bound.{req,resp}.X == "fo" Context sensitive header conditions
@@ -195,22 +197,36 @@ cond %{IP:SERVER} ="..." outbound.ip == "..." Upstream (ne
195197
cond %{IP:OUTBOUND} ="..." outbound.server == "..." ATS's outbound IP address, connecting upstream
196198
cond %{LAST-CAPTURE:<#>} ="..." capture.<#> == "..." Last capture group from regex match (range: `0-9`)
197199
cond %{METHOD} =GET inbound.method == "GET" HTTP method match
198-
cond %{NEXT-HOP:<C>} ="bar" outbound.url.<C> == "bar" Next-hop URL component match, <:ref:`C<admin-plugins-header-rewrite-url-parts>`> is ``host`` etc.
200+
cond %{NEXT-HOP:<C>} ="bar" nexthop.<C> == "bar" Next-hop destination, ``<C>`` is ``host``, ``port``, or ``strategy``
199201
cond %{NOW:<U>} ="..." now.<U> == "..." Current date/time in format, <:ref:`U<admin-plugins-header-rewrite-geo>`> selects time unit
200202
cond %{OUTBOUND:CLIENT-CERT:<X>} outbound.client-cert.<X> Access the mTLS / client certificate details, on the outbound (upstream) connection
201203
cond %{OUTbOUND:SERVER-CERT:<X>} outbound.client-cert.<X> Access the server (handshake) certificate details, on the outbound connection
202204
cond %{RANDOM:500} >250 random(500) > 250 Random number between 0 and the specified range
203205
cond %{SSN-TXN-COUNT} >10 ssn-txn-count() > 10 Number of transactions on server connection
204206
cond %{TO-URL:<C>} =bar to.url.<C> == "bar" Remap ``To URL`` component match, <:ref:`C<admin-plugins-header-rewrite-url-parts>`> is ``host`` etc.
205207
cond %{TXN-COUNT} >10 txn-count() > 10 Number of transactions on client connection
206-
cond %{URL:<C> =bar {in,out}bound.url.<C> == "bar" Context aware URL component match
208+
cond %{URL:<C> =bar inbound.url.<C> == "bar" Context aware URL component match (use ``inbound.url`` or ``outbound.url``)
207209
cond %{GEO:<C>} =bar geo.<C> == "bar" IP to Geo mapping. <:ref:`C<admin-plugins-header-rewrite-geo>`> is country, asn, etc.
208210
cond %{STATUS} =200 inbound.status ==200 Origin http status code
209211
cond %{TCP-INFO} tcp.info TCP Info struct field values
210212
cond %{HTTP-CNTL:<C>} http.cntl.<C> Check the state of the <:ref:`C<admin-plugins-header-rewrite-set-http-cntl>`> HTTP control
211213
cond %{INBOUND:<C>} {in,out}bound.conn.<c> inbound (:ref:`client, user agent<admin-plugins-header-rewrite-inbound>`) connection to ATS
212214
================================ ================================== ================================================
213215

216+
.. note::
217+
**Header and URL prefix summary:**
218+
219+
- ``inbound.req.<header>`` → ``CLIENT-HEADER`` - Headers from the client request
220+
- ``outbound.req.<header>`` → ``SERVER-HEADER`` - Headers in the request sent to origin
221+
- ``inbound.url.<part>`` → ``CLIENT-URL`` - URL from the original client request
222+
- ``outbound.url.<part>`` → ``SERVER-URL`` - URL in the request sent to origin (after remapping)
223+
- ``nexthop.<field>`` → ``NEXT-HOP`` - Network destination info (host, port, strategy)
224+
225+
The distinction between ``outbound.url`` and ``nexthop`` is important:
226+
227+
- ``outbound.url`` is the HTTP request URL (what's in the request line/Host header)
228+
- ``nexthop`` is the network destination (where ATS connects, may be a parent proxy)
229+
214230
The conditions operating on headers and URLs are also available as operators. E.g.:
215231

216232
.. code-block:: none
@@ -271,9 +287,9 @@ HRW4U provides a special ``+=`` operator for adding headers::
271287

272288
The ``+=`` operator only works with the following pre-defined symbols:
273289

274-
- ``inbound.req.<header>`` - Client request headers
290+
- ``inbound.req.<header>`` - Client request headers (maps to ``CLIENT-HEADER``)
275291
- ``inbound.resp.<header>`` - Origin response headers
276-
- ``outbound.req.<header>`` - Outbound request headers (context-restricted)
292+
- ``outbound.req.<header>`` - Server request headers (maps to ``SERVER-HEADER``)
277293
- ``outbound.resp.<header>`` - Outbound response headers (context-restricted)
278294

279295
.. note::

doc/admin-guide/plugins/header_rewrite.en.rst

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -369,6 +369,25 @@ header operated on by this condition will be a comma separated string of the
369369
values from every occurrence of the header. More details are provided in
370370
`Repeated Headers`_ below.
371371

372+
SERVER-HEADER
373+
~~~~~~~~~~~~~
374+
::
375+
376+
cond %{SERVER-HEADER:<name>} <operand>
377+
378+
Value of the header ``<name>`` from the request sent to the origin server
379+
(regardless of the hook context in which the rule is being evaluated). This is
380+
useful when you need to check headers that have been modified or added during
381+
the request processing before being sent to the origin. Note that some headers
382+
may appear in an HTTP message more than once. In these cases, the value of the
383+
header operated on by this condition will be a comma separated string of the
384+
values from every occurrence of the header. More details are provided in
385+
`Repeated Headers`_ below.
386+
387+
Note that the server request headers are only available after the
388+
``SEND_REQUEST_HDR_HOOK`` has been reached. Using this condition in earlier
389+
hooks will result in an empty value.
390+
372391
CLIENT-URL
373392
~~~~~~~~~~
374393
::
@@ -385,6 +404,20 @@ phase of the transaction. This happens when there is no host in the incoming UR
385404
and only set as a host header. During the remap phase the host header is copied
386405
to the CLIENT-URL. Use CLIENT-HEADER:Host if you are going to match the host.
387406

407+
SERVER-URL
408+
~~~~~~~~~~
409+
::
410+
411+
cond %{SERVER-URL:<part>} <operand>
412+
413+
The URL of the request being sent to the origin server. This is the URL after
414+
any remapping and modifications have been applied. The ``<part>`` may be
415+
specified according to the options documented in `URL Parts`_.
416+
417+
Note that the server request URL is only available after the
418+
``SEND_REQUEST_HDR_HOOK`` has been reached. Using this condition in earlier
419+
hooks will result in an empty value.
420+
388421
CIDR
389422
~~~~
390423
::

plugins/header_rewrite/conditions.cc

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -224,12 +224,20 @@ ConditionHeader::append_value(std::string &s, const Resources &res)
224224
TSMLoc hdr_loc;
225225
int len;
226226

227-
if (_client) {
227+
switch (_type) {
228+
case CLIENT:
228229
bufp = res.client_bufp;
229230
hdr_loc = res.client_hdr_loc;
230-
} else {
231+
break;
232+
case SERVER:
233+
bufp = res.server_bufp;
234+
hdr_loc = res.server_hdr_loc;
235+
break;
236+
case HEADER:
237+
default:
231238
bufp = res.bufp;
232239
hdr_loc = res.hdr_loc;
240+
break;
233241
}
234242

235243
if (bufp && hdr_loc) {
@@ -272,8 +280,13 @@ ConditionUrl::initialize(Parser &p)
272280
Condition::initialize(p);
273281

274282
auto match = std::make_unique<MatcherType>(_cond_op);
283+
275284
match->set(p.get_arg(), mods());
276285
_matcher = std::move(match);
286+
287+
if (_type == SERVER) {
288+
require_resources(RSRC_SERVER_REQUEST_HEADERS);
289+
}
277290
}
278291

279292
void
@@ -318,6 +331,18 @@ ConditionUrl::append_value(std::string &s, const Resources &res)
318331
TSError("[%s] Error getting the pristine URL", PLUGIN_NAME);
319332
return;
320333
}
334+
} else if (_type == SERVER) {
335+
Dbg(pi_dbg_ctl, " Using the server request url");
336+
bufp = res.server_bufp;
337+
if (bufp && res.server_hdr_loc) {
338+
if (TSHttpHdrUrlGet(bufp, res.server_hdr_loc, &url) != TS_SUCCESS) {
339+
TSError("[%s] Error getting the server request URL", PLUGIN_NAME);
340+
return;
341+
}
342+
} else {
343+
Dbg(pi_dbg_ctl, " Server request not available");
344+
return;
345+
}
321346
} else if (res._rri != nullptr) {
322347
// called at the remap hook
323348
bufp = res._rri->requestBufp;

plugins/header_rewrite/conditions.h

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -255,9 +255,11 @@ class ConditionHeader : public Condition
255255
using SelfType = ConditionHeader;
256256

257257
public:
258-
explicit ConditionHeader(bool client = false) : _client(client)
258+
enum HeaderType { HEADER, CLIENT, SERVER };
259+
260+
explicit ConditionHeader(HeaderType type = HEADER) : _type(type)
259261
{
260-
Dbg(dbg_ctl, "Calling CTOR for ConditionHeader, client %d", client);
262+
Dbg(dbg_ctl, "Calling CTOR for ConditionHeader, type %d", static_cast<int>(type));
261263
}
262264

263265
// noncopyable
@@ -271,7 +273,7 @@ class ConditionHeader : public Condition
271273
bool eval(const Resources &res) override;
272274

273275
private:
274-
bool _client;
276+
HeaderType _type;
275277
};
276278

277279
// url
@@ -282,7 +284,7 @@ class ConditionUrl : public Condition
282284
using SelfType = ConditionUrl;
283285

284286
public:
285-
enum UrlType { CLIENT, URL, FROM, TO };
287+
enum UrlType { CLIENT, URL, FROM, TO, SERVER };
286288

287289
explicit ConditionUrl(const UrlType type) : _type(type) { Dbg(dbg_ctl, "Calling CTOR for ConditionUrl"); }
288290

plugins/header_rewrite/factory.cc

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -132,9 +132,13 @@ condition_factory(const std::string &cond)
132132
} else if (c_name == "HEADER") { // This condition adapts to the hook
133133
c = new ConditionHeader();
134134
} else if (c_name == "CLIENT-HEADER") {
135-
c = new ConditionHeader(true);
135+
c = new ConditionHeader(ConditionHeader::CLIENT);
136+
} else if (c_name == "SERVER-HEADER") {
137+
c = new ConditionHeader(ConditionHeader::SERVER);
136138
} else if (c_name == "CLIENT-URL") { // This condition adapts to the hook
137139
c = new ConditionUrl(ConditionUrl::CLIENT);
140+
} else if (c_name == "SERVER-URL") {
141+
c = new ConditionUrl(ConditionUrl::SERVER);
138142
} else if (c_name == "URL") {
139143
c = new ConditionUrl(ConditionUrl::URL);
140144
} else if (c_name == "FROM-URL") {

plugins/header_rewrite/resources.cc

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,6 @@ void
3333
Resources::gather(const ResourceIDs ids, TSHttpHookID hook)
3434
{
3535
Dbg(pi_dbg_ctl, "Building resources, hook=%s", TSHttpHookNameLookup(hook));
36-
3736
Dbg(pi_dbg_ctl, "Gathering resources for hook %s with IDs %d", TSHttpHookNameLookup(hook), ids);
3837

3938
// If we need the client request headers, make sure it's also available in the client vars.
@@ -45,6 +44,14 @@ Resources::gather(const ResourceIDs ids, TSHttpHookID hook)
4544
}
4645
}
4746

47+
if (ids & RSRC_SERVER_REQUEST_HEADERS) {
48+
Dbg(pi_dbg_ctl, "\tAdding TXN server request header buffers");
49+
if (TSHttpTxnServerReqGet(state.txnp, &server_bufp, &server_hdr_loc) != TS_SUCCESS) {
50+
Dbg(pi_dbg_ctl, "could not gather bufp/hdr_loc for server request");
51+
// Not a fatal error - server request may not be available in all hooks
52+
}
53+
}
54+
4855
switch (hook) {
4956
case TS_HTTP_READ_RESPONSE_HDR_HOOK:
5057
// Read response headers from server
@@ -172,6 +179,12 @@ Resources::destroy()
172179
}
173180
}
174181

182+
if (server_bufp && (server_bufp != bufp) && (server_bufp != client_bufp)) {
183+
if (server_hdr_loc && (server_hdr_loc != hdr_loc) && (server_hdr_loc != client_hdr_loc)) {
184+
TSHandleMLocRelease(server_bufp, TS_NULL_MLOC, server_hdr_loc);
185+
}
186+
}
187+
175188
#if TS_HAS_CRIPTS
176189
delete client_conn;
177190
delete server_conn;

plugins/header_rewrite/resources.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,8 @@ class Resources
115115
TSMLoc hdr_loc = nullptr;
116116
TSMBuffer client_bufp = nullptr;
117117
TSMLoc client_hdr_loc = nullptr;
118+
TSMBuffer server_bufp = nullptr;
119+
TSMLoc server_hdr_loc = nullptr;
118120
#if TS_HAS_CRIPTS
119121
cripts::Transaction state; // This now holds txpn / ssnp
120122
cripts::Client::Connection *client_conn = nullptr;

tests/gold_tests/pluginTest/header_rewrite/header_rewrite_bundle.replay.yaml

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,12 @@ autest:
145145
args:
146146
- "rules/query_sub_key.conf"
147147

148+
- from: "http://www.example.com/from_14/"
149+
to: "http://backend.ex:{SERVER_HTTP_PORT}/to_14/"
150+
plugins:
151+
- name: "header_rewrite.so"
152+
args:
153+
- "rules/rule_server_conditions.conf"
148154

149155

150156
# Proxy verifier sessions
@@ -1074,3 +1080,30 @@ sessions:
10741080
headers:
10751081
fields:
10761082
- [ X-Query-Sub, { as: absent } ]
1083+
1084+
# Test 30: SERVER-HEADER and SERVER-URL conditions
1085+
- transactions:
1086+
- client-request:
1087+
method: "GET"
1088+
version: "1.1"
1089+
url: /from_14/test
1090+
headers:
1091+
fields:
1092+
- [ Host, www.example.com ]
1093+
- [ uuid, 36 ]
1094+
1095+
server-response:
1096+
status: 200
1097+
reason: OK
1098+
headers:
1099+
fields:
1100+
- [ Connection, close ]
1101+
1102+
proxy-response:
1103+
status: 200
1104+
headers:
1105+
fields:
1106+
- [ X-Server-Path, { value: "to_14/test", as: equal } ]
1107+
- [ X-Marker-Found, { value: "Yes", as: equal } ]
1108+
- [ X-Server-Host-Header, { value: "backend.ex", as: contains } ]
1109+
- [ X-Path-Match, { value: "Yes", as: equal } ]
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
#
2+
# Licensed to the Apache Software Foundation (ASF) under one
3+
# or more contributor license agreements. See the NOTICE file
4+
# distributed with this work for additional information
5+
# regarding copyright ownership. The ASF licenses this file
6+
# to you under the Apache License, Version 2.0 (the
7+
# "License"); you may not use this file except in compliance
8+
# with the License. You may obtain a copy of the License at
9+
#
10+
# http://www.apache.org/licenses/LICENSE-2.0
11+
#
12+
# Unless required by applicable law or agreed to in writing, software
13+
# distributed under the License is distributed on an "AS IS" BASIS,
14+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
# See the License for the specific language governing permissions and
16+
# limitations under the License.
17+
18+
# Test SERVER-HEADER and SERVER-URL conditions
19+
cond %{SEND_REQUEST_HDR_HOOK}
20+
set-header X-Server-Marker "ATS-Processed"
21+
22+
cond %{SEND_RESPONSE_HDR_HOOK}
23+
set-header X-Server-Path "%{SERVER-URL:PATH}"
24+
set-header X-Server-Host-Header "%{SERVER-HEADER:Host}"
25+
26+
cond %{SEND_RESPONSE_HDR_HOOK} [AND]
27+
cond %{SERVER-HEADER:X-Server-Marker} ="ATS-Processed"
28+
set-header X-Marker-Found "Yes"
29+
30+
cond %{SEND_RESPONSE_HDR_HOOK} [AND]
31+
cond %{SERVER-URL:PATH} /^to_14\//
32+
set-header X-Path-Match "Yes"

tools/hrw4u/pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ build-backend = "setuptools.build_meta"
2020

2121
[project]
2222
name = "hrw4u"
23-
version = "1.4.1"
23+
version = "1.4.4"
2424
description = "HRW4U CLI tool for Apache Traffic Server header rewrite rules"
2525
authors = [
2626
{name = "Leif Hedstrom", email = "leif@apache.org"}

0 commit comments

Comments
 (0)