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; }