1 /** @file
2
3 Multiplexes request to other origins.
4
5 @section license License
6
7 Licensed to the Apache Software Foundation (ASF) under one
8 or more contributor license agreements. See the NOTICE file
9 distributed with this work for additional information
10 regarding copyright ownership. The ASF licenses this file
11 to you under the Apache License, Version 2.0 (the
12 "License"); you may not use this file except in compliance
13 with the License. You may obtain a copy of the License at
14
15 http://www.apache.org/licenses/LICENSE-2.0
16
17 Unless required by applicable law or agreed to in writing, software
18 distributed under the License is distributed on an "AS IS" BASIS,
19 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
20 See the License for the specific language governing permissions and
21 limitations under the License.
22 */
23 #include <algorithm>
24 #include <ts/ts.h>
25 #include <ts/remap.h>
26
27 #include <cinttypes>
28
29 #include "dispatch.h"
30 #include "fetcher.h"
31 #include "original-request.h"
32 #include "post.h"
33
34 #ifndef PLUGIN_TAG
35 #error Please define a PLUGIN_TAG before including this file.
36 #endif
37
38 // 1s
39 const size_t DEFAULT_TIMEOUT = 1000000000000;
40
41 Statistics statistics;
42
43 TSReturnCode
TSRemapInit(TSRemapInterface *,char *,int)44 TSRemapInit(TSRemapInterface *, char *, int)
45 {
46 {
47 timeout = 0;
48 const char *const timeoutEnv = getenv(PLUGIN_TAG "__timeout");
49 if (timeoutEnv != nullptr) {
50 timeout = atol(timeoutEnv);
51 }
52 if (timeout < 1) {
53 timeout = DEFAULT_TIMEOUT;
54 }
55 TSDebug(PLUGIN_TAG, "timeout is set to: %zu", timeout);
56 }
57
58 statistics.failures = TSStatCreate(PLUGIN_TAG ".failures", TS_RECORDDATATYPE_INT, TS_STAT_NON_PERSISTENT, TS_STAT_SYNC_COUNT);
59
60 statistics.hits = TSStatCreate(PLUGIN_TAG ".hits", TS_RECORDDATATYPE_INT, TS_STAT_NON_PERSISTENT, TS_STAT_SYNC_COUNT);
61
62 statistics.time = TSStatCreate(PLUGIN_TAG ".time", TS_RECORDDATATYPE_INT, TS_STAT_NON_PERSISTENT, TS_STAT_SYNC_AVG);
63
64 statistics.requests = TSStatCreate(PLUGIN_TAG ".requests", TS_RECORDDATATYPE_INT, TS_STAT_NON_PERSISTENT, TS_STAT_SYNC_COUNT);
65
66 statistics.timeouts = TSStatCreate(PLUGIN_TAG ".timeouts", TS_RECORDDATATYPE_INT, TS_STAT_NON_PERSISTENT, TS_STAT_SYNC_COUNT);
67
68 statistics.size = TSStatCreate(PLUGIN_TAG ".size", TS_RECORDDATATYPE_INT, TS_STAT_NON_PERSISTENT, TS_STAT_SYNC_AVG);
69
70 return TS_SUCCESS;
71 }
72
73 TSReturnCode
TSRemapNewInstance(int argc,char ** argv,void ** i,char *,int)74 TSRemapNewInstance(int argc, char **argv, void **i, char *, int)
75 {
76 assert(i != nullptr);
77 Instance *instance = new Instance;
78 instance->skipPostPut = false;
79
80 if (argc > 2) {
81 std::copy_if(argv + 2, argv + argc, std::back_inserter(instance->origins), [&](const std::string &s) {
82 if (s == "proxy.config.multiplexer.skip_post_put=1") {
83 instance->skipPostPut = true;
84 return false;
85 }
86 return true;
87 });
88 }
89 TSDebug(PLUGIN_TAG, "skipPostPut is %s", (instance->skipPostPut ? "true" : "false"));
90
91 *i = static_cast<void *>(instance);
92
93 return TS_SUCCESS;
94 }
95
96 void
TSRemapDeleteInstance(void * i)97 TSRemapDeleteInstance(void *i)
98 {
99 assert(i != nullptr);
100 delete static_cast<Instance *>(i);
101 }
102
103 void
DoRemap(const Instance & i,TSHttpTxn t)104 DoRemap(const Instance &i, TSHttpTxn t)
105 {
106 assert(t != nullptr);
107 /*
108 if (POST || PUT) {
109 transformRequest
110 }
111 */
112 TSMBuffer buffer;
113 TSMLoc location;
114
115 CHECK(TSHttpTxnClientReqGet(t, &buffer, &location));
116
117 assert(buffer != nullptr);
118 assert(location != nullptr);
119
120 int length;
121 const char *const method = TSHttpHdrMethodGet(buffer, location, &length);
122
123 TSDebug(PLUGIN_TAG, "Method is %s.", std::string(method, length).c_str());
124
125 if (i.skipPostPut && ((length == TS_HTTP_LEN_POST && memcmp(TS_HTTP_METHOD_POST, method, TS_HTTP_LEN_POST) == 0) ||
126 (length == TS_HTTP_LEN_PUT && memcmp(TS_HTTP_METHOD_PUT, method, TS_HTTP_LEN_PUT) == 0))) {
127 TSHandleMLocRelease(buffer, TS_NULL_MLOC, location);
128 } else {
129 {
130 TSMLoc field;
131
132 CHECK(TSMimeHdrFieldCreateNamed(buffer, location, "X-Multiplexer", 13, &field));
133 assert(field != nullptr);
134
135 CHECK(TSMimeHdrFieldValueStringSet(buffer, location, field, -1, "original", 8));
136
137 CHECK(TSMimeHdrFieldAppend(buffer, location, field));
138
139 CHECK(TSHandleMLocRelease(buffer, location, field));
140 }
141
142 Requests requests;
143 generateRequests(i.origins, buffer, location, requests);
144 assert(requests.size() == i.origins.size());
145
146 if (length == TS_HTTP_LEN_POST && memcmp(TS_HTTP_METHOD_POST, method, TS_HTTP_LEN_POST) == 0) {
147 const TSVConn vconnection = TSTransformCreate(handlePost, t);
148 assert(vconnection != nullptr);
149 TSContDataSet(vconnection, new PostState(requests));
150 assert(requests.empty());
151 TSHttpTxnHookAdd(t, TS_HTTP_REQUEST_TRANSFORM_HOOK, vconnection);
152 } else {
153 dispatch(requests, timeout);
154 }
155
156 TSHandleMLocRelease(buffer, TS_NULL_MLOC, location);
157
158 TSStatIntIncrement(statistics.requests, 1);
159 }
160 }
161
162 TSRemapStatus
TSRemapDoRemap(void * i,TSHttpTxn t,TSRemapRequestInfo * r)163 TSRemapDoRemap(void *i, TSHttpTxn t, TSRemapRequestInfo *r)
164 {
165 assert(i != nullptr);
166 assert(t != nullptr);
167 const Instance *const instance = static_cast<const Instance *>(i);
168
169 if (!instance->origins.empty() && !TSHttpTxnIsInternal(t)) {
170 DoRemap(*instance, t);
171 } else {
172 TSDebug(PLUGIN_TAG, "Skipping transaction %p", t);
173 }
174
175 return TSREMAP_NO_REMAP;
176 }
177