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