From 25210a90bace5c18bd35f618319d9fd266edd223 Mon Sep 17 00:00:00 2001 From: Brian Neradt Date: Thu, 4 Sep 2025 19:41:23 +0000 Subject: [PATCH] prscs: proxy response status code setter In complex setups where there are multiple plugins that can be setting an error response, it can be non-trival to determine which component intervened for any given transaction. This adds the prscs log field, proxy response status code setter, which provides an identifying string for the component (a plugin name, or ip_allow, for instance) that set the transaction's response status. --- doc/admin-guide/logging/formatting.en.rst | 9 +++ .../api/functions/TSHttpHdrStatusSet.en.rst | 15 +++++ .../api/functions/TSHttpTxnStatusSet.en.rst | 67 +++++++++++++++++++ .../plugins/c-api/basic_auth/basic_auth.cc | 2 +- .../plugins/c-api/denylist_0/denylist_0.cc | 2 +- .../plugins/c-api/denylist_1/denylist_1.cc | 2 +- .../plugins/c-api/redirect_1/redirect_1.cc | 2 +- example/plugins/c-api/remap/remap.cc | 2 +- .../plugins/c-api/secure_link/secure_link.cc | 2 +- include/proxy/http/HttpTransact.h | 3 +- include/proxy/logging/LogAccess.h | 1 + include/ts/ts.h | 33 ++++++++- plugins/authproxy/authproxy.cc | 2 +- plugins/experimental/access_control/plugin.cc | 10 +-- .../experimental/cookie_remap/cookie_remap.cc | 8 +-- plugins/experimental/geoip_acl/geoip_acl.cc | 2 +- plugins/experimental/http_stats/http_stats.cc | 2 +- plugins/experimental/icap/icap_plugin.cc | 2 +- .../experimental/maxmind_acl/maxmind_acl.cc | 2 +- plugins/experimental/mp4/mp4.cc | 2 +- plugins/experimental/rate_limit/rate_limit.cc | 2 +- .../experimental/rate_limit/txn_limiter.cc | 2 +- plugins/experimental/tls_bridge/tls_bridge.cc | 2 +- .../txn_box/plugin/src/ts_util.cc | 4 +- .../experimental/uri_signing/uri_signing.cc | 2 +- plugins/experimental/url_sig/url_sig.cc | 2 +- plugins/experimental/wasm/ats_context.cc | 4 +- plugins/header_rewrite/operators.cc | 10 +-- plugins/lua/ts_lua_client_response.cc | 4 +- plugins/lua/ts_lua_http.cc | 4 +- plugins/lua/ts_lua_server_response.cc | 2 +- .../origin_server_auth/origin_server_auth.cc | 4 +- plugins/prefetch/plugin.cc | 2 +- plugins/regex_remap/regex_remap.cc | 6 +- plugins/remap_purge/remap_purge.cc | 2 +- plugins/statichit/statichit.cc | 4 +- plugins/stats_over_http/stats_over_http.cc | 2 +- src/api/InkAPI.cc | 26 ++++++- src/cripts/Error.cc | 2 +- src/cripts/Headers.cc | 4 +- src/proxy/http/HttpSM.cc | 1 + src/proxy/http/HttpTransact.cc | 6 +- src/proxy/http/remap/UrlRewrite.cc | 12 ++++ src/proxy/logging/Log.cc | 5 ++ src/proxy/logging/LogAccess.cc | 23 +++++++ src/tscpp/api/Transaction.cc | 2 +- tests/gold_tests/ip_allow/gold/log.gold | 6 +- tests/gold_tests/ip_allow/ip_allow.test.py | 14 ++-- .../gold/header_rewrite-502.gold | 5 ++ .../gold/plugin-status-test.gold | 2 + .../header_rewrite/header_rewrite.test.py | 46 +++++++++++-- .../pluginTest/header_rewrite/rules/rule.conf | 6 +- .../polite_hook_wait/polite_hook_wait.cc | 2 +- tests/tools/plugins/custom204plugin.cc | 2 +- tests/tools/plugins/user_args.cc | 2 +- 55 files changed, 317 insertions(+), 77 deletions(-) create mode 100644 doc/developer-guide/api/functions/TSHttpTxnStatusSet.en.rst create mode 100644 tests/gold_tests/pluginTest/header_rewrite/gold/header_rewrite-502.gold create mode 100644 tests/gold_tests/pluginTest/header_rewrite/gold/plugin-status-test.gold diff --git a/doc/admin-guide/logging/formatting.en.rst b/doc/admin-guide/logging/formatting.en.rst index 5a8039c66cd..3df372979fb 100644 --- a/doc/admin-guide/logging/formatting.en.rst +++ b/doc/admin-guide/logging/formatting.en.rst @@ -678,6 +678,7 @@ Status Codes .. _cfsc: .. _csssc: .. _pfsc: +.. _prscs: .. _pssc: .. _sssc: .. _prrp: @@ -699,6 +700,14 @@ pfsc Proxy Request Finish status code specifying whether the proxy (``INTR``), or timed out (``TIMEOUT``). prrp Proxy Response HTTP response reason phrase sent by |TS| proxy to the client. +prscs Proxy Response The identifying label for the entity (such as a plugin + name or component) that last set the HTTP status code + for the transaction. This is set via + :func:`TSHttpTxnStatusSet` with a ``setter`` parameter + or :func:`TSHttpHdrStatusSet` with a ``setter`` + parameter. Shows ``-`` if no setter has been recorded. + ``ip_allow`` will be set if the :file:`ip_allow.yaml` + component denies the request. pssc Proxy Response HTTP response status code sent by |TS| proxy to the client. sssc Origin Response HTTP response status code sent by the origin server diff --git a/doc/developer-guide/api/functions/TSHttpHdrStatusSet.en.rst b/doc/developer-guide/api/functions/TSHttpHdrStatusSet.en.rst index 144ab14f7f7..b7c1a69223b 100644 --- a/doc/developer-guide/api/functions/TSHttpHdrStatusSet.en.rst +++ b/doc/developer-guide/api/functions/TSHttpHdrStatusSet.en.rst @@ -30,5 +30,20 @@ Synopsis .. function:: TSReturnCode TSHttpHdrStatusSet(TSMBuffer bufp, TSMLoc offset, TSHttpStatus status) +.. function:: TSReturnCode TSHttpHdrStatusSet(TSMBuffer bufp, TSMLoc offset, TSHttpStatus status, TSHttpTxn txnp, std::string_view setter) + Description =========== + +Sets the HTTP status code on an existing HTTP header object. An overload also +accepts the transaction and an identifying setter label. When provided, the +setter is recorded on the transaction for logging via the `prscs` log field. + +Parameters +========== + +- bufp: Marshal buffer containing the HTTP header. +- offset: Location of the HTTP header within bufp. +- status: The HTTP status code to set. +- txnp: Optional transaction handle on which to record the setter label. +- setter: Optional label identifying the component setting the status; pass empty to leave unchanged. diff --git a/doc/developer-guide/api/functions/TSHttpTxnStatusSet.en.rst b/doc/developer-guide/api/functions/TSHttpTxnStatusSet.en.rst new file mode 100644 index 00000000000..0e8278bc52f --- /dev/null +++ b/doc/developer-guide/api/functions/TSHttpTxnStatusSet.en.rst @@ -0,0 +1,67 @@ +.. 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 + +TSHttpTxnStatusSet +****************** + +Synopsis +======== + +.. code-block:: cpp + + #include + +.. function:: void TSHttpTxnStatusSet(TSHttpTxn txnp, TSHttpStatus status) + +.. function:: void TSHttpTxnStatusSet(TSHttpTxn txnp, TSHttpStatus status, std::string_view setter) + +Description +=========== + +:func:`TSHttpTxnStatusSet` sets the transaction's internal status state, which triggers +Traffic Server's error handling system. This is typically used for access control, +authentication failures, and early transaction processing. Traffic Server will +automatically generate an appropriate error response body. + +:arg:`txnp` is the associated transaction for the new status. + +:arg:`status` is the HTTP status code to set. + +:arg:`setter` (overload) is an optional identifying label for the entity setting the status +(e.g., plugin name), used for logging purposes. The setter information can be retrieved +using the 'prscs' log field. If empty, does not change the current setter value. +Defaults to empty string. + +This function is commonly used by plugins that need to terminate a transaction early +with an error status. Unlike :func:`TSHttpHdrStatusSet`, this function affects the +transaction state rather than just the HTTP headers. + +The ``setter`` parameter provides a convenient way to track which component set the status +for debugging and logging purposes. + +Return Values +============= + +:func:`TSHttpTxnStatusSet` returns no value. + +See Also +======== + +:manpage:`TSHttpHdrStatusSet(3ts)` diff --git a/example/plugins/c-api/basic_auth/basic_auth.cc b/example/plugins/c-api/basic_auth/basic_auth.cc index db38a202875..8f6263e1c04 100644 --- a/example/plugins/c-api/basic_auth/basic_auth.cc +++ b/example/plugins/c-api/basic_auth/basic_auth.cc @@ -174,7 +174,7 @@ handle_response(TSHttpTxn txnp) goto done; } - TSHttpHdrStatusSet(bufp, hdr_loc, TS_HTTP_STATUS_PROXY_AUTHENTICATION_REQUIRED); + TSHttpHdrStatusSet(bufp, hdr_loc, TS_HTTP_STATUS_PROXY_AUTHENTICATION_REQUIRED, txnp, PLUGIN_NAME); TSHttpHdrReasonSet(bufp, hdr_loc, TSHttpHdrReasonLookup(TS_HTTP_STATUS_PROXY_AUTHENTICATION_REQUIRED), strlen(TSHttpHdrReasonLookup(TS_HTTP_STATUS_PROXY_AUTHENTICATION_REQUIRED))); diff --git a/example/plugins/c-api/denylist_0/denylist_0.cc b/example/plugins/c-api/denylist_0/denylist_0.cc index 2df7d57d977..85180c1aa77 100644 --- a/example/plugins/c-api/denylist_0/denylist_0.cc +++ b/example/plugins/c-api/denylist_0/denylist_0.cc @@ -99,7 +99,7 @@ handle_response(TSHttpTxn txnp) goto done; } - TSHttpHdrStatusSet(bufp, hdr_loc, TS_HTTP_STATUS_FORBIDDEN); + TSHttpHdrStatusSet(bufp, hdr_loc, TS_HTTP_STATUS_FORBIDDEN, txnp, PLUGIN_NAME); TSHttpHdrReasonSet(bufp, hdr_loc, TSHttpHdrReasonLookup(TS_HTTP_STATUS_FORBIDDEN), strlen(TSHttpHdrReasonLookup(TS_HTTP_STATUS_FORBIDDEN))); diff --git a/example/plugins/c-api/denylist_1/denylist_1.cc b/example/plugins/c-api/denylist_1/denylist_1.cc index 1fd8fcd481d..272fa1776d6 100644 --- a/example/plugins/c-api/denylist_1/denylist_1.cc +++ b/example/plugins/c-api/denylist_1/denylist_1.cc @@ -145,7 +145,7 @@ handle_response(TSHttpTxn txnp, TSCont contp ATS_UNUSED) goto done; } - TSHttpHdrStatusSet(bufp, hdr_loc, TS_HTTP_STATUS_FORBIDDEN); + TSHttpHdrStatusSet(bufp, hdr_loc, TS_HTTP_STATUS_FORBIDDEN, txnp, PLUGIN_NAME); TSHttpHdrReasonSet(bufp, hdr_loc, TSHttpHdrReasonLookup(TS_HTTP_STATUS_FORBIDDEN), strlen(TSHttpHdrReasonLookup(TS_HTTP_STATUS_FORBIDDEN))); diff --git a/example/plugins/c-api/redirect_1/redirect_1.cc b/example/plugins/c-api/redirect_1/redirect_1.cc index deea69b9a2b..701446a5661 100644 --- a/example/plugins/c-api/redirect_1/redirect_1.cc +++ b/example/plugins/c-api/redirect_1/redirect_1.cc @@ -191,7 +191,7 @@ handle_response(TSHttpTxn txnp) goto done; } - TSHttpHdrStatusSet(bufp, hdr_loc, TS_HTTP_STATUS_MOVED_PERMANENTLY); + TSHttpHdrStatusSet(bufp, hdr_loc, TS_HTTP_STATUS_MOVED_PERMANENTLY, txnp, PLUGIN_NAME); TSHttpHdrReasonSet(bufp, hdr_loc, TSHttpHdrReasonLookup(TS_HTTP_STATUS_MOVED_PERMANENTLY), strlen(TSHttpHdrReasonLookup(TS_HTTP_STATUS_MOVED_PERMANENTLY))); diff --git a/example/plugins/c-api/remap/remap.cc b/example/plugins/c-api/remap/remap.cc index 55d8d8bc75b..ab51603b621 100644 --- a/example/plugins/c-api/remap/remap.cc +++ b/example/plugins/c-api/remap/remap.cc @@ -278,7 +278,7 @@ TSRemapDoRemap(void *ih, TSHttpTxn rh, TSRemapRequestInfo *rri) len = snprintf(tmp, 255, "This is very small example of TS API usage!\nIteration %d!\nHTTP return code %d\n", my_local_counter, TS_HTTP_STATUS_CONTINUE + my_local_counter); - TSHttpTxnStatusSet(rh, static_cast(static_cast(TS_HTTP_STATUS_CONTINUE) + my_local_counter)); + TSHttpTxnStatusSet(rh, static_cast(static_cast(TS_HTTP_STATUS_CONTINUE) + my_local_counter), PLUGIN_NAME); TSHttpTxnErrorBodySet(rh, tmp, len, nullptr); // Defaults to text/html my_local_counter++; } diff --git a/example/plugins/c-api/secure_link/secure_link.cc b/example/plugins/c-api/secure_link/secure_link.cc index 83b44c08143..50d575bbfb7 100644 --- a/example/plugins/c-api/secure_link/secure_link.cc +++ b/example/plugins/c-api/secure_link/secure_link.cc @@ -144,7 +144,7 @@ TSRemapDoRemap(void *ih, TSHttpTxn rh, TSRemapRequestInfo *rri) } if (sli->strict) { Dbg(dbg_ctl, "request is DENY"); - TSHttpTxnStatusSet(rh, TS_HTTP_STATUS_FORBIDDEN); + TSHttpTxnStatusSet(rh, TS_HTTP_STATUS_FORBIDDEN, PLUGIN_NAME); status = TSREMAP_NO_REMAP; } else { Dbg(dbg_ctl, "request is PASS"); diff --git a/include/proxy/http/HttpTransact.h b/include/proxy/http/HttpTransact.h index 34cb0ae0be8..62a44da904c 100644 --- a/include/proxy/http/HttpTransact.h +++ b/include/proxy/http/HttpTransact.h @@ -662,7 +662,8 @@ class HttpTransact HTTPVersion updated_server_version = HTTP_INVALID; CacheLookupResult_t cache_lookup_result = CacheLookupResult_t::NONE; HTTPStatus http_return_code = HTTPStatus::NONE; - CacheAuth_t www_auth_content = CacheAuth_t::NONE; + std::string http_return_code_setter_name; + CacheAuth_t www_auth_content = CacheAuth_t::NONE; Arena arena; diff --git a/include/proxy/logging/LogAccess.h b/include/proxy/logging/LogAccess.h index 757c9d4a5bc..b71c91ae883 100644 --- a/include/proxy/logging/LogAccess.h +++ b/include/proxy/logging/LogAccess.h @@ -170,6 +170,7 @@ class LogAccess int marshal_proxy_resp_squid_len(char *); // INT int marshal_proxy_resp_content_len(char *); // INT int marshal_proxy_resp_status_code(char *); // INT + int marshal_status_plugin_entry(char *); // STR int marshal_proxy_resp_header_len(char *); // INT int marshal_proxy_finish_status_code(char *); // INT int marshal_cache_result_code(char *); // INT diff --git a/include/ts/ts.h b/include/ts/ts.h index 3bd473fe11a..7bfab149827 100644 --- a/include/ts/ts.h +++ b/include/ts/ts.h @@ -34,6 +34,7 @@ #endif #include +#include #include #include "tsutil/DbgCtl.h" @@ -1142,7 +1143,9 @@ TSReturnCode TSHttpHdrUrlGet(TSMBuffer bufp, TSMLoc offset, TSMLoc *locp); TSReturnCode TSHttpHdrUrlSet(TSMBuffer bufp, TSMLoc offset, TSMLoc url); TSHttpStatus TSHttpHdrStatusGet(TSMBuffer bufp, TSMLoc offset); +/** This is a candidate for deprecation in v10.0.0 in favor of the version that takes the setter. */ TSReturnCode TSHttpHdrStatusSet(TSMBuffer bufp, TSMLoc offset, TSHttpStatus status); +TSReturnCode TSHttpHdrStatusSet(TSMBuffer bufp, TSMLoc offset, TSHttpStatus status, TSHttpTxn txnp, std::string_view setter); const char *TSHttpHdrReasonGet(TSMBuffer bufp, TSMLoc offset, int *length); TSReturnCode TSHttpHdrReasonSet(TSMBuffer bufp, TSMLoc offset, const char *value, int length); const char *TSHttpHdrReasonLookup(TSHttpStatus status); @@ -1637,7 +1640,35 @@ TSReturnCode TSUserArgIndexLookup(TSUserArgType type, int arg_idx, const char ** void TSUserArgSet(void *data, int arg_idx, void *arg); void *TSUserArgGet(void *data, int arg_idx); -void TSHttpTxnStatusSet(TSHttpTxn txnp, TSHttpStatus status); +/** Set the HTTP status code for a transaction. + * + * Sets the transaction's internal status state, triggering Traffic Server's + * error handling system. This is typically used for access control, + * authentication failures, and early transaction processing. Traffic Server + * will automatically generate an appropriate error response body. + * + * @note This is a candidate for deprecation in v10.0.0 in favor of the version + * that takes the setter. + * + * @param[in] txnp The associated transaction for the new status. + * @param[in] status The HTTP status code to set. + */ +void TSHttpTxnStatusSet(TSHttpTxn txnp, TSHttpStatus status); + +/** Set the HTTP status code for a transaction and track the entity that set it. + * + * Sets the transaction's internal status state, triggering Traffic Server's + * error handling system. This is typically used for access control, + * authentication failures, and early transaction processing. Traffic Server + * will automatically generate an appropriate error response body. + * + * @param[in] txnp The associated transaction for the new status. + * @param[in] status The HTTP status code to set. + * @param[in] setter Identifying label for the entity setting the status + * (e.g., plugin name). If empty, clears the current setter information. + */ +void TSHttpTxnStatusSet(TSHttpTxn txnp, TSHttpStatus status, std::string_view setter); + TSHttpStatus TSHttpTxnStatusGet(TSHttpTxn txnp); void TSHttpTxnActiveTimeoutSet(TSHttpTxn txnp, int timeout); diff --git a/plugins/authproxy/authproxy.cc b/plugins/authproxy/authproxy.cc index 6f9138f6901..c5ea8d56eac 100644 --- a/plugins/authproxy/authproxy.cc +++ b/plugins/authproxy/authproxy.cc @@ -611,7 +611,7 @@ StateUnauthorized(AuthRequestContext *auth, void *) { static const char msg[] = "authorization denied\n"; - TSHttpTxnStatusSet(auth->txn, TS_HTTP_STATUS_FORBIDDEN); + TSHttpTxnStatusSet(auth->txn, TS_HTTP_STATUS_FORBIDDEN, "authproxy"); TSHttpTxnErrorBodySet(auth->txn, TSstrdup(msg), sizeof(msg) - 1, TSstrdup("text/plain")); TSHttpTxnReenable(auth->txn, TS_EVENT_HTTP_ERROR); diff --git a/plugins/experimental/access_control/plugin.cc b/plugins/experimental/access_control/plugin.cc index 005cd94fc1a..f5817c6e624 100644 --- a/plugins/experimental/access_control/plugin.cc +++ b/plugins/experimental/access_control/plugin.cc @@ -280,7 +280,7 @@ handleInvalidToken(TSHttpTxn txnp, AccessControlTxnData *data, bool reject, cons { TSRemapStatus resultStatus = TSREMAP_NO_REMAP; if (reject) { - TSHttpTxnStatusSet(txnp, httpStatus); + TSHttpTxnStatusSet(txnp, httpStatus, PLUGIN_NAME); resultStatus = TSREMAP_DID_REMAP; } else { data->_vaState = status; @@ -395,7 +395,7 @@ contHandleAccessControl(const TSCont contp, TSEvent event, void *edata) } else { AccessControlDebug("failed to construct a valid origin access token, did not set-cookie with it"); /* Don't set any cookie, fail the request here returning appropriate status code and body.*/ - TSHttpTxnStatusSet(txnp, config->_invalidOriginResponse); + TSHttpTxnStatusSet(txnp, config->_invalidOriginResponse, PLUGIN_NAME); static const char *body = "Unexpected Response From the Origin Server\n"; size_t bufsize = strlen(body) + 1; char *buf = static_cast(TSmalloc(bufsize)); @@ -602,18 +602,18 @@ TSRemapDoRemap(void *instance, TSHttpTxn txnp, TSRemapRequestInfo *rri) } } } else { - TSHttpTxnStatusSet(txnp, config->_invalidRequest); + TSHttpTxnStatusSet(txnp, config->_invalidRequest, PLUGIN_NAME); AccessControlDebug("https is the only allowed scheme (plugin should be used only with TLS)"); remapStatus = TSREMAP_DID_REMAP; } } else { - TSHttpTxnStatusSet(txnp, config->_internalError); + TSHttpTxnStatusSet(txnp, config->_internalError, PLUGIN_NAME); AccessControlError("failed to get request uri-scheme"); remapStatus = TSREMAP_DID_REMAP; } } else { /* Something is terribly wrong, we cannot get the configuration */ - TSHttpTxnStatusSet(txnp, TS_HTTP_STATUS_INTERNAL_SERVER_ERROR); + TSHttpTxnStatusSet(txnp, TS_HTTP_STATUS_INTERNAL_SERVER_ERROR, PLUGIN_NAME); AccessControlError("configuration unavailable"); remapStatus = TSREMAP_DID_REMAP; } diff --git a/plugins/experimental/cookie_remap/cookie_remap.cc b/plugins/experimental/cookie_remap/cookie_remap.cc index e860466715a..56b43fb8ad4 100644 --- a/plugins/experimental/cookie_remap/cookie_remap.cc +++ b/plugins/experimental/cookie_remap/cookie_remap.cc @@ -1217,18 +1217,18 @@ TSRemapDoRemap(void *ih, TSHttpTxn txnp, TSRemapRequestInfo *rri) // Maybe set the return status if (status > TS_HTTP_STATUS_NONE) { Dbg(dbg_ctl, "Setting return status to %d", status); - TSHttpTxnStatusSet(txnp, status); + TSHttpTxnStatusSet(txnp, status, MY_NAME); if ((status == TS_HTTP_STATUS_MOVED_PERMANENTLY) || (status == TS_HTTP_STATUS_MOVED_TEMPORARILY)) { if (rewrite_to.size() > 8192) { TSError("Redirect in target " "URL too long"); - TSHttpTxnStatusSet(txnp, TS_HTTP_STATUS_REQUEST_URI_TOO_LONG); + TSHttpTxnStatusSet(txnp, TS_HTTP_STATUS_REQUEST_URI_TOO_LONG, MY_NAME); } else { const char *start = rewrite_to.c_str(); int dest_len = rewrite_to.size(); if (TS_PARSE_ERROR == TSUrlParse(rri->requestBufp, rri->requestUrl, &start, start + dest_len)) { - TSHttpTxnStatusSet(txnp, TS_HTTP_STATUS_INTERNAL_SERVER_ERROR); + TSHttpTxnStatusSet(txnp, TS_HTTP_STATUS_INTERNAL_SERVER_ERROR, MY_NAME); TSError("can't parse " "substituted " "URL string"); @@ -1251,7 +1251,7 @@ TSRemapDoRemap(void *ih, TSHttpTxn txnp, TSRemapRequestInfo *rri) // set the new url if (TSUrlParse(rri->requestBufp, rri->requestUrl, &start, start + rewrite_to.length()) == TS_PARSE_ERROR) { - TSHttpTxnStatusSet(txnp, TS_HTTP_STATUS_INTERNAL_SERVER_ERROR); + TSHttpTxnStatusSet(txnp, TS_HTTP_STATUS_INTERNAL_SERVER_ERROR, MY_NAME); TSError("can't parse substituted URL string"); goto error; } else { diff --git a/plugins/experimental/geoip_acl/geoip_acl.cc b/plugins/experimental/geoip_acl/geoip_acl.cc index a1d614f3ed4..0fabc70d6c8 100644 --- a/plugins/experimental/geoip_acl/geoip_acl.cc +++ b/plugins/experimental/geoip_acl/geoip_acl.cc @@ -97,7 +97,7 @@ TSRemapDoRemap(void *ih, TSHttpTxn rh, TSRemapRequestInfo *rri) if (!a->eval(rri, rh)) { Dbg(dbg_ctl, "denying request"); - TSHttpTxnStatusSet(rh, static_cast(403)); + TSHttpTxnStatusSet(rh, static_cast(403), PLUGIN_NAME); a->send_html(rh); } } diff --git a/plugins/experimental/http_stats/http_stats.cc b/plugins/experimental/http_stats/http_stats.cc index bbeefbee9f4..2e67570470d 100644 --- a/plugins/experimental/http_stats/http_stats.cc +++ b/plugins/experimental/http_stats/http_stats.cc @@ -580,7 +580,7 @@ TSRemapDoRemap(void *ih, TSHttpTxn rh, TSRemapRequestInfo * /* rri ATS_UNUSED */ if (!cfg) { VERROR("No remap context available, check code / config"); - TSHttpTxnStatusSet(rh, TS_HTTP_STATUS_INTERNAL_SERVER_ERROR); + TSHttpTxnStatusSet(rh, TS_HTTP_STATUS_INTERNAL_SERVER_ERROR, PLUGIN); return TSREMAP_NO_REMAP; } diff --git a/plugins/experimental/icap/icap_plugin.cc b/plugins/experimental/icap/icap_plugin.cc index fdaa629b52f..f51991ad2e9 100644 --- a/plugins/experimental/icap/icap_plugin.cc +++ b/plugins/experimental/icap/icap_plugin.cc @@ -241,7 +241,7 @@ handle_invalid_icap_behavior(TSCont contp, TransformData *data, const char *msg) TSError("[%s] Couldn't clear client response header", PLUGIN_NAME); return 0; } - TSHttpHdrStatusSet(bufp, hdr_loc, TS_HTTP_STATUS_BAD_GATEWAY); + TSHttpHdrStatusSet(bufp, hdr_loc, TS_HTTP_STATUS_BAD_GATEWAY, data->txn, PLUGIN_NAME); TSHttpHdrReasonSet(bufp, hdr_loc, TSHttpHdrReasonLookup(TS_HTTP_STATUS_BAD_GATEWAY), strlen(TSHttpHdrReasonLookup(TS_HTTP_STATUS_BAD_GATEWAY))); TSHandleMLocRelease(bufp, TS_NULL_MLOC, hdr_loc); diff --git a/plugins/experimental/maxmind_acl/maxmind_acl.cc b/plugins/experimental/maxmind_acl/maxmind_acl.cc index 2bbb24680df..a6c6a26948f 100644 --- a/plugins/experimental/maxmind_acl/maxmind_acl.cc +++ b/plugins/experimental/maxmind_acl/maxmind_acl.cc @@ -70,7 +70,7 @@ TSRemapDoRemap(void *ih, TSHttpTxn rh, TSRemapRequestInfo *rri) Acl *a = static_cast(ih); if (!a->eval(rri, rh)) { Dbg(dbg_ctl, "denying request"); - TSHttpTxnStatusSet(rh, TS_HTTP_STATUS_FORBIDDEN); + TSHttpTxnStatusSet(rh, TS_HTTP_STATUS_FORBIDDEN, PLUGIN_NAME); a->send_html(rh); } } diff --git a/plugins/experimental/mp4/mp4.cc b/plugins/experimental/mp4/mp4.cc index b2baffeb1a6..6bd57e4e53d 100644 --- a/plugins/experimental/mp4/mp4.cc +++ b/plugins/experimental/mp4/mp4.cc @@ -106,7 +106,7 @@ TSRemapDoRemap(void * /* ih ATS_UNUSED */, TSHttpTxn rh, TSRemapRequestInfo *rri return TSREMAP_NO_REMAP; } else if (start < 0) { - TSHttpTxnStatusSet(rh, TS_HTTP_STATUS_BAD_REQUEST); + TSHttpTxnStatusSet(rh, TS_HTTP_STATUS_BAD_REQUEST, DEBUG_TAG); TSHttpTxnErrorBodySet(rh, TSstrdup("Invalid request."), sizeof("Invalid request.") - 1, nullptr); } diff --git a/plugins/experimental/rate_limit/rate_limit.cc b/plugins/experimental/rate_limit/rate_limit.cc index 50765576784..57e4bb20b90 100644 --- a/plugins/experimental/rate_limit/rate_limit.cc +++ b/plugins/experimental/rate_limit/rate_limit.cc @@ -147,7 +147,7 @@ TSRemapDoRemap(void *ih, TSHttpTxn txnp, TSRemapRequestInfo * /* rri ATS_UNUSED case ReserveStatus::HIGH_RATE: if (!limiter->max_queue() || limiter->full()) { // We are running at limit, and the queue has reached max capacity, give back an error and be done. - TSHttpTxnStatusSet(txnp, static_cast(limiter->error())); + TSHttpTxnStatusSet(txnp, static_cast(limiter->error()), PLUGIN_NAME); limiter->setupTxnCont(txnp, TS_HTTP_SEND_RESPONSE_HDR_HOOK); Dbg(dbg_ctl, "Rejecting request, we're at %s and queue is full", status == ReserveStatus::FULL ? "capacity" : "high rate"); } else { diff --git a/plugins/experimental/rate_limit/txn_limiter.cc b/plugins/experimental/rate_limit/txn_limiter.cc index f991df90b58..6eef9de0f56 100644 --- a/plugins/experimental/rate_limit/txn_limiter.cc +++ b/plugins/experimental/rate_limit/txn_limiter.cc @@ -97,7 +97,7 @@ txn_queue_cont(TSCont cont, TSEvent /* event ATS_UNUSED */, void * /* edata ATS_ delayHeader(txnp, limiter->header(), age); Dbg(dbg_ctl, "Queued TXN is too old (%ldms), erroring out", static_cast(age.count())); - TSHttpTxnStatusSet(txnp, static_cast(limiter->error())); + TSHttpTxnStatusSet(txnp, static_cast(limiter->error()), PLUGIN_NAME); TSHttpTxnHookAdd(txnp, TS_HTTP_SEND_RESPONSE_HDR_HOOK, contp); limiter->incrementMetric(RATE_LIMITER_METRIC_EXPIRED); TSHttpTxnReenable(txnp, TS_EVENT_HTTP_ERROR); diff --git a/plugins/experimental/tls_bridge/tls_bridge.cc b/plugins/experimental/tls_bridge/tls_bridge.cc index ff74e71c60b..37dfdc0f36a 100644 --- a/plugins/experimental/tls_bridge/tls_bridge.cc +++ b/plugins/experimental/tls_bridge/tls_bridge.cc @@ -513,7 +513,7 @@ Bridge::update_ua_response() // an actual upstream connection. Otherwise, let the original connection response code // ride. if (_out_response_code != TS_HTTP_STATUS_OK && _out_response_code != TS_HTTP_STATUS_NONE) { - TSHttpHdrStatusSet(mbuf, hdr_loc, _out_response_code); + TSHttpHdrStatusSet(mbuf, hdr_loc, _out_response_code, _ua_txn, PLUGIN_NAME); if (!_out_response_reason.empty()) { TSHttpHdrReasonSet(mbuf, hdr_loc, _out_response_reason.data(), _out_response_reason.size()); } diff --git a/plugins/experimental/txn_box/plugin/src/ts_util.cc b/plugins/experimental/txn_box/plugin/src/ts_util.cc index 097132bc478..81da18c32fc 100644 --- a/plugins/experimental/txn_box/plugin/src/ts_util.cc +++ b/plugins/experimental/txn_box/plugin/src/ts_util.cc @@ -130,7 +130,7 @@ namespace compat status_set(ts::HttpTxn &txn, TSHttpStatus status, swoc::meta::CaseTag<1>) -> decltype(TSHttpTxnStatusSet(txn, eraser(status)), bool()) { - TSHttpTxnStatusSet(txn, eraser(status)); // no error return, sigh. + TSHttpTxnStatusSet(txn, eraser(status), "txn_box"); return true; } @@ -660,7 +660,7 @@ ts::HttpHeader::field_remove(swoc::TextView name) bool ts::HttpResponse::status_set(TSHttpStatus status) const { - return TS_SUCCESS == TSHttpHdrStatusSet(_buff, _loc, status); + return TS_SUCCESS == TSHttpHdrStatusSet(_buff, _loc, status, nullptr, ""); } TextView diff --git a/plugins/experimental/uri_signing/uri_signing.cc b/plugins/experimental/uri_signing/uri_signing.cc index 8e2db033a5d..d57e941b264 100644 --- a/plugins/experimental/uri_signing/uri_signing.cc +++ b/plugins/experimental/uri_signing/uri_signing.cc @@ -324,7 +324,7 @@ TSRemapDoRemap(void *ih, TSHttpTxn txnp, TSRemapRequestInfo *rri) return status; } fail: - TSHttpTxnStatusSet(txnp, TS_HTTP_STATUS_FORBIDDEN); + TSHttpTxnStatusSet(txnp, TS_HTTP_STATUS_FORBIDDEN, PLUGIN_NAME); if (url != nullptr) { PluginDebug("Invalid JWT for %.*s", url_ct, url); TSfree((void *)url); diff --git a/plugins/experimental/url_sig/url_sig.cc b/plugins/experimental/url_sig/url_sig.cc index ec1167c20f0..67891be2df7 100644 --- a/plugins/experimental/url_sig/url_sig.cc +++ b/plugins/experimental/url_sig/url_sig.cc @@ -869,7 +869,7 @@ TSRemapDoRemap(void *ih, TSHttpTxn txnp, TSRemapRequestInfo *rri) break; } /* Always set the return status */ - TSHttpTxnStatusSet(txnp, cfg->err_status); + TSHttpTxnStatusSet(txnp, cfg->err_status, PLUGIN_NAME); return TSREMAP_DID_REMAP; diff --git a/plugins/experimental/wasm/ats_context.cc b/plugins/experimental/wasm/ats_context.cc index 8e8bd2d7d79..01fcf16870f 100644 --- a/plugins/experimental/wasm/ats_context.cc +++ b/plugins/experimental/wasm/ats_context.cc @@ -1412,7 +1412,7 @@ Context::setProperty(std::string_view key, std::string_view serialized_value) if (TSHttpTxnServerRespGet(txnp_, &bufp, &hdr_loc) == TS_SUCCESS) { int64_t *status = reinterpret_cast(const_cast(serialized_value.data())); - TSHttpHdrStatusSet(bufp, hdr_loc, static_cast(*status)); + TSHttpHdrStatusSet(bufp, hdr_loc, static_cast(*status), txnp_, WASM_DEBUG_TAG); TSHandleMLocRelease(bufp, TS_NULL_MLOC, hdr_loc); } return WasmResult::Ok; @@ -1487,7 +1487,7 @@ Context::sendLocalResponse(uint32_t response_code, std::string_view body_text, P TSError("[wasm][%s] Can't send local response without a transaction", __FUNCTION__); return WasmResult::InternalFailure; } else { - TSHttpTxnStatusSet(txnp_, static_cast(response_code)); + TSHttpTxnStatusSet(txnp_, static_cast(response_code), WASM_DEBUG_TAG); if (body_text.size() > 0) { TSHttpTxnErrorBodySet(txnp_, TSstrndup(body_text.data(), body_text.size()), body_text.size(), diff --git a/plugins/header_rewrite/operators.cc b/plugins/header_rewrite/operators.cc index 42c00027c63..4ac0e245b02 100644 --- a/plugins/header_rewrite/operators.cc +++ b/plugins/header_rewrite/operators.cc @@ -199,14 +199,14 @@ OperatorSetStatus::exec(const Resources &res) const case TS_HTTP_READ_RESPONSE_HDR_HOOK: case TS_HTTP_SEND_RESPONSE_HDR_HOOK: if (res.bufp && res.hdr_loc) { - TSHttpHdrStatusSet(res.bufp, res.hdr_loc, static_cast(_status.get_int_value())); + TSHttpHdrStatusSet(res.bufp, res.hdr_loc, static_cast(_status.get_int_value()), res.state.txnp, PLUGIN_NAME); if (_reason && _reason_len > 0) { TSHttpHdrReasonSet(res.bufp, res.hdr_loc, _reason, _reason_len); } } break; default: - TSHttpTxnStatusSet(res.state.txnp, static_cast(_status.get_int_value())); + TSHttpTxnStatusSet(res.state.txnp, static_cast(_status.get_int_value()), PLUGIN_NAME); break; } @@ -591,14 +591,14 @@ OperatorSetRedirect::exec(const Resources &res) const Dbg(pi_dbg_ctl, "Could not set Location field value to: %s", value.c_str()); } // Set the new status. - TSHttpTxnStatusSet(res.state.txnp, static_cast(_status.get_int_value())); + TSHttpTxnStatusSet(res.state.txnp, static_cast(_status.get_int_value()), PLUGIN_NAME); const_cast(res).changed_url = true; res._rri->redirect = 1; } else { Dbg(pi_dbg_ctl, "OperatorSetRedirect::exec() hook=%d", int(get_hook())); // Set the new status code and reason. TSHttpStatus status = static_cast(_status.get_int_value()); - TSHttpHdrStatusSet(res.bufp, res.hdr_loc, status); + TSHttpHdrStatusSet(res.bufp, res.hdr_loc, status, res.state.txnp, PLUGIN_NAME); EditRedirectResponse(res.state.txnp, value, status, res.bufp, res.hdr_loc); } Dbg(pi_dbg_ctl, "OperatorSetRedirect::exec() invoked with destination=%s and status code=%d", value.c_str(), @@ -1382,7 +1382,7 @@ OperatorSetBodyFrom::exec(const Resources &res) const // Forces original status code in event TSHttpTxnErrorBodySet changed // the code or another condition was set conflicting with this one. // Set here because res is the only structure that contains the original status code. - TSHttpTxnStatusSet(res.state.txnp, res.resp_status); + TSHttpTxnStatusSet(res.state.txnp, res.resp_status, PLUGIN_NAME); } else { TSError(PLUGIN_NAME, "OperatorSetBodyFrom:exec:: Could not create request"); return true; diff --git a/plugins/lua/ts_lua_client_response.cc b/plugins/lua/ts_lua_client_response.cc index 8a0d480c640..c1d27d441ef 100644 --- a/plugins/lua/ts_lua_client_response.cc +++ b/plugins/lua/ts_lua_client_response.cc @@ -403,7 +403,7 @@ ts_lua_client_response_set_status(lua_State *L) reason = TSHttpHdrReasonLookup(TSHttpStatus(status)); reason_len = strlen(reason); - TSHttpHdrStatusSet(http_ctx->client_response_bufp, http_ctx->client_response_hdrp, TSHttpStatus(status)); + TSHttpHdrStatusSet(http_ctx->client_response_bufp, http_ctx->client_response_hdrp, TSHttpStatus(status), http_ctx->txnp, "lua"); TSHttpHdrReasonSet(http_ctx->client_response_bufp, http_ctx->client_response_hdrp, reason, reason_len); return 0; @@ -488,7 +488,7 @@ ts_lua_client_response_set_error_resp(lua_State *L) } reason_len = strlen(reason); - TSHttpHdrStatusSet(http_ctx->client_response_bufp, http_ctx->client_response_hdrp, TSHttpStatus(status)); + TSHttpHdrStatusSet(http_ctx->client_response_bufp, http_ctx->client_response_hdrp, TSHttpStatus(status), http_ctx->txnp, "lua"); TSHttpHdrReasonSet(http_ctx->client_response_bufp, http_ctx->client_response_hdrp, reason, reason_len); body_len = 0; diff --git a/plugins/lua/ts_lua_http.cc b/plugins/lua/ts_lua_http.cc index 4326224f782..b631206bb24 100644 --- a/plugins/lua/ts_lua_http.cc +++ b/plugins/lua/ts_lua_http.cc @@ -356,7 +356,7 @@ ts_lua_http_set_retstatus(lua_State *L) GET_HTTP_CONTEXT(http_ctx, L); status = luaL_checkinteger(L, 1); - TSHttpTxnStatusSet(http_ctx->txnp, TSHttpStatus(status)); + TSHttpTxnStatusSet(http_ctx->txnp, TSHttpStatus(status), "lua"); return 0; } @@ -387,7 +387,7 @@ ts_lua_http_set_resp(lua_State *L) n = lua_gettop(L); status = luaL_checkinteger(L, 1); - TSHttpTxnStatusSet(http_ctx->txnp, TSHttpStatus(status)); + TSHttpTxnStatusSet(http_ctx->txnp, TSHttpStatus(status), "lua"); if (n == 2) { body = luaL_checklstring(L, 2, &body_len); diff --git a/plugins/lua/ts_lua_server_response.cc b/plugins/lua/ts_lua_server_response.cc index c6dcaef0b2f..481976a397d 100644 --- a/plugins/lua/ts_lua_server_response.cc +++ b/plugins/lua/ts_lua_server_response.cc @@ -391,7 +391,7 @@ ts_lua_server_response_set_status(lua_State *L) reason_len = strlen(reason); } - TSHttpHdrStatusSet(http_ctx->server_response_bufp, http_ctx->server_response_hdrp, TSHttpStatus(status)); + TSHttpHdrStatusSet(http_ctx->server_response_bufp, http_ctx->server_response_hdrp, TSHttpStatus(status), http_ctx->txnp, "lua"); TSHttpHdrReasonSet(http_ctx->server_response_bufp, http_ctx->server_response_hdrp, reason, reason_len); return 0; diff --git a/plugins/origin_server_auth/origin_server_auth.cc b/plugins/origin_server_auth/origin_server_auth.cc index 12c98ce9dd7..583ca0f785a 100644 --- a/plugins/origin_server_auth/origin_server_auth.cc +++ b/plugins/origin_server_auth/origin_server_auth.cc @@ -1099,7 +1099,7 @@ event_handler(TSCont cont, TSEvent event, void *edata) Dbg(dbg_ctl, "Successfully signed the URL"); } else { Dbg(dbg_ctl, "Failed to sign the URL, status = %d", status); - TSHttpTxnStatusSet(txnp, status); + TSHttpTxnStatusSet(txnp, status, PLUGIN_NAME); enable_event = TS_EVENT_HTTP_ERROR; } break; @@ -1325,7 +1325,7 @@ TSRemapDoRemap(void *ih, TSHttpTxn txnp, TSRemapRequestInfo * /* rri */) } else { Dbg(dbg_ctl, "Remap context is invalid"); TSError("[%s] No remap context available, check code / config", PLUGIN_NAME); - TSHttpTxnStatusSet(txnp, TS_HTTP_STATUS_INTERNAL_SERVER_ERROR); + TSHttpTxnStatusSet(txnp, TS_HTTP_STATUS_INTERNAL_SERVER_ERROR, PLUGIN_NAME); } // This plugin actually doesn't do anything with remapping. Ever. diff --git a/plugins/prefetch/plugin.cc b/plugins/prefetch/plugin.cc index c3b01d76271..b935e7928db 100644 --- a/plugins/prefetch/plugin.cc +++ b/plugins/prefetch/plugin.cc @@ -687,7 +687,7 @@ contHandleFetch(const TSCont contp, TSEvent event, void *edata) if (TS_SUCCESS == TSHttpTxnClientRespGet(txnp, &bufp, &hdrLoc)) { const char *reason = TSHttpHdrReasonLookup(data->_status); int reasonLen = strlen(reason); - TSHttpHdrStatusSet(bufp, hdrLoc, data->_status); + TSHttpHdrStatusSet(bufp, hdrLoc, data->_status, txnp, PLUGIN_NAME); TSHttpHdrReasonSet(bufp, hdrLoc, reason, reasonLen); PrefetchDebug("set response: %d %.*s '%s'", data->_status, reasonLen, reason, data->_body.c_str()); diff --git a/plugins/regex_remap/regex_remap.cc b/plugins/regex_remap/regex_remap.cc index ca838c23ef1..2c9e5db29a2 100644 --- a/plugins/regex_remap/regex_remap.cc +++ b/plugins/regex_remap/regex_remap.cc @@ -1021,12 +1021,12 @@ TSRemapDoRemap(void *ih, TSHttpTxn txnp, TSRemapRequestInfo *rri) re->status_option() != TS_HTTP_STATUS_TEMPORARY_REDIRECT && re->status_option() != TS_HTTP_STATUS_PERMANENT_REDIRECT) { // Don't set the URL / Location for this. - TSHttpTxnStatusSet(txnp, re->status_option()); + TSHttpTxnStatusSet(txnp, re->status_option(), PLUGIN_NAME); break; } Dbg(dbg_ctl, "Redirecting URL, status=%d", re->status_option()); - TSHttpTxnStatusSet(txnp, re->status_option()); + TSHttpTxnStatusSet(txnp, re->status_option(), PLUGIN_NAME); rri->redirect = 1; } @@ -1036,7 +1036,7 @@ TSRemapDoRemap(void *ih, TSHttpTxn txnp, TSRemapRequestInfo *rri) // Setup the new URL if (TS_PARSE_ERROR == TSUrlParse(rri->requestBufp, rri->requestUrl, &start, start + dest_len)) { - TSHttpTxnStatusSet(txnp, TS_HTTP_STATUS_INTERNAL_SERVER_ERROR); + TSHttpTxnStatusSet(txnp, TS_HTTP_STATUS_INTERNAL_SERVER_ERROR, PLUGIN_NAME); TSError("[%s] can't parse substituted URL string", PLUGIN_NAME); } } diff --git a/plugins/remap_purge/remap_purge.cc b/plugins/remap_purge/remap_purge.cc index 26f606dcc06..fd0b8198fbe 100644 --- a/plugins/remap_purge/remap_purge.cc +++ b/plugins/remap_purge/remap_purge.cc @@ -143,7 +143,7 @@ on_send_response_header(TSHttpTxn txnp, TSCont contp, PurgeInstance *purge) char response[1024]; int len = snprintf(response, sizeof(response), "PURGED %s\r\n\r\n", purge->id); - TSHttpHdrStatusSet(bufp, hdr_loc, TS_HTTP_STATUS_OK); + TSHttpHdrStatusSet(bufp, hdr_loc, TS_HTTP_STATUS_OK, txnp, PLUGIN_NAME); TSHttpHdrReasonSet(bufp, hdr_loc, "OK", 2); TSHttpTxnErrorBodySet(txnp, TSstrdup(response), len >= (int)sizeof(response) ? (int)sizeof(response) - 1 : len, nullptr); diff --git a/plugins/statichit/statichit.cc b/plugins/statichit/statichit.cc index 37f240ca764..66d5a9eb640 100644 --- a/plugins/statichit/statichit.cc +++ b/plugins/statichit/statichit.cc @@ -628,7 +628,7 @@ TSRemapDoRemap(void *ih, TSHttpTxn rh, TSRemapRequestInfo *rri) if (!cfg) { VERROR("No remap context available, check code / config"); - TSHttpTxnStatusSet(rh, TS_HTTP_STATUS_INTERNAL_SERVER_ERROR); + TSHttpTxnStatusSet(rh, TS_HTTP_STATUS_INTERNAL_SERVER_ERROR, PLUGIN); return TSREMAP_NO_REMAP; } @@ -638,7 +638,7 @@ TSRemapDoRemap(void *ih, TSHttpTxn rh, TSRemapRequestInfo *rri) TSUrlPathGet(rri->requestBufp, rri->requestUrl, &pathsz); if (pathsz > 0) { VDEBUG("Path is not an exact match. Rejecting!"); - TSHttpTxnStatusSet(rh, TS_HTTP_STATUS_NOT_FOUND); + TSHttpTxnStatusSet(rh, TS_HTTP_STATUS_NOT_FOUND, PLUGIN); return TSREMAP_NO_REMAP; } } diff --git a/plugins/stats_over_http/stats_over_http.cc b/plugins/stats_over_http/stats_over_http.cc index b2953885ca6..dbcd776ce9f 100644 --- a/plugins/stats_over_http/stats_over_http.cc +++ b/plugins/stats_over_http/stats_over_http.cc @@ -760,7 +760,7 @@ stats_origin(TSCont contp, TSEvent /* event ATS_UNUSED */, void *edata) if (auto addr = TSHttpTxnClientAddrGet(txnp); !is_ipmap_allowed(config, addr)) { Dbg(dbg_ctl, "not right ip"); - TSHttpTxnStatusSet(txnp, TS_HTTP_STATUS_FORBIDDEN); + TSHttpTxnStatusSet(txnp, TS_HTTP_STATUS_FORBIDDEN, PLUGIN_NAME); reenable = TS_EVENT_HTTP_ERROR; goto notforme; } diff --git a/src/api/InkAPI.cc b/src/api/InkAPI.cc index a500d847ff0..6c6f34b37e9 100644 --- a/src/api/InkAPI.cc +++ b/src/api/InkAPI.cc @@ -2929,7 +2929,7 @@ TSHttpHdrStatusGet(TSMBuffer bufp, TSMLoc obj) } TSReturnCode -TSHttpHdrStatusSet(TSMBuffer bufp, TSMLoc obj, TSHttpStatus status) +TSHttpHdrStatusSet(TSMBuffer bufp, TSMLoc obj, TSHttpStatus status, TSHttpTxn txnp, std::string_view setter) { // Allow to modify the buffer only // if bufp is modifiable. If bufp is not modifiable return @@ -2947,9 +2947,21 @@ TSHttpHdrStatusSet(TSMBuffer bufp, TSMLoc obj, TSHttpStatus status) SET_HTTP_HDR(h, bufp, obj); ink_assert(static_cast(h.m_http->m_type) == HdrHeapObjType::HTTP_HEADER); h.status_set(static_cast(status)); + + if (txnp != nullptr && !setter.empty()) { + sdk_assert(sdk_sanity_check_txn(txnp) == TS_SUCCESS); + HttpSM *sm = reinterpret_cast(txnp); + sm->t_state.http_return_code_setter_name = setter; + } return TS_SUCCESS; } +TSReturnCode +TSHttpHdrStatusSet(TSMBuffer bufp, TSMLoc obj, TSHttpStatus status) +{ + return TSHttpHdrStatusSet(bufp, obj, status, nullptr, std::string_view{}); +} + const char * TSHttpHdrReasonGet(TSMBuffer bufp, TSMLoc obj, int *length) { @@ -5242,12 +5254,22 @@ TSUserArgGet(void *data, int arg_idx) } void -TSHttpTxnStatusSet(TSHttpTxn txnp, TSHttpStatus status) +TSHttpTxnStatusSet(TSHttpTxn txnp, TSHttpStatus status, std::string_view setter) { sdk_assert(sdk_sanity_check_txn(txnp) == TS_SUCCESS); HttpSM *sm = reinterpret_cast(txnp); sm->t_state.http_return_code = static_cast(status); + + if (!setter.empty()) { + sm->t_state.http_return_code_setter_name = setter; + } +} + +void +TSHttpTxnStatusSet(TSHttpTxn txnp, TSHttpStatus status) +{ + TSHttpTxnStatusSet(txnp, status, std::string_view{}); } TSHttpStatus diff --git a/src/cripts/Error.cc b/src/cripts/Error.cc index 606c05cbbd2..63935a52063 100644 --- a/src/cripts/Error.cc +++ b/src/cripts/Error.cc @@ -26,7 +26,7 @@ void Error::Execute(cripts::Context *context) { if (Failed()) { - TSHttpTxnStatusSet(context->state.txnp, _status._getter()); + TSHttpTxnStatusSet(context->state.txnp, _status._getter(), "cripts"); // ToDo: So we can't set the reason phrase here, because ATS doesn't have that // as a transaction API, only on the response header... } diff --git a/src/cripts/Headers.cc b/src/cripts/Headers.cc index 1a648391f13..fb668908f21 100644 --- a/src/cripts/Headers.cc +++ b/src/cripts/Headers.cc @@ -52,10 +52,10 @@ Header::Status::operator=(int status) case TS_HTTP_SEND_RESPONSE_HDR_HOOK: case TS_HTTP_READ_RESPONSE_HDR_HOOK: case TS_HTTP_TXN_CLOSE_HOOK: - TSHttpHdrStatusSet(_owner->_bufp, _owner->_hdr_loc, _status); + TSHttpHdrStatusSet(_owner->_bufp, _owner->_hdr_loc, _status, _owner->_state->txnp, "cripts"); break; default: - TSHttpTxnStatusSet(_owner->_state->txnp, _status); + TSHttpTxnStatusSet(_owner->_state->txnp, _status, "cripts"); break; } diff --git a/src/proxy/http/HttpSM.cc b/src/proxy/http/HttpSM.cc index 994aaef6a7b..2978adfbb26 100644 --- a/src/proxy/http/HttpSM.cc +++ b/src/proxy/http/HttpSM.cc @@ -5317,6 +5317,7 @@ HttpSM::ip_allow_deny_request(const IpAllow::ACL &acl) method.data(), ntop_formatted); } + t_state.http_return_code_setter_name = "ip_allow"; t_state.current.retry_attempts.maximize( t_state.configured_connect_attempts_max_retries()); // prevent any more retries with this IP call_transact_and_set_next_state(HttpTransact::Forbidden); diff --git a/src/proxy/http/HttpTransact.cc b/src/proxy/http/HttpTransact.cc index 0fef997b91c..df4727cd465 100644 --- a/src/proxy/http/HttpTransact.cc +++ b/src/proxy/http/HttpTransact.cc @@ -906,6 +906,7 @@ HttpTransact::Forbidden(State *s) { TxnDbg(dbg_ctl_http_trans, "IpAllow marked request forbidden"); bootstrap_state_variables_from_request(s, &s->hdr_info.client_request); + s->http_return_code_setter_name = "ip_allow"; build_error_response(s, HTTPStatus::FORBIDDEN, "Access Denied", "access#denied"); TRANSACT_RETURN(StateMachineAction_t::SEND_ERROR_CACHE_NOOP, nullptr); } @@ -1109,6 +1110,7 @@ HttpTransact::EndRemapRequest(State *s) // We must close this connection if client_connection_enabled == false // ///////////////////////////////////////////////////////////////////////// if (!s->client_connection_allowed) { + s->http_return_code_setter_name = "ip_allow"; build_error_response(s, HTTPStatus::FORBIDDEN, "Access Denied", "access#denied"); s->reverse_proxy = false; goto done; @@ -6509,6 +6511,7 @@ HttpTransact::process_quick_http_filter(State *s, int method) { // connection already disabled by previous ACL filtering, don't modify it. if (!s->client_connection_allowed) { + s->http_return_code_setter_name = "ip_allow"; return; } @@ -6547,7 +6550,8 @@ HttpTransact::process_quick_http_filter(State *s, int method) TxnDbg(dbg_ctl_ip_allow, "Line %d denial for '%.*s' from %s", acl.source_line(), static_cast(method_str.length()), method_str.data(), ats_ip_ntop(&s->client_info.src_addr.sa, ipb, sizeof(ipb))); } - s->client_connection_allowed = false; + s->http_return_code_setter_name = "ip_allow"; + s->client_connection_allowed = false; } } } diff --git a/src/proxy/http/remap/UrlRewrite.cc b/src/proxy/http/remap/UrlRewrite.cc index c3b0375dde5..9f71e183ca1 100644 --- a/src/proxy/http/remap/UrlRewrite.cc +++ b/src/proxy/http/remap/UrlRewrite.cc @@ -573,12 +573,18 @@ UrlRewrite::PerformACLFiltering(HttpTransact::State *s, const url_mapping *const if (match && s->client_connection_allowed) { // make sure that a previous filter did not DENY Dbg(dbg_ctl_url_rewrite, "matched ACL filter rule, %s request", rp->allow_flag ? "allowing" : "denying"); s->client_connection_allowed = rp->allow_flag ? true : false; + if (!s->client_connection_allowed) { + s->http_return_code_setter_name = "ip_allow"; + } } else { if (!s->client_connection_allowed) { Dbg(dbg_ctl_url_rewrite, "Previous ACL filter rule denied request, continuing to deny it"); } else { Dbg(dbg_ctl_url_rewrite, "did NOT match ACL filter rule, %s request", rp->allow_flag ? "denying" : "allowing"); s->client_connection_allowed = rp->allow_flag ? false : true; + if (!s->client_connection_allowed) { + s->http_return_code_setter_name = "ip_allow"; + } } } } else if (ip_matches) { @@ -587,6 +593,9 @@ UrlRewrite::PerformACLFiltering(HttpTransact::State *s, const url_mapping *const // Did they specify allowing the listed methods, or denying them? Dbg(dbg_ctl_url_rewrite, "matched ACL filter rule, %s request", rp->allow_flag ? "allowing" : "denying"); s->client_connection_allowed = rp->allow_flag; + if (!s->client_connection_allowed) { + s->http_return_code_setter_name = "ip_allow"; + } // Since both the IP and method match, this rule will be applied regardless of ACLMatchingPolicy and no need to process // other filters nor ip_allow.yaml rules @@ -605,6 +614,9 @@ UrlRewrite::PerformACLFiltering(HttpTransact::State *s, const url_mapping *const Dbg(dbg_ctl_url_rewrite, "ACL rule matched on IP but not on method, action: %s, %s the request", rp->get_action_description(), (rp->allow_flag ? "denying" : "allowing")); s->client_connection_allowed = !rp->allow_flag; + if (!s->client_connection_allowed) { + s->http_return_code_setter_name = "ip_allow"; + } // Since IP match and configured policy is MATCH_ON_IP_ONLY, no need to process other filters nor ip_allow.yaml rules. s->skip_ip_allow_yaml = true; diff --git a/src/proxy/logging/Log.cc b/src/proxy/logging/Log.cc index 5178883b5d5..ad9ffd00ad8 100644 --- a/src/proxy/logging/Log.cc +++ b/src/proxy/logging/Log.cc @@ -639,6 +639,11 @@ Log::init_fields() global_field_list.add(field, false); field_symbol_hash.emplace("pssc", field); + field = new LogField("proxy_response_status_code_setter", "prscs", LogField::STRING, &LogAccess::marshal_status_plugin_entry, + &LogAccess::unmarshal_str); + global_field_list.add(field, false); + field_symbol_hash.emplace("prscs", field); + field = new LogField("proxy_resp_header_len", "pshl", LogField::sINT, &LogAccess::marshal_proxy_resp_header_len, &LogAccess::unmarshal_int_to_str); global_field_list.add(field, false); diff --git a/src/proxy/logging/LogAccess.cc b/src/proxy/logging/LogAccess.cc index 41ed00ae770..9ef27d49608 100644 --- a/src/proxy/logging/LogAccess.cc +++ b/src/proxy/logging/LogAccess.cc @@ -2401,6 +2401,29 @@ LogAccess::marshal_proxy_resp_status_code(char *buf) return INK_MIN_ALIGN; } +/*------------------------------------------------------------------------- + -------------------------------------------------------------------------*/ + +int +LogAccess::marshal_status_plugin_entry(char *buf) +{ + char const *str = nullptr; + int len = INK_MIN_ALIGN; + + if (m_http_sm) { + std::string const &tag = m_http_sm->t_state.http_return_code_setter_name; + if (!tag.empty()) { + str = tag.c_str(); + len = LogAccess::padded_strlen(str); + } + } + + if (buf) { + marshal_str(buf, str, len); + } + return len; +} + /*------------------------------------------------------------------------- -------------------------------------------------------------------------*/ diff --git a/src/tscpp/api/Transaction.cc b/src/tscpp/api/Transaction.cc index b194b46ef5c..b9ef6d67e4e 100644 --- a/src/tscpp/api/Transaction.cc +++ b/src/tscpp/api/Transaction.cc @@ -199,7 +199,7 @@ void Transaction::setStatusCode(HttpStatus code) { LOG_DEBUG("Transaction tshttptxn=%p setting status code: %d", state_->txn_, code); - TSHttpTxnStatusSet(state_->txn_, static_cast(code)); + TSHttpTxnStatusSet(state_->txn_, static_cast(code), "tscpp"); } bool diff --git a/tests/gold_tests/ip_allow/gold/log.gold b/tests/gold_tests/ip_allow/gold/log.gold index 4508f83174f..e4bc2108b8a 100644 --- a/tests/gold_tests/ip_allow/gold/log.gold +++ b/tests/gold_tests/ip_allow/gold/log.gold @@ -1,3 +1,3 @@ -scheme=http 127.0.0.1 TCP_MISS/200 130 GET https://127.0.0.1:SOMEPORT/get DIRECT - - - 127.0.0.1:SOMEPORT - sftover=- sftmat=- sftcls=- sftbadclf=- yra=- -scheme=UNKNOWN 127.0.0.1 ERR_PROXY_DENIED/403 453 CONNECT 127.0.0.1:SOMEPORT/connect DIRECT text/html - - 127.0.0.1:SOMEPORT - sftover=- sftmat=- sftcls=- sftbadclf=- yra=- -scheme=https 127.0.0.1 ERR_PROXY_DENIED/403 453 PUSH https://127.0.0.1:SOMEPORT/h2_push DIRECT text/html - - 127.0.0.1:SOMEPORT - sftover=- sftmat=- sftcls=- sftbadclf=- yra=- +scheme=http 127.0.0.1 TCP_MISS/200 130 GET https://127.0.0.1:SOMEPORT/get DIRECT - - - 127.0.0.1:SOMEPORT - sftover=- sftmat=- sftcls=- sftbadclf=- yra=- status_setter=- +scheme=UNKNOWN 127.0.0.1 ERR_PROXY_DENIED/403 453 CONNECT 127.0.0.1:SOMEPORT/connect DIRECT text/html - - 127.0.0.1:SOMEPORT - sftover=- sftmat=- sftcls=- sftbadclf=- yra=- status_setter=ip_allow +scheme=https 127.0.0.1 ERR_PROXY_DENIED/403 453 PUSH https://127.0.0.1:SOMEPORT/h2_push DIRECT text/html - - 127.0.0.1:SOMEPORT - sftover=- sftmat=- sftcls=- sftbadclf=- yra=- status_setter=ip_allow diff --git a/tests/gold_tests/ip_allow/ip_allow.test.py b/tests/gold_tests/ip_allow/ip_allow.test.py index 7f8669dc8fb..0454a4e373f 100644 --- a/tests/gold_tests/ip_allow/ip_allow.test.py +++ b/tests/gold_tests/ip_allow/ip_allow.test.py @@ -85,7 +85,7 @@ ts.Disk.records_config.update( { 'proxy.config.diags.debug.enabled': 1, - 'proxy.config.diags.debug.tags': 'ip_allow', + 'proxy.config.diags.debug.tags': 'ip_allow|http|url_rewrite', 'proxy.config.http.push_method_enabled': 1, 'proxy.config.http.connect_ports': '{0}'.format(server.Variables.SSL_Port), 'proxy.config.ssl.server.cert.path': '{0}'.format(ts.Variables.SSLDir), @@ -101,17 +101,17 @@ '%<{Y-YPCS}pqh> %<{Host}cqh> %<{CHAD}pqh> ' 'sftover=%<{x-safet-overlimit-rules}cqh> sftmat=%<{x-safet-matched-rules}cqh> ' 'sftcls=%<{x-safet-classification}cqh> ' - 'sftbadclf=%<{x-safet-bad-classifiers}cqh> yra=%<{Y-RA}cqh>') + 'sftbadclf=%<{x-safet-bad-classifiers}cqh> yra=%<{Y-RA}cqh> status_setter=%') ts.Disk.logging_yaml.AddLines( - ''' logging: + f''' logging: formats: - name: custom - format: '{}' + format: '{format_string}' logs: - filename: squid.log format: custom -'''.format(format_string).split("\n")) +'''.split("\n")) ts.Disk.remap_config.AddLine('map / https://127.0.0.1:{0}'.format(server.Variables.SSL_Port)) @@ -173,10 +173,10 @@ tr.StillRunningAfter = server tr = Test.AddTestRun('Await and verify the transaction log file') +squid_log = os.path.join(ts.Variables.LOGDIR, 'squid.log') tr.Processes.Default.Command = ( os.path.join(Test.Variables.AtsTestToolsDir, 'stdout_wait') + ' 60 "{} {}" {}'.format( - os.path.join(Test.TestDirectory, 'run_sed.sh'), os.path.join(ts.Variables.LOGDIR, 'squid.log'), - os.path.join(Test.TestDirectory, 'gold/log.gold'))) + os.path.join(Test.TestDirectory, 'run_sed.sh'), squid_log, os.path.join(Test.TestDirectory, 'gold/log.gold'))) tr.Processes.Default.ReturnCode = 0 IP_ALLOW_CONFIG_ALLOW_ALL = '''ip_allow: diff --git a/tests/gold_tests/pluginTest/header_rewrite/gold/header_rewrite-502.gold b/tests/gold_tests/pluginTest/header_rewrite/gold/header_rewrite-502.gold new file mode 100644 index 00000000000..27ca4f0d608 --- /dev/null +++ b/tests/gold_tests/pluginTest/header_rewrite/gold/header_rewrite-502.gold @@ -0,0 +1,5 @@ +`` +> GET ``503`` +`` +< ``502`` +`` diff --git a/tests/gold_tests/pluginTest/header_rewrite/gold/plugin-status-test.gold b/tests/gold_tests/pluginTest/header_rewrite/gold/plugin-status-test.gold new file mode 100644 index 00000000000..a888a518e29 --- /dev/null +++ b/tests/gold_tests/pluginTest/header_rewrite/gold/plugin-status-test.gold @@ -0,0 +1,2 @@ +GET - 303 header_rewrite +GET 503 502 header_rewrite diff --git a/tests/gold_tests/pluginTest/header_rewrite/header_rewrite.test.py b/tests/gold_tests/pluginTest/header_rewrite/header_rewrite.test.py index db5babf3da2..b8c6477ca09 100644 --- a/tests/gold_tests/pluginTest/header_rewrite/header_rewrite.test.py +++ b/tests/gold_tests/pluginTest/header_rewrite/header_rewrite.test.py @@ -16,6 +16,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +import os + Test.Summary = ''' Test a basic remap of a http connection ''' @@ -27,16 +29,22 @@ Test.testName = "" request_header = {"headers": "GET / HTTP/1.1\r\nHost: www.example.com\r\n\r\n", "timestamp": "1469733493.993", "body": ""} -# expected response from the origin server response_header = {"headers": "HTTP/1.1 200 OK\r\nConnection: close\r\n\r\n", "timestamp": "1469733493.993", "body": ""} +server.addResponse("sessionfile.log", request_header, response_header) -# add response to the server dictionary +request_header = {"headers": "GET /503 HTTP/1.1\r\nHost: www.example.com\r\n\r\n", "timestamp": "1469733493.993", "body": ""} +response_header = { + "headers": "HTTP/1.1 503 Service Unavailable\r\nConnection: close\r\n\r\n", + "timestamp": "1469733493.993", + "body": "" +} server.addResponse("sessionfile.log", request_header, response_header) + ts.Disk.records_config.update( { 'proxy.config.diags.debug.enabled': 1, 'proxy.config.diags.show_location': 0, - 'proxy.config.diags.debug.tags': 'header.*', + 'proxy.config.diags.debug.tags': 'header|http', }) # The following rule changes the status code returned from origin server to 303 ts.Setup.CopyAs('rules/rule.conf', Test.RunDirectory) @@ -44,8 +52,23 @@ ts.Disk.remap_config.AddLine('map http://www.example.com http://127.0.0.1:{0}'.format(server.Variables.Port)) ts.Disk.remap_config.AddLine('map http://www.example.com:8080 http://127.0.0.1:{0}'.format(server.Variables.Port)) +# Add logging configuration to test the new plugin tag field +ts.Disk.logging_yaml.AddLines( + ''' +logging: + formats: + - name: plugin_status + format: '% % % %' + logs: + - filename: plugin-status-test + format: plugin_status +'''.split("\n")) + +plugin_status_log = os.path.join(ts.Variables.LOGDIR, 'plugin-status-test.log') +ts.Disk.File(plugin_status_log, exists=True, content='gold/plugin-status-test.gold') + # call localhost straight -tr = Test.AddTestRun() +tr = Test.AddTestRun("Header Rewrite 200 to 303") tr.MakeCurlCommand( '--proxy 127.0.0.1:{0} "http://www.example.com" -H "Proxy-Connection: keep-alive" --verbose'.format(ts.Variables.port), ts=ts) tr.Processes.Default.ReturnCode = 0 @@ -54,4 +77,19 @@ tr.Processes.Default.Streams.stderr = "gold/header_rewrite-303.gold" tr.StillRunningAfter = server +tr = Test.AddTestRun("Header Rewrite 503 to 502") +tr.MakeCurlCommand( + '--proxy 127.0.0.1:{0} "http://www.example.com/503" -H "Proxy-Connection: keep-alive" --verbose'.format(ts.Variables.port), + ts=ts) +tr.Processes.Default.ReturnCode = 0 +tr.Processes.Default.Streams.stderr = "gold/header_rewrite-502.gold" +tr.StillRunningAfter = server + ts.Disk.traffic_out.Content = "gold/header_rewrite-tag.gold" + +# Verify the plugin status log file contains the expected plugin tag + +# Wait for log file to appear, then wait one extra second to make sure TS is done writing it. +tr = Test.AddTestRun() +tr.Processes.Default.Command = (os.path.join(Test.Variables.AtsTestToolsDir, 'condwait') + f' 60 1 -f {plugin_status_log}') +tr.Processes.Default.ReturnCode = 0 diff --git a/tests/gold_tests/pluginTest/header_rewrite/rules/rule.conf b/tests/gold_tests/pluginTest/header_rewrite/rules/rule.conf index 551373b411c..982ef6a4fa9 100644 --- a/tests/gold_tests/pluginTest/header_rewrite/rules/rule.conf +++ b/tests/gold_tests/pluginTest/header_rewrite/rules/rule.conf @@ -16,4 +16,8 @@ # limitations under the License. cond %{STATUS} =200 -set-status 303 + set-status 303 +elif + cond %{STATUS} =503 + set-status 502 + diff --git a/tests/gold_tests/pluginTest/polite_hook_wait/polite_hook_wait.cc b/tests/gold_tests/pluginTest/polite_hook_wait/polite_hook_wait.cc index 8473ada745d..92b86245d60 100644 --- a/tests/gold_tests/pluginTest/polite_hook_wait/polite_hook_wait.cc +++ b/tests/gold_tests/pluginTest/polite_hook_wait/polite_hook_wait.cc @@ -231,7 +231,7 @@ Blocking_action::_txn_cont_func(TSCont, TSEvent event, void *eventData) Blocking_action &ba = AuxDataMgr::data(txn); if (!ba.txn_valid) { - TSHttpTxnStatusSet(txn, TS_HTTP_STATUS_FORBIDDEN); + TSHttpTxnStatusSet(txn, TS_HTTP_STATUS_FORBIDDEN, PINAME); } TSHttpTxnReenable(txn, ba.txn_valid ? TS_EVENT_HTTP_CONTINUE : TS_EVENT_HTTP_ERROR); diff --git a/tests/tools/plugins/custom204plugin.cc b/tests/tools/plugins/custom204plugin.cc index 63d628ceb58..b73886a77cc 100644 --- a/tests/tools/plugins/custom204plugin.cc +++ b/tests/tools/plugins/custom204plugin.cc @@ -97,7 +97,7 @@ local_handler(TSCont contp, TSEvent event, void *edata) break; case TS_EVENT_HTTP_SEND_RESPONSE_HDR: Dbg(dbg_ctl, "Returning 204 with custom response body."); - TSHttpTxnStatusSet(txnp, TS_HTTP_STATUS_NO_CONTENT); + TSHttpTxnStatusSet(txnp, TS_HTTP_STATUS_NO_CONTENT, PLUGIN_NAME); TSHttpTxnErrorBodySet(txnp, TSstrdup(msg), strlen(msg), TSstrdup("text/html")); break; diff --git a/tests/tools/plugins/user_args.cc b/tests/tools/plugins/user_args.cc index 88a9c21c9d6..93a3f47ad53 100644 --- a/tests/tools/plugins/user_args.cc +++ b/tests/tools/plugins/user_args.cc @@ -179,7 +179,7 @@ TSRemapDoRemap(void *ih, TSHttpTxn txnp, TSRemapRequestInfo * /* rri ATS_UNUSED ArgIndexes *ix = static_cast(ih); TSHttpTxnHookAdd(txnp, TS_HTTP_SEND_RESPONSE_HDR_HOOK, ix->contp); - TSHttpTxnStatusSet(txnp, TS_HTTP_STATUS_MOVED_TEMPORARILY); + TSHttpTxnStatusSet(txnp, TS_HTTP_STATUS_MOVED_TEMPORARILY, PLUGIN_NAME); return TSREMAP_DID_REMAP; }