-
Notifications
You must be signed in to change notification settings - Fork 780
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add set-custom-body config item to header_rewrite #11472
base: master
Are you sure you want to change the base?
Conversation
(cherry picked from commit 5bd999953b2605898f15714453250cb7e5e403f9) (cherry picked from commit 08c614ef0089b175a5b6cee205b748416efb87cd)
* Update response body to exclude headers * Update tests to check both response with headers and response body only * Update header_rewrite_custom_body.test.py * Fix tests to check headers and body (cherry picked from commit cb552b63d6755a554edc5b67721f35678b38163b) (cherry picked from commit e12f4798d0d46bd90c810f8f3a7a067ea3b2c76f)
(cherry picked from commit 964b12cc21a9a7c3f9d3fd50286e4cd2d2757bcc)
Remove whitespaces doc formatting fix Doc formatting fix
1b7bf2f
to
fbb0537
Compare
The AuTest prefetch_overflow failed.
The AuTest prefetch_bignum failed:
|
plugins/header_rewrite/operators.cc
Outdated
const unsigned int MAX_SIZE = 256; | ||
const int LOCAL_PORT = 8080; | ||
|
||
static int |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It doesn't need to be static. Putting it in an anonymous namespace makes it file scope.
TSContDestroy(cont); | ||
TSHttpTxnReenable(http_txn, TS_EVENT_HTTP_CONTINUE); | ||
} break; | ||
case TS_EVENT_HTTP_SEND_RESPONSE_HDR: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What's the purpose of executing the continuation on this hook (since nothing is done)?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This pauses the original transaction to allow time to get the response from the second endpoint. The second endpoint will reenable the first once it has completed. It is written in the code for readability
self.runTraffic() | ||
|
||
|
||
HeaderRewriteSetBodyFromTest().run() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could you add another test run where the fetch of the body URL fails?
The AuTest transaction_data_sink failed.
|
tests/gold_tests/pluginTest/header_rewrite/header_rewrite_set_body_from.test.py
Outdated
Show resolved
Hide resolved
tests/gold_tests/pluginTest/header_rewrite/header_rewrite_set_body_from.test.py
Outdated
Show resolved
Hide resolved
tests/gold_tests/pluginTest/header_rewrite/header_rewrite_set_body_from.test.py
Outdated
Show resolved
Hide resolved
tests/gold_tests/pluginTest/header_rewrite/header_rewrite_set_body_from.test.py
Outdated
Show resolved
Hide resolved
event_ids.failure_event_id = TS_EVENT_FETCHSM_FAILURE; | ||
event_ids.timeout_event_id = TS_EVENT_FETCHSM_TIMEOUT; | ||
|
||
struct sockaddr_in addr; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Include <sys/socket.h>
?
void | ||
OperatorSetBodyFrom::initialize_hooks() | ||
{ | ||
add_allowed_hook(TS_HTTP_READ_RESPONSE_HDR_HOOK); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I notice that your test scenario is using header_rewrite as a remap plugin. Would this operator ever be useful when header_rewrite is used as a global plugin?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes - this works with both remap and plugin configs. I have just added some tests to check this aswell.
cond %{CLIENT-URL:PATH} = "200" | ||
set-body-from http://www.example.com/404.html | ||
|
||
cond %{READ_RESPONSE_HDR_HOOK} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does the set-body-from operator require this hook condition in order to work properly? If so, maybe you should mention that in the documentation.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It does not. I am using the path to differentiate the tests.
map http://www.example.com/plugin_fail http://127.0.0.1:{0}/plugin_fail | ||
map http://www.example.com/404.html http://127.0.0.1:{0}/404.html | ||
map http://www.example.com/502 http://127.0.0.1:{0}/502 | ||
""".format(self.server.Variables.Port, Test.RunDirectory)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I suggest you also have a remap for a body URL like this:
Test.GetTcpPort("bad_port")
self.ts.Disk.remap_config.AddLine(f'map http://www.example.com/plugin_no_server http://127.0.0.1::{Test.Variables.bad_port}/plugin_no_server")
in order to verify proper behavior if it's not possible to connect to the server that provided the body that's set by the operator.
|
||
cond %{READ_RESPONSE_HDR_HOOK} | ||
cond %{CLIENT-URL:PATH} = "remap_fail" | ||
set-body-from http://www.example.com/fail |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So, in all the test cases, we still do the original request to upstream, then entirely throw it away. Wouldn't more realistic test cases be with conditions checking response status or response headers?
If the intent is to always discard the original response, wouldn't it be better for the operator to execute in the remap pseudo hook, and set an error status there, to block the original request?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
statichit is an example of a plugin that blocks the request to upstream by setting an error status in the DoRemap funciton:
trafficserver/plugins/statichit/statichit.cc
Line 639 in 0d004b3
TSHttpTxnStatusSet(rh, TS_HTTP_STATUS_NOT_FOUND); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have written the tests to replicate what happens in production. Do you have an example of another test that only checks the response status?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If the tests reflect how you would use this operator in production, why wouldn't you want to avoid the initial request to the upstream server, when the data it returns will always be entirely discarded?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The status is set from that initial request.
plugins/header_rewrite/operators.cc
Outdated
// The transaction is reenabled with the FetchSM transaction | ||
break; | ||
default: | ||
TSError("Warning: handleFetchEvents got unknown event: %d", event); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So in diags.log this will look like:
ERROR: Warning: ...
huh???
TSCont fetchCont = TSContCreate(handleFetchEvents, TSMutexCreate()); | ||
TSContDataSet(fetchCont, static_cast<void *>(res.txnp)); | ||
|
||
TSHttpTxnHookAdd(res.txnp, TS_HTTP_SEND_RESPONSE_HDR_HOOK, fetchCont); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Suppose there is another continuation for the transaction, that is either after the continuation calling this exec func on the READ_RESPONSE_HDR_HOOK, or before fetchCont on the SEND_RESPONSE_HDR_HOOK And, suppose this continuation, like fetchEvent for the SEND_RESPONSE_HDR evert, does not call TxnReenable(). Seems like there could be a race condition, where the URL fetch body done event reenables for that other continuation by mistake.
Could you add a return value to the operator exec funtions, which would control whether the continuation running the exec functions called TxnReenable()? That way, no other continuations of transaction hooks would be run until the fetch of the body URL finished.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I see what you are saying but I dont see how the exec func returning something would help. I think a return value would only have impact on header_rewrite rather than all plugins. This would also make changes to all of header_rewrite.
Adding config item set-custom-body to header rewrite.
The config takes in a url. When triggered, there will be a secondary call the the specified endpoint to get a response body from. What the client will see is the response body from the second call.
The second call goes through as an internal ATS request and goes gets remapped.
The status of the original transaction will remain in all error status code cases. OK status codes will appear as a 500 error code due to the nature of TSHttpTxnErrorBodySet.
e.g.
set_custom_body.conf
remap.config
With the above configs, when /home returns a 404 status code, a second call will be made to /404 through ATS internal request
The response that is sent to the client is