1 /*
2 Licensed to the Apache Software Foundation (ASF) under one
3 or more contributor license agreements. See the NOTICE file
4 distributed with this work for additional information
5 regarding copyright ownership. The ASF licenses this file
6 to you under the Apache License, Version 2.0 (the
7 "License"); you may not use this file except in compliance
8 with the License. You may obtain a copy of the License at
9
10 http://www.apache.org/licenses/LICENSE-2.0
11
12 Unless required by applicable law or agreed to in writing, software
13 distributed under the License is distributed on an "AS IS" BASIS,
14 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 See the License for the specific language governing permissions and
16 limitations under the License.
17 */
18
19 #include "mp4_common.h"
20
21 static char *ts_arg(const char *param, size_t param_len, const char *key, size_t key_len, size_t *val_len);
22 static int mp4_handler(TSCont contp, TSEvent event, void *edata);
23 static void mp4_cache_lookup_complete(Mp4Context *mc, TSHttpTxn txnp);
24 static void mp4_read_response(Mp4Context *mc, TSHttpTxn txnp);
25 static void mp4_add_transform(Mp4Context *mc, TSHttpTxn txnp);
26 static int mp4_transform_entry(TSCont contp, TSEvent event, void *edata);
27 static int mp4_transform_handler(TSCont contp, Mp4Context *mc);
28 static int mp4_parse_meta(Mp4TransformContext *mtc, bool body_complete);
29
30 TSReturnCode
TSRemapInit(TSRemapInterface * api_info,char * errbuf,int errbuf_size)31 TSRemapInit(TSRemapInterface *api_info, char *errbuf, int errbuf_size)
32 {
33 if (!api_info) {
34 snprintf(errbuf, errbuf_size, "[TSRemapInit] - Invalid TSRemapInterface argument");
35 return TS_ERROR;
36 }
37
38 if (api_info->size < sizeof(TSRemapInterface)) {
39 snprintf(errbuf, errbuf_size, "[TSRemapInit] - Incorrect size of TSRemapInterface structure");
40 return TS_ERROR;
41 }
42
43 return TS_SUCCESS;
44 }
45
46 TSReturnCode
TSRemapNewInstance(int argc,char **,void ** ih,char * errbuf,int errbuf_size)47 TSRemapNewInstance(int argc, char ** /* argv ATS_UNUSED */, void **ih, char *errbuf, int errbuf_size)
48 {
49 if (argc > 2) {
50 snprintf(errbuf, errbuf_size, "[TSRemapNewInstance] - Argument should be removed");
51 }
52
53 *ih = nullptr;
54 return TS_SUCCESS;
55 }
56
57 void
TSRemapDeleteInstance(void *)58 TSRemapDeleteInstance(void * /* ih ATS_UNUSED */)
59 {
60 return;
61 }
62
63 TSRemapStatus
TSRemapDoRemap(void *,TSHttpTxn rh,TSRemapRequestInfo * rri)64 TSRemapDoRemap(void * /* ih ATS_UNUSED */, TSHttpTxn rh, TSRemapRequestInfo *rri)
65 {
66 const char *method, *query, *path;
67 int method_len, query_len, path_len;
68 size_t val_len;
69 const char *val;
70 int ret;
71 float start;
72 char buf[1024];
73 int buf_len;
74 int left, right;
75 TSMLoc ae_field, range_field;
76 TSCont contp;
77 Mp4Context *mc;
78
79 method = TSHttpHdrMethodGet(rri->requestBufp, rri->requestHdrp, &method_len);
80 if (method != TS_HTTP_METHOD_GET) {
81 return TSREMAP_NO_REMAP;
82 }
83
84 // check suffix
85 path = TSUrlPathGet(rri->requestBufp, rri->requestUrl, &path_len);
86
87 if (path == nullptr || path_len <= 4) {
88 return TSREMAP_NO_REMAP;
89
90 } else if (strncasecmp(path + path_len - 4, ".mp4", 4) != 0) {
91 return TSREMAP_NO_REMAP;
92 }
93
94 start = 0;
95 query = TSUrlHttpQueryGet(rri->requestBufp, rri->requestUrl, &query_len);
96
97 val = ts_arg(query, query_len, "start", sizeof("start") - 1, &val_len);
98 if (val != nullptr) {
99 ret = sscanf(val, "%f", &start);
100 if (ret != 1) {
101 start = 0;
102 }
103 }
104
105 if (start == 0) {
106 return TSREMAP_NO_REMAP;
107
108 } else if (start < 0) {
109 TSHttpTxnStatusSet(rh, TS_HTTP_STATUS_BAD_REQUEST);
110 TSHttpTxnErrorBodySet(rh, TSstrdup("Invalid request."), sizeof("Invalid request.") - 1, nullptr);
111 }
112
113 // reset args
114 left = val - sizeof("start") - query;
115 right = query + query_len - val - val_len;
116
117 if (left > 0) {
118 left--;
119 }
120
121 if (left == 0 && right > 0) {
122 right--;
123 }
124
125 buf_len = sprintf(buf, "%.*s%.*s", left, query, right, query + query_len - right);
126 TSUrlHttpQuerySet(rri->requestBufp, rri->requestUrl, buf, buf_len);
127
128 // remove Accept-Encoding
129 ae_field = TSMimeHdrFieldFind(rri->requestBufp, rri->requestHdrp, TS_MIME_FIELD_ACCEPT_ENCODING, TS_MIME_LEN_ACCEPT_ENCODING);
130 if (ae_field) {
131 TSMimeHdrFieldDestroy(rri->requestBufp, rri->requestHdrp, ae_field);
132 TSHandleMLocRelease(rri->requestBufp, rri->requestHdrp, ae_field);
133 }
134
135 // remove Range
136 range_field = TSMimeHdrFieldFind(rri->requestBufp, rri->requestHdrp, TS_MIME_FIELD_RANGE, TS_MIME_LEN_RANGE);
137 if (range_field) {
138 TSMimeHdrFieldDestroy(rri->requestBufp, rri->requestHdrp, range_field);
139 TSHandleMLocRelease(rri->requestBufp, rri->requestHdrp, range_field);
140 }
141
142 mc = new Mp4Context(start);
143 contp = TSContCreate(mp4_handler, nullptr);
144 TSContDataSet(contp, mc);
145
146 TSHttpTxnHookAdd(rh, TS_HTTP_CACHE_LOOKUP_COMPLETE_HOOK, contp);
147 TSHttpTxnHookAdd(rh, TS_HTTP_READ_RESPONSE_HDR_HOOK, contp);
148 TSHttpTxnHookAdd(rh, TS_HTTP_TXN_CLOSE_HOOK, contp);
149 return TSREMAP_NO_REMAP;
150 }
151
152 static int
mp4_handler(TSCont contp,TSEvent event,void * edata)153 mp4_handler(TSCont contp, TSEvent event, void *edata)
154 {
155 TSHttpTxn txnp;
156 Mp4Context *mc;
157
158 txnp = static_cast<TSHttpTxn>(edata);
159 mc = static_cast<Mp4Context *>(TSContDataGet(contp));
160
161 switch (event) {
162 case TS_EVENT_HTTP_CACHE_LOOKUP_COMPLETE:
163 mp4_cache_lookup_complete(mc, txnp);
164 break;
165
166 case TS_EVENT_HTTP_READ_RESPONSE_HDR:
167 mp4_read_response(mc, txnp);
168 break;
169
170 case TS_EVENT_HTTP_TXN_CLOSE:
171 delete mc;
172 TSContDestroy(contp);
173 break;
174
175 default:
176 break;
177 }
178
179 TSHttpTxnReenable(txnp, TS_EVENT_HTTP_CONTINUE);
180 return 0;
181 }
182
183 static void
mp4_cache_lookup_complete(Mp4Context * mc,TSHttpTxn txnp)184 mp4_cache_lookup_complete(Mp4Context *mc, TSHttpTxn txnp)
185 {
186 TSMBuffer bufp;
187 TSMLoc hdrp;
188 TSMLoc cl_field;
189 TSHttpStatus code;
190 int obj_status;
191 int64_t n;
192
193 if (TSHttpTxnCacheLookupStatusGet(txnp, &obj_status) == TS_ERROR) {
194 TSError("[%s] Couldn't get cache status of object", __FUNCTION__);
195 return;
196 }
197
198 if (obj_status != TS_CACHE_LOOKUP_HIT_STALE && obj_status != TS_CACHE_LOOKUP_HIT_FRESH) {
199 return;
200 }
201
202 if (TSHttpTxnCachedRespGet(txnp, &bufp, &hdrp) != TS_SUCCESS) {
203 TSError("[%s] Couldn't get cache resp", __FUNCTION__);
204 return;
205 }
206
207 code = TSHttpHdrStatusGet(bufp, hdrp);
208 if (code != TS_HTTP_STATUS_OK) {
209 goto release;
210 }
211
212 n = 0;
213
214 cl_field = TSMimeHdrFieldFind(bufp, hdrp, TS_MIME_FIELD_CONTENT_LENGTH, TS_MIME_LEN_CONTENT_LENGTH);
215 if (cl_field) {
216 n = TSMimeHdrFieldValueInt64Get(bufp, hdrp, cl_field, -1);
217 TSHandleMLocRelease(bufp, hdrp, cl_field);
218 }
219
220 if (n <= 0) {
221 goto release;
222 }
223
224 mc->cl = n;
225 mp4_add_transform(mc, txnp);
226
227 release:
228
229 TSHandleMLocRelease(bufp, TS_NULL_MLOC, hdrp);
230 }
231
232 static void
mp4_read_response(Mp4Context * mc,TSHttpTxn txnp)233 mp4_read_response(Mp4Context *mc, TSHttpTxn txnp)
234 {
235 TSMBuffer bufp;
236 TSMLoc hdrp;
237 TSMLoc cl_field;
238 TSHttpStatus status;
239 int64_t n;
240
241 if (TSHttpTxnServerRespGet(txnp, &bufp, &hdrp) != TS_SUCCESS) {
242 TSError("[%s] could not get request os data", __FUNCTION__);
243 return;
244 }
245
246 status = TSHttpHdrStatusGet(bufp, hdrp);
247 if (status != TS_HTTP_STATUS_OK) {
248 goto release;
249 }
250
251 n = 0;
252 cl_field = TSMimeHdrFieldFind(bufp, hdrp, TS_MIME_FIELD_CONTENT_LENGTH, TS_MIME_LEN_CONTENT_LENGTH);
253 if (cl_field) {
254 n = TSMimeHdrFieldValueInt64Get(bufp, hdrp, cl_field, -1);
255 TSHandleMLocRelease(bufp, hdrp, cl_field);
256 }
257
258 if (n <= 0) {
259 goto release;
260 }
261
262 mc->cl = n;
263 mp4_add_transform(mc, txnp);
264
265 release:
266
267 TSHandleMLocRelease(bufp, TS_NULL_MLOC, hdrp);
268 }
269
270 static void
mp4_add_transform(Mp4Context * mc,TSHttpTxn txnp)271 mp4_add_transform(Mp4Context *mc, TSHttpTxn txnp)
272 {
273 TSVConn connp;
274
275 if (mc->transform_added) {
276 return;
277 }
278
279 mc->mtc = new Mp4TransformContext(mc->start, mc->cl);
280
281 TSHttpTxnUntransformedRespCache(txnp, 1);
282 TSHttpTxnTransformedRespCache(txnp, 0);
283
284 connp = TSTransformCreate(mp4_transform_entry, txnp);
285 TSContDataSet(connp, mc);
286 TSHttpTxnHookAdd(txnp, TS_HTTP_RESPONSE_TRANSFORM_HOOK, connp);
287
288 mc->transform_added = true;
289 }
290
291 static int
mp4_transform_entry(TSCont contp,TSEvent event,void *)292 mp4_transform_entry(TSCont contp, TSEvent event, void * /* edata ATS_UNUSED */)
293 {
294 TSVIO input_vio;
295 Mp4Context *mc = static_cast<Mp4Context *>(TSContDataGet(contp));
296
297 if (TSVConnClosedGet(contp)) {
298 TSContDestroy(contp);
299 return 0;
300 }
301
302 switch (event) {
303 case TS_EVENT_ERROR:
304 input_vio = TSVConnWriteVIOGet(contp);
305 TSContCall(TSVIOContGet(input_vio), TS_EVENT_ERROR, input_vio);
306 break;
307
308 case TS_EVENT_VCONN_WRITE_COMPLETE:
309 TSVConnShutdown(TSTransformOutputVConnGet(contp), 0, 1);
310 break;
311
312 case TS_EVENT_VCONN_WRITE_READY:
313 default:
314 mp4_transform_handler(contp, mc);
315 break;
316 }
317
318 return 0;
319 }
320
321 static int
mp4_transform_handler(TSCont contp,Mp4Context * mc)322 mp4_transform_handler(TSCont contp, Mp4Context *mc)
323 {
324 TSVConn output_conn;
325 TSVIO input_vio;
326 TSIOBufferReader input_reader;
327 int64_t avail, toread, need, upstream_done;
328 int ret;
329 bool write_down;
330 Mp4TransformContext *mtc;
331
332 mtc = mc->mtc;
333
334 output_conn = TSTransformOutputVConnGet(contp);
335 input_vio = TSVConnWriteVIOGet(contp);
336 input_reader = TSVIOReaderGet(input_vio);
337
338 if (!TSVIOBufferGet(input_vio)) {
339 if (mtc->output.buffer) {
340 TSVIONBytesSet(mtc->output.vio, mtc->total);
341 TSVIOReenable(mtc->output.vio);
342 }
343 return 1;
344 }
345
346 avail = TSIOBufferReaderAvail(input_reader);
347 upstream_done = TSVIONDoneGet(input_vio);
348
349 TSIOBufferCopy(mtc->res_buffer, input_reader, avail, 0);
350 TSIOBufferReaderConsume(input_reader, avail);
351 TSVIONDoneSet(input_vio, upstream_done + avail);
352
353 toread = TSVIONTodoGet(input_vio);
354 write_down = false;
355
356 if (!mtc->parse_over) {
357 ret = mp4_parse_meta(mtc, toread <= 0);
358 if (ret == 0) {
359 goto trans;
360 }
361
362 mtc->parse_over = true;
363 mtc->output.buffer = TSIOBufferCreate();
364 mtc->output.reader = TSIOBufferReaderAlloc(mtc->output.buffer);
365
366 if (ret < 0) {
367 mtc->output.vio = TSVConnWrite(output_conn, contp, mtc->output.reader, mc->cl);
368 mtc->raw_transform = true;
369
370 } else {
371 mtc->output.vio = TSVConnWrite(output_conn, contp, mtc->output.reader, mtc->content_length);
372 }
373 }
374
375 avail = TSIOBufferReaderAvail(mtc->res_reader);
376
377 if (mtc->raw_transform) {
378 if (avail > 0) {
379 TSIOBufferCopy(mtc->output.buffer, mtc->res_reader, avail, 0);
380 TSIOBufferReaderConsume(mtc->res_reader, avail);
381 mtc->total += avail;
382 write_down = true;
383 }
384
385 } else {
386 // copy the new meta data
387 if (mtc->total < mtc->meta_length) {
388 TSIOBufferCopy(mtc->output.buffer, mtc->mm.out_handle.reader, mtc->meta_length, 0);
389 mtc->total += mtc->meta_length;
390 write_down = true;
391 }
392
393 // ignore useless part
394 if (mtc->pos < mtc->tail) {
395 avail = TSIOBufferReaderAvail(mtc->res_reader);
396 need = mtc->tail - mtc->pos;
397 if (need > avail) {
398 need = avail;
399 }
400
401 if (need > 0) {
402 TSIOBufferReaderConsume(mtc->res_reader, need);
403 mtc->pos += need;
404 }
405 }
406
407 // copy the video & audio data
408 if (mtc->pos >= mtc->tail) {
409 avail = TSIOBufferReaderAvail(mtc->res_reader);
410
411 if (avail > 0) {
412 TSIOBufferCopy(mtc->output.buffer, mtc->res_reader, avail, 0);
413 TSIOBufferReaderConsume(mtc->res_reader, avail);
414
415 mtc->pos += avail;
416 mtc->total += avail;
417 write_down = true;
418 }
419 }
420 }
421
422 trans:
423
424 if (write_down) {
425 TSVIOReenable(mtc->output.vio);
426 }
427
428 if (toread > 0) {
429 TSContCall(TSVIOContGet(input_vio), TS_EVENT_VCONN_WRITE_READY, input_vio);
430
431 } else {
432 TSVIONBytesSet(mtc->output.vio, mtc->total);
433 TSContCall(TSVIOContGet(input_vio), TS_EVENT_VCONN_WRITE_COMPLETE, input_vio);
434 }
435
436 return 1;
437 }
438
439 static int
mp4_parse_meta(Mp4TransformContext * mtc,bool body_complete)440 mp4_parse_meta(Mp4TransformContext *mtc, bool body_complete)
441 {
442 int ret;
443 int64_t avail, bytes;
444 TSIOBufferBlock blk;
445 const char *data;
446 Mp4Meta *mm;
447
448 mm = &mtc->mm;
449
450 avail = TSIOBufferReaderAvail(mtc->dup_reader);
451 blk = TSIOBufferReaderStart(mtc->dup_reader);
452
453 while (blk != nullptr) {
454 data = TSIOBufferBlockReadStart(blk, mtc->dup_reader, &bytes);
455 if (bytes > 0) {
456 TSIOBufferWrite(mm->meta_buffer, data, bytes);
457 }
458
459 blk = TSIOBufferBlockNext(blk);
460 }
461
462 TSIOBufferReaderConsume(mtc->dup_reader, avail);
463
464 ret = mm->parse_meta(body_complete);
465
466 if (ret > 0) { // meta success
467 mtc->tail = mm->start_pos;
468 mtc->content_length = mm->content_length;
469 mtc->meta_length = TSIOBufferReaderAvail(mm->out_handle.reader);
470 }
471
472 if (ret != 0) {
473 TSIOBufferReaderFree(mtc->dup_reader);
474 mtc->dup_reader = nullptr;
475 }
476
477 return ret;
478 }
479
480 static char *
ts_arg(const char * param,size_t param_len,const char * key,size_t key_len,size_t * val_len)481 ts_arg(const char *param, size_t param_len, const char *key, size_t key_len, size_t *val_len)
482 {
483 const char *p, *last;
484 const char *val;
485
486 *val_len = 0;
487
488 if (!param || !param_len) {
489 return nullptr;
490 }
491
492 p = param;
493 last = p + param_len;
494
495 for (; p < last; p++) {
496 p = static_cast<char *>(memmem(p, last - p, key, key_len));
497
498 if (p == nullptr) {
499 return nullptr;
500 }
501
502 if ((p == param || *(p - 1) == '&') && *(p + key_len) == '=') {
503 val = p + key_len + 1;
504
505 p = (char *)memchr(p, '&', last - p);
506
507 if (p == nullptr) {
508 p = param + param_len;
509 }
510
511 *val_len = p - val;
512
513 return const_cast<char *>(val);
514 }
515 }
516
517 return nullptr;
518 }
519