This is an automated email from the ASF dual-hosted git repository.
bneradt pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/trafficserver.git
The following commit(s) were added to refs/heads/master by this push:
new 060721f3da Propagate PROXY-Protocol src to outbound surfaces (#13120)
060721f3da is described below
commit 060721f3daa4e5da79ff649729f59c2fbcea426d
Author: Brian Neradt <[email protected]>
AuthorDate: Tue Apr 28 14:30:12 2026 -0500
Propagate PROXY-Protocol src to outbound surfaces (#13120)
On listeners that receive PROXY-Protocol, the parsed source IP currently
does not reach several origin-facing and operator-visible surfaces: the
outbound `Client-ip` and `X-Forwarded-For` request headers always carry
the immediate TCP peer (the CDN edge), and the outbound TPROXY
local-bind address, the HostDB parent-selection affinity hash, and the
Slow-Request error log all key off the TCP peer even on listeners that
have opted into `:pp-clnt`. The `ssl_has_proxy_v1` debug line is also
misleading: it fires for both PPv1 and PPv2 and only logs the
destination.
This aligns those outbound surfaces with the PROXY-Protocol source.
`HttpTransact::add_client_ip_to_outgoing_request` is updated to
prefer `pp_info.src_addr` whenever the user-agent connection has a
parsed PROXY-Protocol header, mirroring
`add_forwarded_field_to_request`, so the legacy headers agree with
`Forwarded: for=` regardless of `:pp-clnt`. The outbound TPROXY
local-bind, HostDB parent-selection affinity, and Slow-Request error
log in `HttpSM.cc` are migrated to `t_state.effective_client_addr`,
which is `:pp-clnt`-gated by construction so listeners without that
flag are unaffected. The PP debug line is rewritten to print the
actual PP version together with both `src` and `dst`.
This extends the proxy_protocol autest with a PPv2-over-TLS session
using a custom `src-addr` to lock in the new outbound-header
behavior, and updates the admin-guide PROXY-Protocol page to clarify
which surfaces are `:pp-clnt`-gated and which (`Client-ip`,
`X-Forwarded-For`, `Forwarded: for=`) are unconditional.
---
.../configuration/proxy-protocol.en.rst | 30 ++++++++++
doc/admin-guide/files/records.yaml.en.rst | 11 ++++
.../api/functions/TSHttpSsnClientAddrGet.en.rst | 64 +++++++++++++++++++++
.../api/functions/TSHttpTxnClientAddrGet.en.rst | 64 +++++++++++++++++++++
.../api/functions/TSNetVConnClientAddrGet.en.rst | 66 ++++++++++++++++++++++
src/iocore/net/SSLNetVConnection.cc | 10 +++-
src/proxy/http/HttpSM.cc | 17 ++++--
src/proxy/http/HttpTransact.cc | 15 ++++-
.../gold_tests/proxy_protocol/gold/access-cp.gold | 1 +
.../proxy_protocol/gold/access-nocp.gold | 1 +
.../proxy_protocol/proxy_protocol.test.py | 2 +
.../replay/proxy_protocol_in.replay.yaml | 46 +++++++++++++++
12 files changed, 318 insertions(+), 9 deletions(-)
diff --git a/doc/admin-guide/configuration/proxy-protocol.en.rst
b/doc/admin-guide/configuration/proxy-protocol.en.rst
index 4263d9e771..d7fca8ac51 100644
--- a/doc/admin-guide/configuration/proxy-protocol.en.rst
+++ b/doc/admin-guide/configuration/proxy-protocol.en.rst
@@ -17,6 +17,8 @@
.. include:: ../../common.defs
+.. default-domain:: cpp
+
.. _proxy-protocol:
Proxy Protocol
@@ -59,6 +61,27 @@ enable PROXY protocol and want to apply ACL against the IP
address delivered by
If you specify the server_ports flag `pp-clnt` then the client IP address used
for the
transaction will be the one provided by proxy protocol.
+The ``pp-clnt`` flag governs whether the operator-visible "client IP" is the
+PROXY-Protocol source address rather than the immediate TCP peer for the
+following surfaces:
+
+* The squid log field ``%<chi>`` (and its derivatives ``%<chp>`` /
+ ``%<chh>``).
+* :func:`TSHttpTxnClientAddrGet`, :func:`TSHttpSsnClientAddrGet`, and
+ :func:`TSNetVConnClientAddrGet` (the plugin-visible client address).
+* SNI ACL evaluation, the HTTP/2 ``PEER`` ACL, SSL diagnostics, and
+ client-certificate validation.
+* Outbound transparency: when binding the proxy's outbound socket to the
+ client's address (``addr_binding = FOREIGN_ADDR``), |TS| binds to the
+ PROXY-Protocol source address only when ``pp-clnt`` is in effect.
+* HostDB parent-selection affinity: the consistent-hash key that groups
+ requests onto the same upstream peer is the PROXY-Protocol source only
+ when ``pp-clnt`` is in effect; otherwise it is the immediate TCP peer.
+
+The new log field ``%<rchi>`` (and its derivatives ``%<rchp>`` /
+``%<rchh>``) always reports the immediate TCP peer regardless of
+``pp-clnt``.
+
1. HTTP Forwarded Header
The client IP address in the PROXY protocol header is passed to the origin
server via an HTTP `Forwarded:
@@ -67,6 +90,13 @@ Detection of the PROXY protocol header is automatic. If the
PROXY header
precludes the request, it will automatically be parse and made available to the
Forwarded: request header sent to the origin server.
+The legacy outbound headers ``Client-ip:``
+(:ts:cv:`proxy.config.http.insert_client_ip`) and ``X-Forwarded-For:``
+(:ts:cv:`proxy.config.http.insert_squid_x_forwarded_for`) likewise carry
+the PROXY-Protocol source address whenever a PROXY-Protocol header is
+present, mirroring the ``Forwarded: for=`` parameter. This behavior is
+independent of the ``pp-clnt`` listener flag.
+
2. Outbound PROXY protocol
See :ts:cv:`proxy.config.http.proxy_protocol_out` for configuration
information.
diff --git a/doc/admin-guide/files/records.yaml.en.rst
b/doc/admin-guide/files/records.yaml.en.rst
index 30b8d5c255..b319c15b1b 100644
--- a/doc/admin-guide/files/records.yaml.en.rst
+++ b/doc/admin-guide/files/records.yaml.en.rst
@@ -17,6 +17,8 @@
.. include:: ../../common.defs
+.. default-domain:: cpp
+
.. configfile:: records.yaml
records.yaml
@@ -844,6 +846,15 @@ pp
:ref:`Proxy Protocol <proxy-protocol>` for more details on how to configure
this option properly.
+pp-clnt
+ Use the source address from the Proxy Protocol header as the client IP
+ address for the transaction. This affects which address is reported by the
+ ``%<chi>`` log field, by the plugin client-address APIs (such as
+ :func:`TSHttpTxnClientAddrGet`), and by SNI / HTTP/2 peer ACLs and SSL
+ client-certificate validation, among other surfaces. Only meaningful in
+ combination with ``pp``. See :ref:`Proxy Protocol <proxy-protocol>` for the
+ full enumeration of behaviors gated by this flag.
+
tr-full
Fully transparent. This is a convenience option and is identical to
specifying both ``tr-in`` and ``tr-out``.
diff --git a/doc/developer-guide/api/functions/TSHttpSsnClientAddrGet.en.rst
b/doc/developer-guide/api/functions/TSHttpSsnClientAddrGet.en.rst
new file mode 100644
index 0000000000..8008f11b0a
--- /dev/null
+++ b/doc/developer-guide/api/functions/TSHttpSsnClientAddrGet.en.rst
@@ -0,0 +1,64 @@
+.. Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed
+ with this work for additional information regarding copyright
+ ownership. The ASF licenses this file to you under the Apache
+ License, Version 2.0 (the "License"); you may not use this file
+ except in compliance with the License. You may obtain a copy of
+ the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied. See the License for the specific language governing
+ permissions and limitations under the License.
+
+
+.. include:: ../../../common.defs
+
+.. default-domain:: cpp
+
+TSHttpSsnClientAddrGet
+**********************
+
+Synopsis
+========
+
+.. code-block:: cpp
+
+ #include <ts/ts.h>
+
+.. function:: struct sockaddr const * TSHttpSsnClientAddrGet(TSHttpSsn ssnp)
+
+Description
+===========
+
+Return the socket address of the client for the HTTP session :arg:`ssnp`.
+The returned pointer references storage owned by |TS| and is only valid
+for the duration of the current callback; plugins that need to keep the
+value across callbacks must copy it into their own storage.
+
+This is the session-level counterpart of :func:`TSHttpTxnClientAddrGet`
+and is appropriate when the caller has a session handle but no specific
+transaction (for example, in session-level hooks).
+
+If the listener that accepted the connection has the ``pp-clnt`` flag set
+and a PROXY Protocol header was successfully parsed, the returned address
+is the PROXY-Protocol source address rather than the immediate TCP peer.
+Without ``pp-clnt`` the returned address is the immediate TCP peer even
+when PROXY Protocol is enabled. See :ref:`Proxy Protocol <proxy-protocol>`
+for the full enumeration of surfaces gated by ``pp-clnt``.
+
+Return Value
+============
+
+A pointer to the client address, or ``nullptr`` if :arg:`ssnp` is invalid
+or no client address is available.
+
+See Also
+========
+
+:manpage:`TSAPI(3ts)`,
+:func:`TSHttpTxnClientAddrGet`,
+:func:`TSNetVConnClientAddrGet`
diff --git a/doc/developer-guide/api/functions/TSHttpTxnClientAddrGet.en.rst
b/doc/developer-guide/api/functions/TSHttpTxnClientAddrGet.en.rst
new file mode 100644
index 0000000000..c3866bf04d
--- /dev/null
+++ b/doc/developer-guide/api/functions/TSHttpTxnClientAddrGet.en.rst
@@ -0,0 +1,64 @@
+.. Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed
+ with this work for additional information regarding copyright
+ ownership. The ASF licenses this file to you under the Apache
+ License, Version 2.0 (the "License"); you may not use this file
+ except in compliance with the License. You may obtain a copy of
+ the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied. See the License for the specific language governing
+ permissions and limitations under the License.
+
+
+.. include:: ../../../common.defs
+
+.. default-domain:: cpp
+
+TSHttpTxnClientAddrGet
+**********************
+
+Synopsis
+========
+
+.. code-block:: cpp
+
+ #include <ts/ts.h>
+
+.. function:: struct sockaddr const * TSHttpTxnClientAddrGet(TSHttpTxn txnp)
+
+Description
+===========
+
+Return the socket address of the client that initiated the transaction
+:arg:`txnp`. The returned pointer references storage owned by |TS| and is
+only valid for the duration of the current callback; plugins that need to
+keep the value across callbacks must copy it into their own storage.
+
+The returned ``struct sockaddr`` is address-family agnostic. Inspect the
+``sa_family`` field (or use the ``ats_ip_*`` helpers) to dispatch on IPv4
+versus IPv6.
+
+If the listener that accepted the connection has the ``pp-clnt`` flag set
+and a PROXY Protocol header was successfully parsed, the returned address
+is the PROXY-Protocol source address rather than the immediate TCP peer.
+Without ``pp-clnt`` the returned address is the immediate TCP peer even
+when PROXY Protocol is enabled. See :ref:`Proxy Protocol <proxy-protocol>`
+for the full enumeration of surfaces gated by ``pp-clnt``.
+
+Return Value
+============
+
+A pointer to the client address, or ``nullptr`` if :arg:`txnp` is invalid
+or no client address is available.
+
+See Also
+========
+
+:manpage:`TSAPI(3ts)`,
+:func:`TSHttpSsnClientAddrGet`,
+:func:`TSNetVConnClientAddrGet`
diff --git a/doc/developer-guide/api/functions/TSNetVConnClientAddrGet.en.rst
b/doc/developer-guide/api/functions/TSNetVConnClientAddrGet.en.rst
new file mode 100644
index 0000000000..0c69232ddc
--- /dev/null
+++ b/doc/developer-guide/api/functions/TSNetVConnClientAddrGet.en.rst
@@ -0,0 +1,66 @@
+.. Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed
+ with this work for additional information regarding copyright
+ ownership. The ASF licenses this file to you under the Apache
+ License, Version 2.0 (the "License"); you may not use this file
+ except in compliance with the License. You may obtain a copy of
+ the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied. See the License for the specific language governing
+ permissions and limitations under the License.
+
+
+.. include:: ../../../common.defs
+
+.. default-domain:: cpp
+
+TSNetVConnClientAddrGet
+***********************
+
+Synopsis
+========
+
+.. code-block:: cpp
+
+ #include <ts/ts.h>
+
+.. function:: struct sockaddr const * TSNetVConnClientAddrGet(TSVConn vc)
+
+Description
+===========
+
+Return the socket address of the client for the network virtual connection
+:arg:`vc`. The returned pointer references storage owned by |TS| and is
+only valid for the duration of the current callback; plugins that need to
+keep the value across callbacks must copy it into their own storage.
+
+This is the VConn-level counterpart of :func:`TSHttpTxnClientAddrGet` and
+is appropriate in low-level hooks (for example, SSL or TCP hooks) where
+no HTTP session or transaction is yet associated with the connection.
+
+If the listener that accepted the connection has the ``pp-clnt`` flag set
+and a PROXY Protocol header was successfully parsed, the returned address
+is the PROXY-Protocol source address rather than the immediate TCP peer.
+Without ``pp-clnt`` the returned address is the immediate TCP peer even
+when PROXY Protocol is enabled. Use ``TSNetVConnRemoteAddrGet()`` if the
+immediate TCP peer is always required regardless of ``pp-clnt``. See
+:ref:`Proxy Protocol <proxy-protocol>` for the full enumeration of
+surfaces gated by ``pp-clnt``.
+
+Return Value
+============
+
+A pointer to the client address, or ``nullptr`` if :arg:`vc` is invalid
+or no client address is available.
+
+See Also
+========
+
+:manpage:`TSAPI(3ts)`,
+:func:`TSHttpTxnClientAddrGet`,
+:func:`TSHttpSsnClientAddrGet`
diff --git a/src/iocore/net/SSLNetVConnection.cc
b/src/iocore/net/SSLNetVConnection.cc
index 8b599e5e4a..ab5eb9d32b 100644
--- a/src/iocore/net/SSLNetVConnection.cc
+++ b/src/iocore/net/SSLNetVConnection.cc
@@ -376,11 +376,15 @@ SSLNetVConnection::read_raw_data()
if (this->has_proxy_protocol(buffer, &r)) {
Dbg(dbg_ctl_proxyprotocol, "ssl has proxy protocol header");
if (dbg_ctl_proxyprotocol.on()) {
+ IpEndpoint src;
+ src.sa = *(this->get_proxy_protocol_src_addr());
IpEndpoint dst;
dst.sa = *(this->get_proxy_protocol_dst_addr());
- ip_port_text_buffer ipb1;
- ats_ip_nptop(&dst, ipb1, sizeof(ipb1));
- DbgPrint(dbg_ctl_proxyprotocol, "ssl_has_proxy_v1, dest IP received
[%s]", ipb1);
+ ip_port_text_buffer src_ipb, dst_ipb;
+ ats_ip_nptop(&src, src_ipb, sizeof(src_ipb));
+ ats_ip_nptop(&dst, dst_ipb, sizeof(dst_ipb));
+ DbgPrint(dbg_ctl_proxyprotocol, "ssl proxy protocol v%d header
parsed: src=[%s] dst=[%s]",
+ static_cast<int>(this->get_proxy_protocol_version()),
src_ipb, dst_ipb);
}
} else {
Dbg(dbg_ctl_proxyprotocol, "proxy protocol was enabled, but Proxy
Protocol header was not present");
diff --git a/src/proxy/http/HttpSM.cc b/src/proxy/http/HttpSM.cc
index c8a6b128dc..524a140574 100644
--- a/src/proxy/http/HttpSM.cc
+++ b/src/proxy/http/HttpSM.cc
@@ -2411,7 +2411,10 @@ HttpSM::process_hostdb_info(HostDBRecord *record)
}
if (record && !record->is_failed()) {
- t_state.dns_info.inbound_remote_addr = &t_state.client_info.src_addr.sa;
+ // HostDB parent-selection hashes by inbound_remote_addr; using
+ // effective_client_addr keeps sharding affinity tied to the real client
+ // when :pp-clnt is in effect, and is the TCP peer otherwise.
+ t_state.dns_info.inbound_remote_addr = &t_state.effective_client_addr.sa;
if (!use_client_addr) {
t_state.dns_info.set_active(
record->select_best_http(ts_clock::now(),
t_state.txn_conf->down_server_timeout, t_state.dns_info.inbound_remote_addr));
@@ -5759,7 +5762,11 @@ HttpSM::do_http_server_open(bool raw, bool only_direct)
opt.local_ip = outbound_ip;
} else if (_ua.get_txn()->is_outbound_transparent()) {
opt.addr_binding = NetVCOptions::FOREIGN_ADDR;
- opt.local_ip = t_state.client_info.src_addr;
+ // Use effective_client_addr so that listeners with :pp-clnt bind the
+ // outbound socket to the real client IP rather than the immediate TCP
+ // peer; without :pp-clnt, effective_client_addr is the TCP peer, so
+ // existing transparent deployments are unaffected.
+ opt.local_ip = t_state.effective_client_addr;
/* If the connection is server side transparent, we can bind to the
port that the client chose instead of randomly assigning one at
the proxy. This is controlled by the 'use_client_source_port'
@@ -7869,7 +7876,9 @@ HttpSM::update_stats()
}
int status = static_cast<int>(status_code);
char client_ip[INET6_ADDRSTRLEN];
- ats_ip_ntop(&t_state.client_info.src_addr, client_ip, sizeof(client_ip));
+ // Log the operator-visible client IP (matches %<chi>) so the slow-request
+ // line is consistent with squid logs when :pp-clnt is in effect.
+ ats_ip_ntop(&t_state.effective_client_addr, client_ip, sizeof(client_ip));
Error("[%" PRId64 "] Slow Request: "
"client_ip: %s:%u "
"protocol: %s "
@@ -7902,7 +7911,7 @@ HttpSM::update_stats()
"sm_finish: %.3f "
"plugin_active: %.3f "
"plugin_total: %.3f",
- sm_id, client_ip, t_state.client_info.src_addr.host_order_port(),
+ sm_id, client_ip, t_state.effective_client_addr.host_order_port(),
_ua.get_txn() ? _ua.get_txn()->get_protocol_string() : "-1",
url_string, status, unique_id_string, redirection_tries,
client_response_body_bytes, fd, t_state.client_info.state,
t_state.server_info.state,
milestones.difference_sec(TS_MILESTONE_TLS_HANDSHAKE_START,
TS_MILESTONE_TLS_HANDSHAKE_END),
diff --git a/src/proxy/http/HttpTransact.cc b/src/proxy/http/HttpTransact.cc
index dba7105f3c..27df97059e 100644
--- a/src/proxy/http/HttpTransact.cc
+++ b/src/proxy/http/HttpTransact.cc
@@ -5496,12 +5496,23 @@ HttpTransact::add_client_ip_to_outgoing_request(State
*s, HTTPHdr *request)
char ip_string[INET6_ADDRSTRLEN + 1] = {'\0'};
size_t ip_string_size = 0;
- if (!ats_is_ip(&s->client_info.src_addr.sa)) {
+ // Prefer the PROXY-Protocol source address when one is present, so that
+ // Client-ip and X-Forwarded-For agree with Forwarded: for= regardless of
+ // whether the listener carries the :pp-clnt flag.
+ IpEndpoint src_addr = s->client_info.src_addr;
+ if (s->state_machine->get_ua_txn() &&
s->state_machine->get_ua_txn()->get_netvc()) {
+ const ProxyProtocol &pp =
s->state_machine->get_ua_txn()->get_netvc()->get_proxy_protocol_info();
+ if (pp.version != ProxyProtocolVersion::UNDEFINED) {
+ src_addr = pp.src_addr;
+ }
+ }
+
+ if (!ats_is_ip(&src_addr.sa)) {
return;
}
// Always prepare the IP string.
- if (ats_ip_ntop(&s->client_info.src_addr.sa, ip_string, sizeof(ip_string))
!= nullptr) {
+ if (ats_ip_ntop(&src_addr.sa, ip_string, sizeof(ip_string)) != nullptr) {
ip_string_size += strlen(ip_string);
} else {
// Failure, omg
diff --git a/tests/gold_tests/proxy_protocol/gold/access-cp.gold
b/tests/gold_tests/proxy_protocol/gold/access-cp.gold
index b84f06e4d2..28d355ac6a 100644
--- a/tests/gold_tests/proxy_protocol/gold/access-cp.gold
+++ b/tests/gold_tests/proxy_protocol/gold/access-cp.gold
@@ -5,3 +5,4 @@
198.51.100.1 198.51.100.1 127.0.0.1
127.0.0.1 0 127.0.0.1
127.0.0.1 0 127.0.0.1
+198.51.100.1 198.51.100.1 127.0.0.1
diff --git a/tests/gold_tests/proxy_protocol/gold/access-nocp.gold
b/tests/gold_tests/proxy_protocol/gold/access-nocp.gold
index 7d705c4735..5511d71605 100644
--- a/tests/gold_tests/proxy_protocol/gold/access-nocp.gold
+++ b/tests/gold_tests/proxy_protocol/gold/access-nocp.gold
@@ -5,3 +5,4 @@
127.0.0.1 198.51.100.1 127.0.0.1
127.0.0.1 0 127.0.0.1
127.0.0.1 0 127.0.0.1
+127.0.0.1 198.51.100.1 127.0.0.1
diff --git a/tests/gold_tests/proxy_protocol/proxy_protocol.test.py
b/tests/gold_tests/proxy_protocol/proxy_protocol.test.py
index 4af04153a4..e2abc9f713 100644
--- a/tests/gold_tests/proxy_protocol/proxy_protocol.test.py
+++ b/tests/gold_tests/proxy_protocol/proxy_protocol.test.py
@@ -58,6 +58,8 @@ ssl_multicert:
{
"proxy.config.http.proxy_protocol_allowlist": "127.0.0.1",
"proxy.config.http.insert_forwarded": "for|by=ip|proto",
+ "proxy.config.http.insert_client_ip": 2,
+ "proxy.config.http.insert_squid_x_forwarded_for": 1,
"proxy.config.ssl.server.cert.path":
f"{self.ts.Variables.SSLDir}",
"proxy.config.ssl.server.private_key.path":
f"{self.ts.Variables.SSLDir}",
"proxy.config.diags.debug.enabled": 1,
diff --git
a/tests/gold_tests/proxy_protocol/replay/proxy_protocol_in.replay.yaml
b/tests/gold_tests/proxy_protocol/replay/proxy_protocol_in.replay.yaml
index b8b85a2c4a..180e749660 100644
--- a/tests/gold_tests/proxy_protocol/replay/proxy_protocol_in.replay.yaml
+++ b/tests/gold_tests/proxy_protocol/replay/proxy_protocol_in.replay.yaml
@@ -229,3 +229,49 @@ sessions:
proxy-response:
status: 200
+
+ # Test 6: Incoming PROXY Protocol v2 on SSL port, with arbitrary source and
+ # destination address in PROXY message. Verifies that Client-ip and
+ # X-Forwarded-For (the legacy outbound header writers) carry the PROXY
+ # Protocol source address, in addition to the Forwarded: for= field. This
+ # exercises HttpTransact::add_client_ip_to_outgoing_request together with
+ # HttpTransactHeaders::add_forwarded_field_to_request.
+ - protocol:
+ - name: http
+ version: 1
+ - name: tls
+ - name: proxy-protocol
+ version: 2
+ src-addr: 198.51.100.1:51137
+ dst-addr: 198.51.100.2:443
+ - name: tcp
+ - name: ip
+
+ transactions:
+ - client-request:
+ method: GET
+ version: "1.1"
+ url: /get
+ headers:
+ fields:
+ - [uuid, 8]
+ - [Host, "127.0.0.1:443"]
+
+ proxy-request:
+ headers:
+ fields:
+ - [
+ Forwarded,
+ {
+ value: for=198.51.100.1;by=127.0.0.1;proto=https,
+ as: equal,
+ },
+ ]
+ - [X-Forwarded-For, { value: 198.51.100.1, as: equal }]
+ - [Client-ip, { value: 198.51.100.1, as: equal }]
+
+ server-response:
+ status: 200
+
+ proxy-response:
+ status: 200