| /** @file |
| |
| An example program which illustrates adding and manipulating an |
| HTTP response MIME header: |
| |
| Usage: response_header_1.so |
| |
| add read_resp_header hook |
| get http response header |
| if 200, then |
| add mime extension header with count of zero |
| add mime extension header with date response was received |
| add "Cache-Control: public" header |
| else if 304, then |
| retrieve cached header |
| get old value of mime header count |
| increment mime header count |
| store mime header with new count |
| |
| @section license License |
| |
| 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 <time.h> |
| #include <stdio.h> |
| #include <string.h> |
| #include <ctype.h> |
| |
| #include "ts/ts.h" |
| #include "tscore/ink_defs.h" |
| |
| #define PLUGIN_NAME "response_header_1" |
| |
| static int init_buffer_status; |
| |
| static char *mimehdr1_name; |
| static char *mimehdr2_name; |
| static char *mimehdr1_value; |
| |
| static TSMBuffer hdr_bufp; |
| static TSMLoc hdr_loc; |
| |
| static TSMLoc field_loc; |
| static TSMLoc value_loc; |
| |
| static void |
| modify_header(TSHttpTxn txnp) |
| { |
| TSMBuffer resp_bufp; |
| TSMBuffer cached_bufp; |
| TSMLoc resp_loc; |
| TSMLoc cached_loc; |
| TSHttpStatus resp_status; |
| TSMLoc new_field_loc; |
| TSMLoc cached_field_loc; |
| time_t recvd_time; |
| |
| const char *chkptr; |
| int chklength; |
| |
| int num_refreshes = 0; |
| |
| if (!init_buffer_status) { |
| return; /* caller reenables */ |
| } |
| |
| if (TSHttpTxnServerRespGet(txnp, &resp_bufp, &resp_loc) != TS_SUCCESS) { |
| TSError("[%s] Couldn't retrieve server response header", PLUGIN_NAME); |
| return; /* caller reenables */ |
| } |
| |
| /* TSqa06246/TSqa06144 */ |
| resp_status = TSHttpHdrStatusGet(resp_bufp, resp_loc); |
| |
| if (TS_HTTP_STATUS_OK == resp_status) { |
| TSDebug(PLUGIN_NAME, "Processing 200 OK"); |
| TSMimeHdrFieldCreate(resp_bufp, resp_loc, &new_field_loc); /* Probably should check for errors */ |
| TSDebug(PLUGIN_NAME, "Created new resp field with loc %p", new_field_loc); |
| |
| /* copy name/values created at init |
| * ( "x-num-served-from-cache" ) : ( "0" ) |
| */ |
| TSMimeHdrFieldCopy(resp_bufp, resp_loc, new_field_loc, hdr_bufp, hdr_loc, field_loc); |
| |
| /*********** Unclear why this is needed **************/ |
| TSMimeHdrFieldAppend(resp_bufp, resp_loc, new_field_loc); |
| |
| /* Cache-Control: Public */ |
| TSMimeHdrFieldCreate(resp_bufp, resp_loc, &new_field_loc); /* Probably should check for errors */ |
| TSDebug(PLUGIN_NAME, "Created new resp field with loc %p", new_field_loc); |
| TSMimeHdrFieldAppend(resp_bufp, resp_loc, new_field_loc); |
| TSMimeHdrFieldNameSet(resp_bufp, resp_loc, new_field_loc, TS_MIME_FIELD_CACHE_CONTROL, TS_MIME_LEN_CACHE_CONTROL); |
| TSMimeHdrFieldValueStringInsert(resp_bufp, resp_loc, new_field_loc, -1, TS_HTTP_VALUE_PUBLIC, TS_HTTP_LEN_PUBLIC); |
| |
| /* |
| * mimehdr2_name = TSstrdup( "x-date-200-recvd" ) : CurrentDateTime |
| */ |
| TSMimeHdrFieldCreate(resp_bufp, resp_loc, &new_field_loc); /* Probably should check for errors */ |
| TSDebug(PLUGIN_NAME, "Created new resp field with loc %p", new_field_loc); |
| TSMimeHdrFieldAppend(resp_bufp, resp_loc, new_field_loc); |
| TSMimeHdrFieldNameSet(resp_bufp, resp_loc, new_field_loc, mimehdr2_name, strlen(mimehdr2_name)); |
| recvd_time = time(NULL); |
| TSMimeHdrFieldValueDateInsert(resp_bufp, resp_loc, new_field_loc, recvd_time); |
| |
| TSHandleMLocRelease(resp_bufp, resp_loc, new_field_loc); |
| TSHandleMLocRelease(resp_bufp, TS_NULL_MLOC, resp_loc); |
| |
| } else if (TS_HTTP_STATUS_NOT_MODIFIED == resp_status) { |
| TSDebug(PLUGIN_NAME, "Processing 304 Not Modified"); |
| |
| /* N.B.: Protect writes to data (hash on URL + mutex: (ies)) */ |
| |
| /* Get the cached HTTP header */ |
| if (TSHttpTxnCachedRespGet(txnp, &cached_bufp, &cached_loc) != TS_SUCCESS) { |
| TSError("[%s] STATUS 304, TSHttpTxnCachedRespGet():", PLUGIN_NAME); |
| TSError("[%s] Couldn't retrieve cached response header", PLUGIN_NAME); |
| TSHandleMLocRelease(resp_bufp, TS_NULL_MLOC, resp_loc); |
| return; /* Caller reenables */ |
| } |
| |
| /* Get the cached MIME field name for this HTTP header */ |
| cached_field_loc = TSMimeHdrFieldFind(cached_bufp, cached_loc, (const char *)mimehdr1_name, strlen(mimehdr1_name)); |
| if (TS_NULL_MLOC == cached_field_loc) { |
| TSError("[%s] Can't find header %s in cached document", PLUGIN_NAME, mimehdr1_name); |
| TSHandleMLocRelease(resp_bufp, TS_NULL_MLOC, resp_loc); |
| TSHandleMLocRelease(cached_bufp, TS_NULL_MLOC, cached_loc); |
| return; /* Caller reenables */ |
| } |
| |
| /* Get the cached MIME value for this name in this HTTP header */ |
| chkptr = TSMimeHdrFieldValueStringGet(cached_bufp, cached_loc, cached_field_loc, -1, &chklength); |
| if (NULL == chkptr || !chklength) { |
| TSError("[%s] Could not find value for cached MIME field name %s", PLUGIN_NAME, mimehdr1_name); |
| TSHandleMLocRelease(resp_bufp, TS_NULL_MLOC, resp_loc); |
| TSHandleMLocRelease(cached_bufp, TS_NULL_MLOC, cached_loc); |
| TSHandleMLocRelease(cached_bufp, cached_loc, cached_field_loc); |
| return; /* Caller reenables */ |
| } |
| TSDebug(PLUGIN_NAME, "Header field value is %s, with length %d", chkptr, chklength); |
| |
| /* Get the cached MIME value for this name in this HTTP header */ |
| /* |
| TSMimeHdrFieldValueUintGet(cached_bufp, cached_loc, cached_field_loc, 0, &num_refreshes); |
| TSDebug(PLUGIN_NAME, |
| "Cached header shows %d refreshes so far", num_refreshes ); |
| |
| num_refreshes++ ; |
| */ |
| |
| /* txn origin server response for this transaction stored |
| * in resp_bufp, resp_loc |
| * |
| * Create a new MIME field/value. Cached value has been incremented. |
| * Insert new MIME field/value into the server response buffer, |
| * allow HTTP processing to continue. This will update |
| * (indirectly invalidates) the cached HTTP headers MIME field. |
| * It is apparently not necessary to update all of the MIME fields |
| * in the in-process response in order to have the cached response |
| * become invalid. |
| */ |
| TSMimeHdrFieldCreate(resp_bufp, resp_loc, &new_field_loc); /* Probably should check for errors */ |
| |
| /* mimehdr1_name : TSstrdup( "x-num-served-from-cache" ) ; */ |
| |
| TSMimeHdrFieldAppend(resp_bufp, resp_loc, new_field_loc); |
| TSMimeHdrFieldNameSet(resp_bufp, resp_loc, new_field_loc, mimehdr1_name, strlen(mimehdr1_name)); |
| |
| TSMimeHdrFieldValueUintInsert(resp_bufp, resp_loc, new_field_loc, -1, num_refreshes); |
| |
| TSHandleMLocRelease(resp_bufp, resp_loc, new_field_loc); |
| TSHandleMLocRelease(cached_bufp, cached_loc, cached_field_loc); |
| TSHandleMLocRelease(cached_bufp, TS_NULL_MLOC, cached_loc); |
| TSHandleMLocRelease(resp_bufp, TS_NULL_MLOC, resp_loc); |
| |
| } else { |
| TSDebug(PLUGIN_NAME, "other response code %d", resp_status); |
| } |
| |
| /* |
| * Additional 200/304 processing can go here, if so desired. |
| */ |
| |
| /* Caller reneables */ |
| } |
| |
| static int |
| modify_response_header_plugin(TSCont contp ATS_UNUSED, TSEvent event, void *edata) |
| { |
| TSHttpTxn txnp = (TSHttpTxn)edata; |
| |
| switch (event) { |
| case TS_EVENT_HTTP_READ_RESPONSE_HDR: |
| TSDebug(PLUGIN_NAME, "Called back with TS_EVENT_HTTP_READ_RESPONSE_HDR"); |
| modify_header(txnp); |
| TSHttpTxnReenable(txnp, TS_EVENT_HTTP_CONTINUE); |
| /* fall through */ |
| |
| default: |
| break; |
| } |
| return 0; |
| } |
| |
| void |
| TSPluginInit(int argc, const char *argv[]) |
| { |
| TSMLoc chk_field_loc; |
| |
| TSPluginRegistrationInfo info; |
| |
| info.plugin_name = PLUGIN_NAME; |
| info.vendor_name = "Apache Software Foundation"; |
| info.support_email = "dev@trafficserver.apache.org"; |
| |
| if (TSPluginRegister(&info) != TS_SUCCESS) { |
| TSError("[%s] Plugin registration failed", PLUGIN_NAME); |
| } |
| |
| init_buffer_status = 0; |
| if (argc > 1) { |
| TSError("[%s] usage: %s", PLUGIN_NAME, argv[0]); |
| TSError("[%s] warning: too many args %d", PLUGIN_NAME, argc); |
| TSError("[%s] warning: ignoring unused arguments beginning with %s", PLUGIN_NAME, argv[1]); |
| } |
| |
| /* |
| * The following code sets up an "init buffer" containing an extension header |
| * and its initial value. This will be the same for all requests, so we try |
| * to be efficient and do all of the work here rather than on a per-transaction |
| * basis. |
| */ |
| |
| hdr_bufp = TSMBufferCreate(); |
| TSMimeHdrCreate(hdr_bufp, &hdr_loc); |
| |
| mimehdr1_name = TSstrdup("x-num-served-from-cache"); |
| mimehdr1_value = TSstrdup("0"); |
| |
| /* Create name here and set DateTime value when o.s. |
| * response 200 is received |
| */ |
| mimehdr2_name = TSstrdup("x-date-200-recvd"); |
| |
| TSDebug(PLUGIN_NAME, "Inserting header %s with value %s into init buffer", mimehdr1_name, mimehdr1_value); |
| |
| TSMimeHdrFieldCreate(hdr_bufp, hdr_loc, &field_loc); /* Probably should check for errors */ |
| TSMimeHdrFieldAppend(hdr_bufp, hdr_loc, field_loc); |
| TSMimeHdrFieldNameSet(hdr_bufp, hdr_loc, field_loc, mimehdr1_name, strlen(mimehdr1_name)); |
| TSMimeHdrFieldValueStringInsert(hdr_bufp, hdr_loc, field_loc, -1, mimehdr1_value, strlen(mimehdr1_value)); |
| TSDebug(PLUGIN_NAME, "init buffer hdr, field and value locs are %p, %p and %p", hdr_loc, field_loc, value_loc); |
| init_buffer_status = 1; |
| |
| TSHttpHookAdd(TS_HTTP_READ_RESPONSE_HDR_HOOK, TSContCreate(modify_response_header_plugin, NULL)); |
| |
| /* |
| * The following code demonstrates how to extract the field_loc from the header. |
| * In this plugin, the init buffer and thus field_loc never changes. Code |
| * similar to this may be used to extract header fields from any buffer. |
| */ |
| |
| if (TS_NULL_MLOC == (chk_field_loc = TSMimeHdrFieldGet(hdr_bufp, hdr_loc, 0))) { |
| TSError("[%s] Couldn't retrieve header field from init buffer", PLUGIN_NAME); |
| TSError("[%s] Marking init buffer as corrupt; no more plugin processing", PLUGIN_NAME); |
| init_buffer_status = 0; |
| /* bail out here and reenable transaction */ |
| } else { |
| if (field_loc != chk_field_loc) { |
| TSError("[%s] Retrieved buffer field loc is %p when it should be %p", PLUGIN_NAME, chk_field_loc, field_loc); |
| } |
| } |
| } |