-
Notifications
You must be signed in to change notification settings - Fork 847
Add set-body-from config item to header_rewrite #11472
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
Changes from all commits
87ec9ac
d6a4410
49c65f0
fbb0537
2e7f7b3
11b095a
e71943a
dd1ceec
2285524
0f6e7a0
ead8e5f
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -30,6 +30,91 @@ | |
| #include "operators.h" | ||
| #include "ts/apidefs.h" | ||
|
|
||
| namespace | ||
| { | ||
| const unsigned int LOCAL_IP_ADDRESS = 0x0100007f; | ||
| const unsigned int MAX_SIZE = 256; | ||
| const int LOCAL_PORT = 8080; | ||
|
|
||
| int | ||
| handleFetchEvents(TSCont cont, TSEvent event, void *edata) | ||
| { | ||
| TSHttpTxn http_txn = static_cast<TSHttpTxn>(TSContDataGet(cont)); | ||
|
|
||
| switch (static_cast<int>(event)) { | ||
| case OperatorSetBodyFrom::TS_EVENT_FETCHSM_SUCCESS: { | ||
| TSHttpTxn fetchsm_txn = static_cast<TSHttpTxn>(edata); | ||
| int data_len; | ||
| const char *data_start = TSFetchRespGet(fetchsm_txn, &data_len); | ||
| if (data_start && (data_len > 0)) { | ||
| const char *data_end = data_start + data_len; | ||
| TSHttpParser parser = TSHttpParserCreate(); | ||
| TSMBuffer hdr_buf = TSMBufferCreate(); | ||
| TSMLoc hdr_loc = TSHttpHdrCreate(hdr_buf); | ||
| TSHttpHdrTypeSet(hdr_buf, hdr_loc, TS_HTTP_TYPE_RESPONSE); | ||
| if (TSHttpHdrParseResp(parser, hdr_buf, hdr_loc, &data_start, data_end) == TS_PARSE_DONE) { | ||
| TSHttpTxnErrorBodySet(http_txn, TSstrdup(data_start), (data_end - data_start), nullptr); | ||
| } else { | ||
| TSWarning("[%s] Unable to parse set-custom-body fetch response", __FUNCTION__); | ||
| } | ||
| TSHttpParserDestroy(parser); | ||
| TSHandleMLocRelease(hdr_buf, nullptr, hdr_loc); | ||
| TSMBufferDestroy(hdr_buf); | ||
| } else { | ||
| TSWarning("[%s] Successful set-custom-body fetch did not result in any content", __FUNCTION__); | ||
| } | ||
| TSHttpTxnReenable(http_txn, TS_EVENT_HTTP_ERROR); | ||
| } break; | ||
| case OperatorSetBodyFrom::TS_EVENT_FETCHSM_FAILURE: { | ||
| Dbg(pi_dbg_ctl, "OperatorSetBodyFrom: Error getting custom body"); | ||
| TSHttpTxnReenable(http_txn, TS_EVENT_HTTP_CONTINUE); | ||
| } break; | ||
| case OperatorSetBodyFrom::TS_EVENT_FETCHSM_TIMEOUT: { | ||
| Dbg(pi_dbg_ctl, "OperatorSetBodyFrom: Timeout getting custom body"); | ||
| TSHttpTxnReenable(http_txn, TS_EVENT_HTTP_CONTINUE); | ||
| } break; | ||
| case TS_EVENT_HTTP_TXN_CLOSE: { | ||
| TSContDestroy(cont); | ||
| TSHttpTxnReenable(http_txn, TS_EVENT_HTTP_CONTINUE); | ||
| } break; | ||
| case TS_EVENT_HTTP_SEND_RESPONSE_HDR: | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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)?
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 |
||
| // Do nothing | ||
| // The transaction is reenabled with the FetchSM transaction | ||
| break; | ||
| default: | ||
| TSError("[%s] handleFetchEvents got unknown event: %d", PLUGIN_NAME, event); | ||
| break; | ||
| } | ||
| return 0; | ||
| } | ||
|
|
||
| TSReturnCode | ||
| createRequestString(const std::string_view &value, char (&req_buf)[MAX_SIZE], int *req_buf_size) | ||
| { | ||
| const char *start = value.data(); | ||
| const char *end = start + value.size(); | ||
| TSMLoc url_loc; | ||
| TSMBuffer url_buf = TSMBufferCreate(); | ||
| int host_len, url_len = 0; | ||
|
|
||
| if (TSUrlCreate(url_buf, &url_loc) == TS_SUCCESS && TSUrlParse(url_buf, url_loc, &start, end) == TS_PARSE_DONE) { | ||
| const char *host = TSUrlHostGet(url_buf, url_loc, &host_len); | ||
| const char *url = TSUrlStringGet(url_buf, url_loc, &url_len); | ||
|
|
||
| *req_buf_size = snprintf(req_buf, MAX_SIZE, "GET %.*s HTTP/1.1\r\nHost: %.*s\r\n\r\n", url_len, url, host_len, host); | ||
|
|
||
| TSMBufferDestroy(url_buf); | ||
|
|
||
| return TS_SUCCESS; | ||
| } else { | ||
| Dbg(pi_dbg_ctl, "Failed to parse url %s", start); | ||
| TSMBufferDestroy(url_buf); | ||
| return TS_ERROR; | ||
| } | ||
| } | ||
|
|
||
| } // namespace | ||
|
|
||
| // OperatorConfig | ||
| void | ||
| OperatorSetConfig::initialize(Parser &p) | ||
|
|
@@ -1219,3 +1304,60 @@ OperatorRunPlugin::exec(const Resources &res) const | |
| _plugin->doRemap(res.txnp, res._rri); | ||
| } | ||
| } | ||
|
|
||
| // OperatorSetBody | ||
| void | ||
| OperatorSetBodyFrom::initialize(Parser &p) | ||
| { | ||
| Operator::initialize(p); | ||
| // we want the arg since body only takes one value | ||
| _value.set_value(p.get_arg()); | ||
| require_resources(RSRC_SERVER_RESPONSE_HEADERS); | ||
| require_resources(RSRC_RESPONSE_STATUS); | ||
| } | ||
|
|
||
| void | ||
| OperatorSetBodyFrom::initialize_hooks() | ||
| { | ||
| add_allowed_hook(TS_HTTP_READ_RESPONSE_HDR_HOOK); | ||
ywkaras marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| } | ||
|
|
||
| void | ||
| OperatorSetBodyFrom::exec(const Resources &res) const | ||
| { | ||
| if (TSHttpTxnIsInternal(res.txnp)) { | ||
| // If this is triggered by an internal transaction, a infinte loop may occur | ||
| // It should only be triggered by the original transaction sent by the client | ||
| Dbg(pi_dbg_ctl, "OperatorSetBodyFrom triggered by an internal transaction"); | ||
| return; | ||
| } | ||
|
|
||
| char req_buf[MAX_SIZE]; | ||
| int req_buf_size = 0; | ||
| if (createRequestString(_value.get_value(), req_buf, &req_buf_size) == TS_SUCCESS) { | ||
| TSCont fetchCont = TSContCreate(handleFetchEvents, TSMutexCreate()); | ||
| TSContDataSet(fetchCont, static_cast<void *>(res.txnp)); | ||
|
|
||
| TSHttpTxnHookAdd(res.txnp, TS_HTTP_SEND_RESPONSE_HDR_HOOK, fetchCont); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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.
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is what I'm proposing, for the exec() function to return a flag that controls whether reenable is called: ywkaras@0ef4d24
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I see. I think it would be better to seperate these changes into 2 PRs because the change you are proposing impacts all of header rewrite.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. OK, I can put up a follow-on PR once this is merged. |
||
| TSHttpTxnHookAdd(res.txnp, TS_HTTP_TXN_CLOSE_HOOK, fetchCont); | ||
|
|
||
| TSFetchEvent event_ids; | ||
| event_ids.success_event_id = TS_EVENT_FETCHSM_SUCCESS; | ||
| event_ids.failure_event_id = TS_EVENT_FETCHSM_FAILURE; | ||
| event_ids.timeout_event_id = TS_EVENT_FETCHSM_TIMEOUT; | ||
|
|
||
| struct sockaddr_in addr; | ||
JosiahWI marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| addr.sin_family = AF_INET; | ||
| addr.sin_addr.s_addr = LOCAL_IP_ADDRESS; | ||
| addr.sin_port = LOCAL_PORT; | ||
| TSFetchUrl(static_cast<const char *>(req_buf), req_buf_size, reinterpret_cast<struct sockaddr const *>(&addr), fetchCont, | ||
| AFTER_BODY, event_ids); | ||
|
|
||
| // 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.txnp, res.resp_status); | ||
| } else { | ||
| TSError(PLUGIN_NAME, "OperatorSetBodyFrom:exec:: Could not create request"); | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| Custom body found |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,17 @@ | ||
| <HTML> | ||
| <HEAD> | ||
| <TITLE>Unknown Host</TITLE> | ||
| </HEAD> | ||
|
|
||
| <BODY BGCOLOR="white" FGCOLOR="black"> | ||
| <H1>Unknown Host</H1> | ||
| <HR> | ||
|
|
||
| <FONT FACE="Helvetica,Arial"><B> | ||
| Description: Unable to locate the server requested --- | ||
| the server does not have a DNS entry. Perhaps there is a misspelling | ||
| in the server name, or the server no longer exists. Double-check the | ||
| name and try again. | ||
| </B></FONT> | ||
| <HR> | ||
| </BODY> |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,15 @@ | ||
| <HTML> | ||
| <HEAD> | ||
| <TITLE>Not Found on Accelerator</TITLE> | ||
| </HEAD> | ||
|
|
||
| <BODY BGCOLOR="white" FGCOLOR="black"> | ||
| <H1>Not Found on Accelerator</H1> | ||
| <HR> | ||
|
|
||
| <FONT FACE="Helvetica,Arial"><B> | ||
| Description: Your request on the specified host was not found. | ||
| Check the location and try again. | ||
| </B></FONT> | ||
| <HR> | ||
| </BODY> |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| Custom body found |
Uh oh!
There was an error while loading. Please reload this page.