1 /** @file
2
3 A brief file description
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
24 /****************************************************************************
25
26 HttpTunnel.h
27
28 Description:
29
30
31 ****************************************************************************/
32
33 #pragma once
34
35 #include "tscore/ink_platform.h"
36 #include "P_EventSystem.h"
37
38 // Get rid of any previous definition first... /leif
39 #ifdef MAX_PRODUCERS
40 #undef MAX_PRODUCERS
41 #endif
42 #ifdef MAX_CONSUMERS
43 #undef MAX_CONSUMERS
44 #endif
45 #define MAX_PRODUCERS 2
46 #define MAX_CONSUMERS 4
47
48 #define HTTP_TUNNEL_EVENT_DONE (HTTP_TUNNEL_EVENTS_START + 1)
49 #define HTTP_TUNNEL_EVENT_PRECOMPLETE (HTTP_TUNNEL_EVENTS_START + 2)
50 #define HTTP_TUNNEL_EVENT_CONSUMER_DETACH (HTTP_TUNNEL_EVENTS_START + 3)
51
52 #define HTTP_TUNNEL_STATIC_PRODUCER (VConnection *)!0
53
54 // YTS Team, yamsat Plugin
55 #define ALLOCATE_AND_WRITE_TO_BUF 1
56 #define WRITE_TO_BUF 2
57
58 struct HttpTunnelProducer;
59 class HttpSM;
60 class HttpPagesHandler;
61 typedef int (HttpSM::*HttpSMHandler)(int event, void *data);
62
63 struct HttpTunnelConsumer;
64 struct HttpTunnelProducer;
65 typedef int (HttpSM::*HttpProducerHandler)(int event, HttpTunnelProducer *p);
66 typedef int (HttpSM::*HttpConsumerHandler)(int event, HttpTunnelConsumer *c);
67
68 enum HttpTunnelType_t { HT_HTTP_SERVER, HT_HTTP_CLIENT, HT_CACHE_READ, HT_CACHE_WRITE, HT_TRANSFORM, HT_STATIC, HT_BUFFER_READ };
69
70 enum TunnelChunkingAction_t {
71 TCA_CHUNK_CONTENT,
72 TCA_DECHUNK_CONTENT,
73 TCA_PASSTHRU_CHUNKED_CONTENT,
74 TCA_PASSTHRU_DECHUNKED_CONTENT
75 };
76
77 struct ChunkedHandler {
78 enum ChunkedState {
79 CHUNK_READ_CHUNK = 0,
80 CHUNK_READ_SIZE_START,
81 CHUNK_READ_SIZE,
82 CHUNK_READ_SIZE_CRLF,
83 CHUNK_READ_TRAILER_BLANK,
84 CHUNK_READ_TRAILER_CR,
85 CHUNK_READ_TRAILER_LINE,
86 CHUNK_READ_ERROR,
87 CHUNK_READ_DONE,
88 CHUNK_WRITE_CHUNK,
89 CHUNK_WRITE_DONE,
90 CHUNK_FLOW_CONTROL
91 };
92
93 static int const DEFAULT_MAX_CHUNK_SIZE = 4096;
94
95 enum Action { ACTION_DOCHUNK = 0, ACTION_DECHUNK, ACTION_PASSTHRU, ACTION_UNSET };
96
97 Action action = ACTION_UNSET;
98
99 IOBufferReader *chunked_reader = nullptr;
100 MIOBuffer *dechunked_buffer = nullptr;
101 int64_t dechunked_size = 0;
102
103 IOBufferReader *dechunked_reader = nullptr;
104 MIOBuffer *chunked_buffer = nullptr;
105 int64_t chunked_size = 0;
106
107 bool truncation = false;
108 int64_t skip_bytes = 0;
109
110 ChunkedState state = CHUNK_READ_CHUNK;
111 int64_t cur_chunk_size = 0;
112 int64_t bytes_left = 0;
113 int last_server_event = VC_EVENT_NONE;
114
115 // Parsing Info
116 int running_sum = 0;
117 int num_digits = 0;
118
119 /// @name Output data.
120 //@{
121 /// The maximum chunk size.
122 /// This is the preferred size as well, used whenever possible.
123 int64_t max_chunk_size;
124 /// Caching members to avoid using printf on every chunk.
125 /// It holds the header for a maximal sized chunk which will cover
126 /// almost all output chunks.
127 char max_chunk_header[16];
128 int max_chunk_header_len = 0;
129 //@}
130 ChunkedHandler();
131
132 void init(IOBufferReader *buffer_in, HttpTunnelProducer *p);
133 void init_by_action(IOBufferReader *buffer_in, Action action);
134 void clear();
135
136 /// Set the max chunk @a size.
137 /// If @a size is zero it is set to @c DEFAULT_MAX_CHUNK_SIZE.
138 void set_max_chunk_size(int64_t size);
139
140 // Returns true if complete, false otherwise
141 bool process_chunked_content();
142 bool generate_chunked_content();
143
144 private:
145 void read_size();
146 void read_chunk();
147 void read_trailer();
148 int64_t transfer_bytes();
149 };
150
151 struct HttpTunnelConsumer {
152 HttpTunnelConsumer();
153
154 LINK(HttpTunnelConsumer, link);
155 HttpTunnelProducer *producer = nullptr;
156 HttpTunnelProducer *self_producer = nullptr;
157
158 HttpTunnelType_t vc_type = HT_HTTP_CLIENT;
159 VConnection *vc = nullptr;
160 IOBufferReader *buffer_reader = nullptr;
161 HttpConsumerHandler vc_handler = nullptr;
162 VIO *write_vio = nullptr;
163
164 int64_t skip_bytes = 0; // bytes to skip at beginning of stream
165 int64_t bytes_written = 0; // total bytes written to the vc
166 int handler_state = 0; // state used the handlers
167
168 bool alive = false;
169 bool write_success = false;
170 const char *name = nullptr;
171
172 /** Check if this consumer is downstream from @a vc.
173 @return @c true if any producer in the tunnel eventually feeds
174 data to this consumer.
175 */
176 bool is_downstream_from(VConnection *vc);
177 /** Check if this is a sink (final data destination).
178 @return @c true if data exits the ATS process at this consumer.
179 */
180 bool is_sink() const;
181 };
182
183 struct HttpTunnelProducer {
184 HttpTunnelProducer();
185
186 DLL<HttpTunnelConsumer> consumer_list;
187 HttpTunnelConsumer *self_consumer = nullptr;
188 VConnection *vc = nullptr;
189 HttpProducerHandler vc_handler = nullptr;
190 VIO *read_vio = nullptr;
191 MIOBuffer *read_buffer = nullptr;
192 IOBufferReader *buffer_start = nullptr;
193 HttpTunnelType_t vc_type = HT_HTTP_SERVER;
194
195 ChunkedHandler chunked_handler;
196 TunnelChunkingAction_t chunking_action = TCA_PASSTHRU_DECHUNKED_CONTENT;
197
198 bool do_chunking = false;
199 bool do_dechunking = false;
200 bool do_chunked_passthru = false;
201
202 int64_t init_bytes_done = 0; // bytes passed in buffer
203 int64_t nbytes = 0; // total bytes (client's perspective)
204 int64_t ntodo = 0; // what this vc needs to do
205 int64_t bytes_read = 0; // total bytes read from the vc
206 int handler_state = 0; // state used the handlers
207 int last_event = 0; ///< Tracking for flow control restarts.
208
209 int num_consumers = 0;
210
211 bool alive = false;
212 bool read_success = false;
213 /// Flag and pointer for active flow control throttling.
214 /// If this is set, it points at the source producer that is under flow control.
215 /// If @c NULL then data flow is not being throttled.
216 HttpTunnelProducer *flow_control_source = nullptr;
217 const char *name = nullptr;
218
219 /** Get the largest number of bytes any consumer has not consumed.
220 Use @a limit if you only need to check if the backlog is at least @a limit.
221 @return The actual backlog or a number at least @a limit.
222 */
223 uint64_t backlog(uint64_t limit = UINT64_MAX ///< More than this is irrelevant
224 );
225 /// Check if producer is original (to ATS) source of data.
226 /// @return @c true if this producer is the source of bytes from outside ATS.
227 bool is_source() const;
228 /// Throttle the flow.
229 void throttle();
230 /// Unthrottle the flow.
231 void unthrottle();
232 /// Check throttled state.
233 bool is_throttled() const;
234
235 /// Update the handler_state member if it is still 0
236 void update_state_if_not_set(int new_handler_state);
237
238 /** Set the flow control source producer for the flow.
239 This sets the value for this producer and all downstream producers.
240 @note This is the implementation for @c throttle and @c unthrottle.
241 @see throttle
242 @see unthrottle
243 */
244 void set_throttle_src(HttpTunnelProducer *srcp ///< Source producer of flow.
245 );
246 };
247
248 class HttpTunnel : public Continuation
249 {
250 friend class HttpPagesHandler;
251 friend class CoreUtils;
252
253 /** Data for implementing flow control across a tunnel.
254
255 The goal is to bound the amount of data buffered for a
256 transaction flowing through the tunnel to (roughly) between the
257 @a high_water and @a low_water water marks. Due to the chunky nater of data
258 flow this always approximate.
259 */
260 struct FlowControl {
261 // Default value for high and low water marks.
262 static uint64_t const DEFAULT_WATER_MARK = 1 << 16;
263
264 uint64_t high_water; ///< Buffered data limit - throttle if more than this.
265 uint64_t low_water; ///< Unthrottle if less than this buffered.
266 bool enabled_p = false; ///< Flow control state (@c false means disabled).
267
268 /// Default constructor.
269 FlowControl();
270 };
271
272 public:
273 HttpTunnel();
274
275 void init(HttpSM *sm_arg, Ptr<ProxyMutex> &amutex);
276 void reset();
277 void kill_tunnel();
278 bool
is_tunnel_active()279 is_tunnel_active() const
280 {
281 return active;
282 }
283 bool is_tunnel_alive() const;
284 bool has_cache_writer() const;
285 bool has_consumer_besides_client() const;
286
287 HttpTunnelProducer *add_producer(VConnection *vc, int64_t nbytes, IOBufferReader *reader_start, HttpProducerHandler sm_handler,
288 HttpTunnelType_t vc_type, const char *name);
289
290 void set_producer_chunking_action(HttpTunnelProducer *p, int64_t skip_bytes, TunnelChunkingAction_t action);
291 /// Set the maximum (preferred) chunk @a size of chunked output for @a producer.
292 void set_producer_chunking_size(HttpTunnelProducer *producer, int64_t size);
293
294 HttpTunnelConsumer *add_consumer(VConnection *vc, VConnection *producer, HttpConsumerHandler sm_handler, HttpTunnelType_t vc_type,
295 const char *name, int64_t skip_bytes = 0);
296
297 int deallocate_buffers();
298 DLL<HttpTunnelConsumer> *get_consumers(VConnection *vc);
299 HttpTunnelProducer *get_producer(VConnection *vc);
300 HttpTunnelConsumer *get_consumer(VConnection *vc);
301 HttpTunnelProducer *get_producer(HttpTunnelType_t type);
302 void tunnel_run(HttpTunnelProducer *p = nullptr);
303
304 int main_handler(int event, void *data);
305 void consumer_reenable(HttpTunnelConsumer *c);
306 bool consumer_handler(int event, HttpTunnelConsumer *c);
307 bool producer_handler(int event, HttpTunnelProducer *p);
308 int producer_handler_dechunked(int event, HttpTunnelProducer *p);
309 int producer_handler_chunked(int event, HttpTunnelProducer *p);
310 void local_finish_all(HttpTunnelProducer *p);
311 void chain_finish_all(HttpTunnelProducer *p);
312 void chain_abort_cache_write(HttpTunnelProducer *p);
313 void chain_abort_all(HttpTunnelProducer *p);
314 void abort_cache_write_finish_others(HttpTunnelProducer *p);
315 void append_message_to_producer_buffer(HttpTunnelProducer *p, const char *msg, int64_t msg_len);
316 int64_t final_consumer_bytes_to_write(HttpTunnelProducer *p, HttpTunnelConsumer *c);
317
318 /** Mark a producer and consumer as the same underlying object.
319
320 This is use to chain producer/consumer pairs together to
321 indicate the data flows through them sequentially. The primary
322 example is a transform which serves as a consumer on the server
323 side and a producer on the cache/client side.
324 */
325 void chain(HttpTunnelConsumer *c, ///< Flow goes in here
326 HttpTunnelProducer *p ///< Flow comes back out here
327 );
328
329 void close_vc(HttpTunnelProducer *p);
330 void close_vc(HttpTunnelConsumer *c);
331
332 private:
333 void internal_error();
334 void finish_all_internal(HttpTunnelProducer *p, bool chain);
335 void update_stats_after_abort(HttpTunnelType_t t);
336 void producer_run(HttpTunnelProducer *p);
337
338 HttpTunnelProducer *get_producer(VIO *vio);
339 HttpTunnelConsumer *get_consumer(VIO *vio);
340
341 HttpTunnelProducer *alloc_producer();
342 HttpTunnelConsumer *alloc_consumer();
343
344 int num_producers = 0;
345 int num_consumers = 0;
346 HttpTunnelConsumer consumers[MAX_CONSUMERS];
347 HttpTunnelProducer producers[MAX_PRODUCERS];
348 HttpSM *sm = nullptr;
349
350 bool active = false;
351
352 /// State data about flow control.
353 FlowControl flow_state;
354
355 private:
356 int reentrancy_count = 0;
357 bool call_sm = false;
358 };
359
360 // void HttpTunnel::abort_cache_write_finish_others
361 //
362 // Abort all downstream cache writes and finsish
363 // all other local consumers
364 //
365 inline void
abort_cache_write_finish_others(HttpTunnelProducer * p)366 HttpTunnel::abort_cache_write_finish_others(HttpTunnelProducer *p)
367 {
368 chain_abort_cache_write(p);
369 local_finish_all(p);
370 }
371
372 // void HttpTunnel::local_finish_all(HttpTunnelProducer* p)
373 //
374 // After the producer has finished, causes direct consumers
375 // to finish their writes
376 //
377 inline void
local_finish_all(HttpTunnelProducer * p)378 HttpTunnel::local_finish_all(HttpTunnelProducer *p)
379 {
380 finish_all_internal(p, false);
381 }
382
383 // void HttpTunnel::chain_finish_all(HttpTunnelProducer* p)
384 //
385 // After the producer has finished, cause everyone
386 // downstream in the tunnel to send everything
387 // that producer has placed in the buffer
388 //
389 inline void
chain_finish_all(HttpTunnelProducer * p)390 HttpTunnel::chain_finish_all(HttpTunnelProducer *p)
391 {
392 finish_all_internal(p, true);
393 }
394
395 inline bool
is_tunnel_alive()396 HttpTunnel::is_tunnel_alive() const
397 {
398 bool tunnel_alive = false;
399
400 for (const auto &producer : producers) {
401 if (producer.alive == true) {
402 tunnel_alive = true;
403 break;
404 }
405 }
406 if (!tunnel_alive) {
407 for (const auto &consumer : consumers) {
408 if (consumer.alive == true) {
409 tunnel_alive = true;
410 break;
411 }
412 }
413 }
414
415 return tunnel_alive;
416 }
417
418 inline HttpTunnelProducer *
get_producer(VConnection * vc)419 HttpTunnel::get_producer(VConnection *vc)
420 {
421 for (int i = 0; i < MAX_PRODUCERS; i++) {
422 if (producers[i].vc == vc) {
423 return producers + i;
424 }
425 }
426 return nullptr;
427 }
428
429 inline HttpTunnelProducer *
get_producer(HttpTunnelType_t type)430 HttpTunnel::get_producer(HttpTunnelType_t type)
431 {
432 for (int i = 0; i < MAX_PRODUCERS; i++) {
433 if (producers[i].vc_type == type) {
434 return producers + i;
435 }
436 }
437 return nullptr;
438 }
439
440 inline HttpTunnelConsumer *
get_consumer(VConnection * vc)441 HttpTunnel::get_consumer(VConnection *vc)
442 {
443 /** Rare but persistent problem in which a @c INKVConnInternal is used by a consumer, released,
444 and then re-allocated for a different consumer. This causes two consumers to have the same VC
445 pointer resulting in this method returning the wrong consumer. Note this is a not a bad use of
446 the tunnel, but an unfortunate interaction with the FIFO free lists.
447
448 It's not correct to check for the consumer being alive - at a minimum `HTTP_TUNNEL_EVENT_DONE`
449 is dispatched against a consumer after the consumer is not alive. Instead if a non-alive
450 consumer matches it is stored as a candidate and returned if no other match is found. If a
451 live matching consumer is found, it is immediately returned. It is never valid to have the
452 same VC in more than one active consumer. This should avoid a performance impact because in
453 the usual case the consumer will be alive.
454
455 In the case of a deliberate dispatch of an event to a dead consumer that has a duplicate vc
456 address, this will select the last consumer which will be correct as the consumers are added
457 in order therefore the latter consumer will be the most recent / appropriate target.
458 */
459 HttpTunnelConsumer *zret = nullptr;
460 for (HttpTunnelConsumer &c : consumers) {
461 if (c.vc == vc) {
462 zret = &c;
463 if (c.alive) { // a match that's alive is always the best.
464 break;
465 }
466 }
467 }
468 return zret;
469 }
470
471 inline HttpTunnelProducer *
get_producer(VIO * vio)472 HttpTunnel::get_producer(VIO *vio)
473 {
474 for (int i = 0; i < MAX_PRODUCERS; i++) {
475 if (producers[i].read_vio == vio) {
476 return producers + i;
477 }
478 }
479 return nullptr;
480 }
481
482 inline HttpTunnelConsumer *
get_consumer(VIO * vio)483 HttpTunnel::get_consumer(VIO *vio)
484 {
485 if (vio) {
486 for (int i = 0; i < MAX_CONSUMERS; i++) {
487 if (consumers[i].alive && consumers[i].write_vio == vio) {
488 return consumers + i;
489 }
490 }
491 }
492 return nullptr;
493 }
494
495 inline void
append_message_to_producer_buffer(HttpTunnelProducer * p,const char * msg,int64_t msg_len)496 HttpTunnel::append_message_to_producer_buffer(HttpTunnelProducer *p, const char *msg, int64_t msg_len)
497 {
498 if (p == nullptr || p->read_buffer == nullptr) {
499 return;
500 }
501
502 p->read_buffer->write(msg, msg_len);
503 p->nbytes += msg_len;
504 p->bytes_read += msg_len;
505 }
506
507 inline bool
has_cache_writer()508 HttpTunnel::has_cache_writer() const
509 {
510 for (const auto &consumer : consumers) {
511 if (consumer.vc_type == HT_CACHE_WRITE && consumer.vc != nullptr) {
512 return true;
513 }
514 }
515 return false;
516 }
517
518 /**
519 Return false if there is only a consumer for client
520 */
521 inline bool
has_consumer_besides_client()522 HttpTunnel::has_consumer_besides_client() const
523 {
524 bool res = false; // case of no consumers
525
526 for (const auto &consumer : consumers) {
527 if (!consumer.alive) {
528 continue;
529 }
530
531 if (consumer.vc_type == HT_HTTP_CLIENT) {
532 res = false;
533 continue;
534 } else {
535 res = true;
536 break;
537 }
538 }
539
540 return res;
541 }
542
543 inline bool
is_downstream_from(VConnection * vc)544 HttpTunnelConsumer::is_downstream_from(VConnection *vc)
545 {
546 HttpTunnelProducer *p = producer;
547
548 while (p) {
549 if (p->vc == vc) {
550 return true;
551 }
552 // The producer / consumer chain can contain a cycle in the case
553 // of a blind tunnel so give up if we find ourself (the original
554 // consumer).
555 HttpTunnelConsumer *c = p->self_consumer;
556
557 p = (c && c != this) ? c->producer : nullptr;
558 }
559 return false;
560 }
561
562 inline bool
is_sink()563 HttpTunnelConsumer::is_sink() const
564 {
565 return HT_HTTP_CLIENT == vc_type || HT_CACHE_WRITE == vc_type;
566 }
567
568 inline bool
is_source()569 HttpTunnelProducer::is_source() const
570 {
571 // If a producer is marked as a client, then it's part of a bidirectional tunnel
572 // and so is an actual source of data.
573 return HT_HTTP_SERVER == vc_type || HT_CACHE_READ == vc_type || HT_HTTP_CLIENT == vc_type;
574 }
575
576 inline void
update_state_if_not_set(int new_handler_state)577 HttpTunnelProducer::update_state_if_not_set(int new_handler_state)
578 {
579 if (this->handler_state == 0) {
580 this->handler_state = new_handler_state;
581 }
582 }
583
584 inline bool
is_throttled()585 HttpTunnelProducer::is_throttled() const
586 {
587 return nullptr != flow_control_source;
588 }
589
590 inline void
throttle()591 HttpTunnelProducer::throttle()
592 {
593 if (!this->is_throttled()) {
594 this->set_throttle_src(this);
595 }
596 }
597
598 inline void
unthrottle()599 HttpTunnelProducer::unthrottle()
600 {
601 if (this->is_throttled()) {
602 this->set_throttle_src(nullptr);
603 }
604 }
605
FlowControl()606 inline HttpTunnel::FlowControl::FlowControl() : high_water(DEFAULT_WATER_MARK), low_water(DEFAULT_WATER_MARK) {}
607