[
https://issues.apache.org/jira/browse/TS-4395?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=15357307#comment-15357307
]
ASF GitHub Bot commented on TS-4395:
------------------------------------
Github user jpeach commented on a diff in the pull request:
https://github.com/apache/trafficserver/pull/747#discussion_r69158201
--- Diff: plugins/experimental/remap_purge/remap_purge.c ---
@@ -0,0 +1,335 @@
+/** @file
+
+ Per-remap purge RESTful API for stateful generation ID management.
+
+ @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 <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <string.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <getopt.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include "ts/ts.h"
+#include "ts/remap.h"
+#include "ts/ink_defs.h"
+
+static const char *PLUGIN_NAME = "remap_purge";
+static const char *DEFAULT_DIR = "var/trafficserver"; /* Not perfect, but
no better API) */
+
+typedef struct PurgeInstance_t {
+ char *id;
+ char *secret;
+ int secret_len;
+ char *header;
+ int header_len;
+ char *state_file;
+ bool allow_get;
+ int64_t gen_id;
+ TSMutex lock;
+} PurgeInstance;
+
+static char *
+make_state_path(const char *filename)
+{
+ if ('/' == *filename) {
+ return TSstrdup(filename);
+ } else {
+ char buf[8192];
+ struct stat s;
+ const char *dir = TSInstallDirGet();
+
+ snprintf(buf, sizeof(buf) - 1, "%s/%s/%s", dir, DEFAULT_DIR,
PLUGIN_NAME);
+ if (-1 == stat(buf, &s)) {
+ if (ENOENT == errno) {
+ if (-1 == mkdir(buf, S_IRWXU)) {
+ TSError("[%s] Unable to create directory %s: errno=%d",
PLUGIN_NAME, buf, errno);
+ return NULL;
+ }
+ } else {
+ TSError("[%s] Unable to stat() directory %s: errno=%d",
PLUGIN_NAME, buf, errno);
+ return NULL;
+ }
+ } else {
+ if (!S_ISDIR(s.st_mode)) {
+ TSError("[%s] Can not create directory %s, file exists",
PLUGIN_NAME, buf);
+ return NULL;
+ }
+ }
+ snprintf(buf, sizeof(buf) - 1, "%s/%s/%s/%s.genid", dir, DEFAULT_DIR,
PLUGIN_NAME, filename);
+ return TSstrdup(buf);
+ }
+
+ return NULL;
+}
+
+/* Constructor and destructor for the PurgeInstance */
+static void
+init_purge_instance(PurgeInstance *purge, char *id)
+{
+ FILE *file = fopen(purge->state_file, "r");
+
+ if (file) {
+ fscanf(file, "%" PRId64 "", &purge->gen_id);
+ TSDebug(PLUGIN_NAME, "Read genID from %s for %s", purge->state_file,
purge->id);
+ }
+
+ /* If not specified, we set the ID tag to the fromURL from the remap
rule that triggered */
+ if (!purge->id) {
+ purge->id = TSstrdup(id);
+ }
+
+ purge->lock = TSMutexCreate();
+}
+
+static void
+delete_purge_instance(PurgeInstance *purge)
+{
+ if (purge) {
+ TSfree(purge->id);
+ TSfree(purge->state_file);
+ TSfree(purge->secret);
+ TSfree(purge->header);
+ TSMutexDestroy(purge->lock);
+ TSfree(purge);
+ }
+}
+
+/* This is where we start the PURGE events, setting up the transactino to
fail,
+ and bump the generation ID, and finally save the state. */
+static int
+on_http_cache_lookup_complete(TSHttpTxn txnp, TSCont contp, PurgeInstance
*purge)
+{
+ FILE *file;
+
+ TSMutexLock(purge->lock);
+
+ ++purge->gen_id;
+ TSDebug(PLUGIN_NAME, "Bumping the Generation ID to %" PRId64 " for %s",
purge->gen_id, purge->id);
+
+ if ((file = fopen(purge->state_file, "w"))) {
+ TSDebug(PLUGIN_NAME, "\tsaving state to %s", purge->state_file);
+ fprintf(file, "%" PRId64 "", purge->gen_id);
+ fclose(file);
+ } else {
+ TSError("[%s] Unable to save state to file %s: errno=%d", PLUGIN_NAME,
purge->state_file, errno);
+ }
+
+ TSMutexUnlock(purge->lock);
+
+ TSHttpTxnReenable(txnp, TS_EVENT_HTTP_ERROR);
+ return TS_SUCCESS;
+}
+
+/* Before we can send the response, we want to modify it to a "200 OK"
again,
+ and produce some reasonabel body output. */
+static int
+on_send_response_header(TSHttpTxn txnp, TSCont contp, PurgeInstance *purge)
+{
+ TSMBuffer bufp;
+ TSMLoc hdr_loc;
+
+ TSDebug(PLUGIN_NAME, "Fixing up the response on the succseful PURGE");
+ if (TS_SUCCESS == TSHttpTxnClientRespGet(txnp, &bufp, &hdr_loc)) {
+ char response[1024];
+ int len = snprintf(response, sizeof(response) - 1, "PURGED
%s\r\n\r\n", purge->id);
+ ;
+
+ TSHttpHdrStatusSet(bufp, hdr_loc, TS_HTTP_STATUS_OK);
+ TSHttpHdrReasonSet(bufp, hdr_loc, "OK", 2);
+ TSHttpTxnErrorBodySet(txnp, TSstrdup(response), len, NULL);
+
+ TSHandleMLocRelease(bufp, TS_NULL_MLOC, hdr_loc);
+ TSHttpTxnReenable(txnp, TS_EVENT_HTTP_CONTINUE);
+ } else {
+ TSHttpTxnReenable(txnp, TS_EVENT_HTTP_ERROR);
+ }
+
+ return TS_SUCCESS;
+}
+
+/* This is the main continuation, triggered after DoRemap has decided we
should
+ handle this request internally. */
+static int
+purge_cont(TSCont contp, TSEvent event, void *edata)
+{
+ TSHttpTxn txnp = (TSHttpTxn)edata;
+ PurgeInstance *purge = (PurgeInstance *)TSContDataGet(contp);
+
+ switch (event) {
+ case TS_EVENT_HTTP_SEND_RESPONSE_HDR:
+ return on_send_response_header(txnp, contp, purge);
+ break;
+
+ case TS_EVENT_HTTP_CACHE_LOOKUP_COMPLETE:
+ return on_http_cache_lookup_complete(txnp, contp, purge);
+ break;
+
+ default:
+ TSDebug(PLUGIN_NAME, "Unexpected event: %d", event);
+ break;
+ }
+
+ return TS_SUCCESS;
+}
+
+static void
+handle_purge(TSHttpTxn txnp, PurgeInstance *purge)
+{
+ TSMBuffer reqp;
+ TSMLoc hdr_loc = NULL, url_loc = NULL;
+ bool should_purge = false;
+
+ if (TS_SUCCESS == TSHttpTxnClientReqGet(txnp, &reqp, &hdr_loc)) {
+ /* First see if we require the "secret" to be passed in a header, and
then use that */
+ if (purge->header) {
+ TSMLoc field_loc = TSMimeHdrFieldFind(reqp, hdr_loc, purge->header,
purge->header_len);
+
+ if (field_loc) {
+ const char *header;
+ int header_len;
+
+ header = TSMimeHdrFieldValueStringGet(reqp, hdr_loc, field_loc,
-1, &header_len);
+ TSDebug(PLUGIN_NAME, "Checking for %.*s == %s ?", header_len,
header, purge->secret);
+ if (header && (header_len == purge->secret_len) && !memcmp(header,
purge->secret, header_len)) {
+ should_purge = true;
+ }
+ TSHandleMLocRelease(reqp, hdr_loc, field_loc);
+ }
+ } else {
+ /* We are matching on the path component instead of a header */
+ if (TS_SUCCESS == TSHttpHdrUrlGet(reqp, hdr_loc, &url_loc)) {
+ int path_len = 0, method_len = 0;
+ const char *path = TSUrlPathGet(reqp, url_loc, &path_len);
+ const char *method = TSHttpHdrMethodGet(reqp, hdr_loc,
&method_len);
+
+ TSDebug(PLUGIN_NAME, "Checking PATH = %.*s", path_len, path);
+ if (((TS_HTTP_METHOD_PURGE == method) || ((TS_HTTP_METHOD_GET ==
method) && purge->allow_get)) && path &&
+ (path_len >= purge->secret_len) && !memcmp(path + (path_len -
purge->secret_len), purge->secret, purge->secret_len)) {
+ should_purge = true;
+ }
+ TSHandleMLocRelease(reqp, hdr_loc, url_loc);
+ }
+ }
+ TSHandleMLocRelease(reqp, TS_NULL_MLOC, hdr_loc);
+ }
+
+ /* Setup the continuation to handle this request if appropriate, if not,
set the GenID if needed */
+ if (should_purge) {
+ TSCont cont = TSContCreate(purge_cont, TSMutexCreate());
+
+ TSContDataSet(cont, purge);
+ TSHttpTxnHookAdd(txnp, TS_HTTP_CACHE_LOOKUP_COMPLETE_HOOK, cont);
+ TSHttpTxnHookAdd(txnp, TS_HTTP_SEND_RESPONSE_HDR_HOOK, cont);
+ } else if (purge->gen_id > 0) {
+ TSHttpTxnConfigIntSet(txnp, TS_CONFIG_HTTP_CACHE_GENERATION,
purge->gen_id);
+ }
+}
+
+TSReturnCode
+TSRemapInit(TSRemapInterface *api_info, char *errbuf, int errbuf_size)
+{
+ TSDebug(PLUGIN_NAME, "initialized");
+ return TS_SUCCESS;
+}
+
+TSReturnCode
+TSRemapNewInstance(int argc, char *argv[], void **ih, char *errbuf, int
errbuf_size)
+{
+ char *id = argv[0]; /* The ID is default to
the "from" URL, so save it */
+ PurgeInstance *purge = TSmalloc(sizeof(PurgeInstance));
--- End diff --
This malloc leaks on the error path.
> conf_purge: Simple plugin to purge an entire remap rule
> -------------------------------------------------------
>
> Key: TS-4395
> URL: https://issues.apache.org/jira/browse/TS-4395
> Project: Traffic Server
> Issue Type: New Feature
> Components: Plugins
> Reporter: Leif Hedstrom
> Assignee: Leif Hedstrom
> Fix For: 7.0.0
>
>
> This is similar to the existing plugin to purge based on a genID stored in a
> persistent storage. The difference is that the purging is done exclusively
> via a restful API, and has little (no) overhead on performance (since the
> generation ID is always in memory).
> Example:
> {code}
> map http://example.com/p1 http://p1.example.com
> @plugin=remap_purge.so @pparam=--path=__secret_purge__
> @pparam=--state=example_p1
> {code}
> And to purge:
> {code}
> $ curl -s -D - -X PURGE http://example.com/p1/__secret_purge__
> HTTP/1.1 200 OK
> Date: Sat, 30 Apr 2016 00:09:34 GMT
> Connection: close
> Server: ATS/7.0.0
> Content-Length: 39
> Content-Type: text/html
> PURGED http://example.com/p1
> {code}
--
This message was sent by Atlassian JIRA
(v6.3.4#6332)