blob: 8754c22ccb51cbf93292ed877586e9a53d42cdf4 [file]
/** @file
A brief file description
@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.
*/
/*
* response-header-1.c:
* an example program which illustrates adding and manipulating
* an HTTP response MIME header:
*
* Authorized possession and use of this software pursuant only
* to the terms of a written license agreement.
*
* 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
*
*
*
*/
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <ts/ts.h>
static int init_buffer_status;
static char *mimehdr1_name;
static char *mimehdr2_name;
static char *mimehdr1_value;
static INKMBuffer hdr_bufp;
static INKMLoc hdr_loc;
static INKMLoc field_loc;
static INKMLoc value_loc;
static void
modify_header(INKHttpTxn txnp, INKCont contp)
{
INKMBuffer resp_bufp;
INKMBuffer cached_bufp;
INKMLoc resp_loc;
INKMLoc cached_loc;
INKHttpStatus resp_status;
INKMLoc new_field_loc;
INKMLoc new_value_loc;
INKMLoc cached_field_loc;
time_t recvd_time;
const char *chkptr;
int chklength;
int num_refreshes;
if (!init_buffer_status)
return; /* caller reenables */
if (!INKHttpTxnServerRespGet(txnp, &resp_bufp, &resp_loc)) {
INKError("couldn't retrieve server response header\n");
return; /* caller reenables */
}
/* INKqa06246/INKqa06144 */
resp_status = INKHttpHdrStatusGet(resp_bufp, resp_loc);
if (INK_HTTP_STATUS_OK == resp_status) {
INKDebug("resphdr", "Processing 200 OK");
new_field_loc = INKMimeHdrFieldCreate(resp_bufp, resp_loc);
INKDebug("resphdr", "Created new resp field with loc %d", new_field_loc);
/* copy name/values created at init
* ( "x-num-served-from-cache" ) : ( "0" )
*/
INKMimeHdrFieldCopy(resp_bufp, resp_loc, new_field_loc, hdr_bufp, hdr_loc, field_loc);
/*********** Unclear why this is needed **************/
INKMimeHdrFieldInsert(resp_bufp, resp_loc, new_field_loc, -1);
/* Cache-Control: Public */
new_field_loc = INKMimeHdrFieldCreate(resp_bufp, resp_loc);
INKDebug("resphdr", "Created new resp field with loc %d", new_field_loc);
INKMimeHdrFieldInsert(resp_bufp, resp_loc, new_field_loc, -1);
INKMimeHdrFieldNameSet(resp_bufp, resp_loc, new_field_loc,
INK_MIME_FIELD_CACHE_CONTROL, INK_MIME_LEN_CACHE_CONTROL);
INKMimeHdrFieldValueInsert(resp_bufp, resp_loc, new_field_loc,
INK_HTTP_VALUE_PUBLIC, strlen(INK_HTTP_VALUE_PUBLIC), -1);
/*
* mimehdr2_name = INKstrdup( "x-date-200-recvd" ) : CurrentDateTime
*/
new_field_loc = INKMimeHdrFieldCreate(resp_bufp, resp_loc);
INKDebug("resphdr", "Created new resp field with loc %d", new_field_loc);
INKMimeHdrFieldInsert(resp_bufp, resp_loc, new_field_loc, -1);
INKMimeHdrFieldNameSet(resp_bufp, resp_loc, new_field_loc, mimehdr2_name, strlen(mimehdr2_name));
recvd_time = time(NULL);
INKMimeHdrFieldValueInsertDate(resp_bufp, resp_loc, new_field_loc, recvd_time, -1);
INKHandleMLocRelease(resp_bufp, resp_loc, new_field_loc);
INKHandleMLocRelease(resp_bufp, INK_NULL_MLOC, resp_loc);
} else if (INK_HTTP_STATUS_NOT_MODIFIED == resp_status) {
INKDebug("resphdr", "Processing 304 Not Modified");
/* N.B.: Protect writes to data (hash on URL + mutex: (ies)) */
/* Get the cached HTTP header */
if (!INKHttpTxnCachedRespGet(txnp, &cached_bufp, &cached_loc)) {
INKError("STATUS 304, INKHttpTxnCachedRespGet():");
INKError("couldn't retrieve cached response header\n");
INKHandleMLocRelease(resp_bufp, INK_NULL_MLOC, resp_loc);
return; /* Caller reenables */
}
/* Get the cached MIME field name for this HTTP header */
cached_field_loc = INKMimeHdrFieldFind(cached_bufp, cached_loc,
(const char *) mimehdr1_name, strlen(mimehdr1_name));
if (0 == cached_field_loc) {
INKError("Can't find header %s in cached document", mimehdr1_name);
INKHandleMLocRelease(resp_bufp, INK_NULL_MLOC, resp_loc);
INKHandleMLocRelease(cached_bufp, INK_NULL_MLOC, cached_loc);
return; /* Caller reenables */
}
/* Get the cached MIME value for this name in this HTTP header */
chkptr = INKMimeHdrFieldValueGet(cached_bufp, cached_loc, cached_field_loc, 0, &chklength);
if (NULL == chkptr || !chklength) {
INKError("Could not find value for cached MIME field name %s", mimehdr1_name);
INKHandleMLocRelease(resp_bufp, INK_NULL_MLOC, resp_loc);
INKHandleMLocRelease(cached_bufp, INK_NULL_MLOC, cached_loc);
INKHandleMLocRelease(cached_bufp, cached_loc, cached_field_loc);
return; /* Caller reenables */
}
INKDebug("resphdr", "Header field value is %s, with length %d", chkptr, chklength);
/* TODO check these comments for correctness */
/*
* Since INKMimeHdrFieldValueGet returned with valid values
* are we also guaranteed that INKMimeHdrFieldValueGetUint returns
* valid values? There is no erro code for INKMimeHdrFieldValueGetUint
* and 0 is a valid value.
*/
/* Get the cached MIME value for this name in this HTTP header */
/*
num_refreshes =
INKMimeHdrFieldValueGetUint(cached_bufp, cached_loc,
cached_field_loc, 0);
INKDebug("resphdr",
"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.
*/
new_field_loc = INKMimeHdrFieldCreate(resp_bufp, resp_loc);
/* mimehdr1_name : INKstrdup( "x-num-served-from-cache" ) ; */
INKMimeHdrFieldInsert(resp_bufp, resp_loc, new_field_loc, -1);
INKMimeHdrFieldNameSet(resp_bufp, resp_loc, new_field_loc, mimehdr1_name, strlen(mimehdr1_name));
INKMimeHdrFieldValueInsertUint(resp_bufp, resp_loc, new_field_loc, num_refreshes, -1);
INKHandleStringRelease(cached_bufp, cached_loc, chkptr);
INKHandleMLocRelease(resp_bufp, resp_loc, new_field_loc);
INKHandleMLocRelease(cached_bufp, cached_loc, cached_field_loc);
INKHandleMLocRelease(cached_bufp, INK_NULL_MLOC, cached_loc);
INKHandleMLocRelease(resp_bufp, INK_NULL_MLOC, resp_loc);
} else {
INKDebug("resphdr", "other response code %d", resp_status);
}
/*
* Additional 200/304 processing can go here, if so desired.
*/
/* Caller reneables */
}
static int
modify_response_header_plugin(INKCont contp, INKEvent event, void *edata)
{
INKHttpTxn txnp = (INKHttpTxn) edata;
switch (event) {
case INK_EVENT_HTTP_READ_RESPONSE_HDR:
INKDebug("resphdr", "Called back with INK_EVENT_HTTP_READ_RESPONSE_HDR");
modify_header(txnp, contp);
INKHttpTxnReenable(txnp, INK_EVENT_HTTP_CONTINUE);
/* fall through */
default:
break;
}
return 0;
}
int
check_ts_version()
{
const char *ts_version = INKTrafficServerVersionGet();
int result = 0;
if (ts_version) {
int major_ts_version = 0;
int minor_ts_version = 0;
int patch_ts_version = 0;
if (sscanf(ts_version, "%d.%d.%d", &major_ts_version, &minor_ts_version, &patch_ts_version) != 3) {
return 0;
}
/* Need at least TS 2.0 */
if (major_ts_version >= 2) {
result = 1;
}
}
return result;
}
void
INKPluginInit(int argc, const char *argv[])
{
INKMLoc chk_field_loc;
const char *p;
int i;
INKPluginRegistrationInfo info;
info.plugin_name = "response-header-1";
info.vendor_name = "MyCompany";
info.support_email = "ts-api-support@MyCompany.com";
if (!INKPluginRegister(INK_SDK_VERSION_2_0, &info)) {
INKError("Plugin registration failed.\n");
}
if (!check_ts_version()) {
INKError("Plugin requires Traffic Server 2.0 or later\n");
return;
}
init_buffer_status = 0;
if (argc > 1) {
INKError("usage: %s \n", argv[0]);
INKError("warning: too many args %d\n", argc);
INKError("warning: ignoring unused arguments beginning with %s\n", 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 = INKMBufferCreate();
hdr_loc = INKMimeHdrCreate(hdr_bufp);
mimehdr1_name = INKstrdup("x-num-served-from-cache");
mimehdr1_value = INKstrdup("0");
/* Create name here and set DateTime value when o.s.
* response 200 is recieved
*/
mimehdr2_name = INKstrdup("x-date-200-recvd");
INKDebug("resphdr", "Inserting header %s with value %s into init buffer", mimehdr1_name, mimehdr1_value);
field_loc = INKMimeHdrFieldCreate(hdr_bufp, hdr_loc);
INKMimeHdrFieldInsert(hdr_bufp, hdr_loc, field_loc, -1);
INKMimeHdrFieldNameSet(hdr_bufp, hdr_loc, field_loc, mimehdr1_name, strlen(mimehdr1_name));
value_loc = INKMimeHdrFieldValueInsert(hdr_bufp, hdr_loc, field_loc, mimehdr1_value, strlen(mimehdr1_value), -1);
INKDebug("resphdr", "init buffer hdr, field and value locs are %d, %d and %d", hdr_loc, field_loc, value_loc);
init_buffer_status = 1;
INKHttpHookAdd(INK_HTTP_READ_RESPONSE_HDR_HOOK, INKContCreate(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 (0 == (chk_field_loc = INKMimeHdrFieldGet(hdr_bufp, hdr_loc, 0))) {
INKError("couldn't retrieve header field from init buffer");
INKError("marking init buffer as corrupt; no more plugin processing");
init_buffer_status = 0;
/* bail out here and reenable transaction */
} else {
if (field_loc != chk_field_loc)
INKError("retrieved buffer field loc is %d when it should be %d", chk_field_loc, field_loc);
}
}