~ps10gel/ubuntu/xenial/trafficserver/6.2.0

« back to all changes in this revision

Viewing changes to proxy/Update.cc

  • Committer: Bazaar Package Importer
  • Author(s): Arno Toell
  • Date: 2011-01-13 11:49:18 UTC
  • Revision ID: james.westby@ubuntu.com-20110113114918-vu422h8dknrgkj15
Tags: upstream-2.1.5-unstable
ImportĀ upstreamĀ versionĀ 2.1.5-unstable

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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
#include "libts.h"
 
25
 
 
26
#include "Main.h"
 
27
#include "Update.h"
 
28
#include "ProxyConfig.h"
 
29
#include "StatSystem.h"
 
30
#include "HttpUpdateSM.h"
 
31
#include "HttpDebugNames.h"
 
32
#include "URL.h"
 
33
#include "HdrUtils.h"
 
34
//#include "MixtAPIInternal.h"
 
35
 
 
36
RecRawStatBlock *update_rsb;
 
37
 
 
38
#define UpdateEstablishStaticConfigInteger(_ix,_n) \
 
39
  REC_EstablishStaticConfigInteger(_ix,_n); \
 
40
 
 
41
#define UPDATE_INCREMENT_DYN_STAT(x) \
 
42
        RecIncrRawStat(update_rsb, mutex->thread_holding, (int) x, 1);
 
43
#define UPDATE_DECREMENT_DYN_STAT(x) \
 
44
        RecIncrRawStat(update_rsb, mutex->thread_holding, (int) x, -1);
 
45
#define UPDATE_READ_DYN_STAT(x, C, S) \
 
46
        RecGetRawStatCount(update_rsb, (int) x, &C); \
 
47
        RecGetRawStatSum(update_rsb, (int) x, &S);
 
48
 
 
49
#define UPDATE_CLEAR_DYN_STAT(x) \
 
50
do { \
 
51
        RecSetRawStatSum(update_rsb, x, 0); \
 
52
        RecSetRawStatCount(update_rsb, x, 0); \
 
53
} while (0);
 
54
 
 
55
#define UPDATE_ConfigReadInteger         REC_ConfigReadInteger
 
56
#define UPDATE_ConfigReadString          REC_ConfigReadString
 
57
#define UPDATE_RegisterConfigUpdateFunc  REC_RegisterConfigUpdateFunc
 
58
 
 
59
 
 
60
 
 
61
// Fundamental constants
 
62
 
 
63
static const char *const GET_METHOD = "GET ";
 
64
static const char *const HTTP_VERSION = " HTTP/1.0";
 
65
static const char *const REQUEST_TERMINATOR = "\r\n\r\n";
 
66
static const char *const TERMINATOR = "\r\n";
 
67
static const char *const HTML_COMMENT_TAG = "!--";
 
68
static const char *const HTML_COMMENT_END = "-->";
 
69
static const int MAX_LINE_LENGTH = (32 * 1024);
 
70
 
 
71
// Fundamental constants initialized by UpdateManager::start()
 
72
 
 
73
static int len_GET_METHOD = 0;
 
74
static int len_HTTP_VERSION = 0;
 
75
static int len_REQUEST_TERMINATOR = 0;
 
76
static int len_TERMINATOR = 0;
 
77
 
 
78
struct html_tag update_allowable_html_tags[] = {
 
79
  {"a", "href"},
 
80
  {"img", "src"},
 
81
  {"img", "href"},
 
82
  {"body", "background"},
 
83
  {"frame", "src"},
 
84
  {"iframe", "src"},
 
85
  {"fig", "src"},
 
86
  {"overlay", "src"},
 
87
  {"applet", "code"},
 
88
  {"script", "src"},
 
89
  {"embed", "src"},
 
90
  {"bgsound", "src"},
 
91
  {"area", "href"},
 
92
  {"base", "href"},             // special handling
 
93
  {"meta", "content"},          // special handling
 
94
  {NULL, NULL}
 
95
};
 
96
 
 
97
struct schemes_descriptor
 
98
{
 
99
  const char *tag;
 
100
  int tag_len;
 
101
};
 
102
 
 
103
struct schemes_descriptor proto_schemes[] = {
 
104
  {"cid:", 0},
 
105
  {"clsid:", 0},
 
106
  {"file:", 0},
 
107
  {"finger:", 0},
 
108
  {"ftp:", 0},
 
109
  {"gopher:", 0},
 
110
  {"hdl:", 0},
 
111
  {"http:", 0},
 
112
  {"https:", 0},
 
113
  {"ilu:", 0},
 
114
  {"ior:", 0},
 
115
  {"irc:", 0},
 
116
  {"java:", 0},
 
117
  {"javascript:", 0},
 
118
  {"lifn:", 0},
 
119
  {"mailto:", 0},
 
120
  {"mid:", 0},
 
121
  {"news:", 0},
 
122
  {"path:", 0},
 
123
  {"prospero:", 0},
 
124
  {"rlogin:", 0},
 
125
  {"service:", 0},
 
126
  {"shttp:", 0},
 
127
  {"snews:", 0},
 
128
  {"stanf:", 0},
 
129
  {"telnet:", 0},
 
130
  {"tn3270:", 0},
 
131
  {"wais:", 0},
 
132
  {"whois++:", 0},
 
133
  {NULL, 0}
 
134
};
 
135
 
 
136
struct schemes_descriptor supported_proto_schemes[] = {
 
137
  {"http:",},
 
138
  {NULL, 0}
 
139
};
 
140
 
 
141
static int global_id = 1;
 
142
 
 
143
void
 
144
init_proto_schemes()
 
145
{
 
146
  int n;
 
147
  for (n = 0; proto_schemes[n].tag; ++n) {
 
148
    proto_schemes[n].tag_len = strlen(proto_schemes[n].tag);
 
149
  }
 
150
}
 
151
 
 
152
void
 
153
init_supported_proto_schemes()
 
154
{
 
155
  int n;
 
156
  for (n = 0; supported_proto_schemes[n].tag; ++n) {
 
157
    supported_proto_schemes[n].tag_len = strlen(supported_proto_schemes[n].tag);
 
158
  }
 
159
}
 
160
 
 
161
///////////////////////////////////////////////////////////////////////////////
 
162
// Class UpdateConfigParams
 
163
//      Global subsystem configuration parameters
 
164
///////////////////////////////////////////////////////////////////////////////
 
165
 
 
166
UpdateConfigParams::UpdateConfigParams():
 
167
_enabled(0), _immediate_update(0), _retry_count(0),
 
168
_retry_interval(0), _concurrent_updates(0), _max_update_state_machines(0), _memory_use_in_mb(0)
 
169
{
 
170
}
 
171
 
 
172
UpdateConfigParams::UpdateConfigParams(UpdateConfigParams & p)
 
173
{
 
174
  _enabled = p._enabled;
 
175
  _immediate_update = p._immediate_update;
 
176
  _retry_count = p._retry_count;
 
177
  _retry_interval = p._retry_interval;
 
178
  _concurrent_updates = p._concurrent_updates;
 
179
  _max_update_state_machines = p._max_update_state_machines;
 
180
  _memory_use_in_mb = p._memory_use_in_mb;
 
181
}
 
182
 
 
183
UpdateConfigParams::~UpdateConfigParams()
 
184
{
 
185
}
 
186
 
 
187
UpdateConfigParams & UpdateConfigParams::operator=(UpdateConfigParams & p)
 
188
{
 
189
  _enabled = p._enabled;
 
190
  _immediate_update = p._immediate_update;
 
191
  _retry_count = p._retry_count;
 
192
  _retry_interval = p._retry_interval;
 
193
  _concurrent_updates = p._concurrent_updates;
 
194
  _max_update_state_machines = p._max_update_state_machines;
 
195
  _memory_use_in_mb = p._memory_use_in_mb;
 
196
  return *this;
 
197
}
 
198
 
 
199
int
 
200
UpdateConfigParams::operator==(UpdateConfigParams & p)
 
201
{
 
202
  if (_enabled != p._enabled)
 
203
    return 0;
 
204
  if (_immediate_update != p._immediate_update)
 
205
    return 0;
 
206
  if (_retry_count != p._retry_count)
 
207
    return 0;
 
208
  if (_retry_interval != p._retry_interval)
 
209
    return 0;
 
210
  if (_concurrent_updates != p._concurrent_updates)
 
211
    return 0;
 
212
  if (_max_update_state_machines != p._max_update_state_machines)
 
213
    return 0;
 
214
  if (_memory_use_in_mb != p._memory_use_in_mb)
 
215
    return 0;
 
216
  return 1;
 
217
}
 
218
 
 
219
/////////////////////////////////////////////////////////////////////////////
 
220
// Class UpdateEntry
 
221
//      Per update object descriptor
 
222
/////////////////////////////////////////////////////////////////////////////
 
223
 
 
224
UpdateEntry::UpdateEntry():_group_link(0), _hash_link(0), _id(0), _url(0),
 
225
_URLhandle(), _terminal_url(0),
 
226
_request_headers(0), _num_request_headers(0),
 
227
_http_hdr(0),
 
228
_offset_hour(0), _interval(0), _max_depth(0), _start_time(0), _expired(0), _scheme_index(-1), _update_event_status(0)
 
229
{
 
230
  http_parser_init(&_http_parser);
 
231
}
 
232
 
 
233
UpdateEntry::~UpdateEntry()
 
234
{
 
235
  if (_url) {
 
236
    xfree(_url);
 
237
    _url = NULL;
 
238
  }
 
239
  if (_URLhandle.valid()) {
 
240
    _URLhandle.destroy();
 
241
  }
 
242
  if (_request_headers) {
 
243
    xfree(_request_headers);
 
244
    _request_headers = NULL;
 
245
  }
 
246
  // INKqa12891: _http_hdr can be NULL
 
247
  if (_http_hdr && _http_hdr->valid()) {
 
248
    _http_hdr->destroy();
 
249
    delete _http_hdr;
 
250
    _http_hdr = NULL;
 
251
  }
 
252
  _indirect_list = NULL;
 
253
}
 
254
 
 
255
void
 
256
UpdateEntry::Init(int derived_url)
 
257
{
 
258
  _id = ink_atomic_increment(&global_id, 1);
 
259
  if (derived_url) {
 
260
    return;
 
261
  }
 
262
  ComputeScheduleTime();
 
263
 
 
264
  int scheme_len;
 
265
  const char *scheme = _URLhandle.scheme_get(&scheme_len);
 
266
  if (scheme != URL_SCHEME_HTTP) {
 
267
    // Depth is only valid for scheme "http"
 
268
    _max_depth = 0;
 
269
  }
 
270
 
 
271
}
 
272
 
 
273
int
 
274
UpdateEntry::ValidURL(char *s, char *e)
 
275
{
 
276
  // Note: string 's' is null terminated.
 
277
 
 
278
  const char *url_start = s;
 
279
  char *url_end = e;
 
280
  int err;
 
281
 
 
282
  _URLhandle.create(NULL);
 
283
  err = _URLhandle.parse(&url_start, url_end);
 
284
  if (err >= 0) {
 
285
    _url = xstrdup(s);
 
286
    return 0;                   // Valid URL
 
287
  } else {
 
288
    _URLhandle.destroy();
 
289
    return 1;                   // Invalid URL
 
290
  }
 
291
  return 0;
 
292
}
 
293
 
 
294
int
 
295
UpdateEntry::ValidHeaders(char *s, char *e)
 
296
{
 
297
  NOWARN_UNUSED(e);
 
298
  // Note: string 's' is null terminated.
 
299
 
 
300
  enum
 
301
  {
 
302
    FIND_START_OF_HEADER_NAME = 1,
 
303
    SCAN_FOR_HEADER_NAME,
 
304
    SCAN_FOR_END_OF_HEADER_VALUE
 
305
  };
 
306
 
 
307
  char *p = s;
 
308
  char *t;
 
309
  int bad_header = 0;
 
310
  int end_of_headers = 0;
 
311
  int scan_state = FIND_START_OF_HEADER_NAME;
 
312
 
 
313
  while (*p) {
 
314
    switch (scan_state) {
 
315
    case FIND_START_OF_HEADER_NAME:
 
316
      {
 
317
        if (!ValidHeaderNameChar(*p)) {
 
318
          bad_header = 1;
 
319
          break;
 
320
        } else {
 
321
          scan_state = SCAN_FOR_HEADER_NAME;
 
322
          break;
 
323
        }
 
324
      }
 
325
    case SCAN_FOR_HEADER_NAME:
 
326
      {
 
327
        if (!ValidHeaderNameChar(*p)) {
 
328
          if (*p == ':') {
 
329
            scan_state = SCAN_FOR_END_OF_HEADER_VALUE;
 
330
            break;
 
331
          } else {
 
332
            bad_header = 1;
 
333
            break;
 
334
          }
 
335
        } else {
 
336
          // Get next char
 
337
          break;
 
338
        }
 
339
      }
 
340
    case SCAN_FOR_END_OF_HEADER_VALUE:
 
341
      {
 
342
        t = strchr(p, '\r');
 
343
        if (t) {
 
344
          if (*(t + 1) == '\n') {
 
345
            p = t + 1;
 
346
            ++_num_request_headers;
 
347
            scan_state = FIND_START_OF_HEADER_NAME;
 
348
            break;
 
349
          } else {
 
350
            bad_header = 1;
 
351
            break;
 
352
          }
 
353
        } else {
 
354
          t = strchr(p, 0);
 
355
          if (t) {
 
356
            ++_num_request_headers;
 
357
            end_of_headers = 1;
 
358
          } else {
 
359
            bad_header = 1;
 
360
          }
 
361
          break;
 
362
        }
 
363
      }
 
364
    }                           // End of switch
 
365
 
 
366
    if (bad_header) {
 
367
      if (_num_request_headers) {
 
368
        return 1;               // Fail; Bad header with > 1 valid headers
 
369
      } else {
 
370
        if (p == s) {
 
371
          return 0;             // OK; user specified no headers
 
372
        } else {
 
373
          return 1;             // Fail; first header is invalid
 
374
        }
 
375
      }
 
376
    } else {
 
377
      if (end_of_headers) {
 
378
        break;
 
379
      } else {
 
380
        ++p;
 
381
      }
 
382
    }
 
383
  }
 
384
 
 
385
  // At least 1 valid header exists
 
386
 
 
387
  _request_headers = xstrdup(s);
 
388
  return 0;                     // OK; > 1 valid headers
 
389
}
 
390
 
 
391
int
 
392
UpdateEntry::BuildHttpRequest()
 
393
{
 
394
  // Given the HTTP request and associated headers,
 
395
  // transform the data into a HTTPHdr object.
 
396
 
 
397
  char request[MAX_LINE_LENGTH];
 
398
  int request_size;
 
399
 
 
400
  request_size = len_GET_METHOD + strlen(_url) +
 
401
    len_HTTP_VERSION + (_request_headers ? len_TERMINATOR + strlen(_request_headers) : 0) + len_REQUEST_TERMINATOR + 1;
 
402
  if (request_size > MAX_LINE_LENGTH) {
 
403
    return 1;
 
404
  }
 
405
  if (_request_headers) {
 
406
    snprintf(request, sizeof(request), "%s%s%s%s%s%s", GET_METHOD, _url,
 
407
             HTTP_VERSION, TERMINATOR, _request_headers, REQUEST_TERMINATOR);
 
408
  } else {
 
409
    snprintf(request, sizeof(request), "%s%s%s%s", GET_METHOD, _url, HTTP_VERSION, REQUEST_TERMINATOR);
 
410
  }
 
411
  _http_hdr = NEW(new HTTPHdr);
 
412
  http_parser_init(&_http_parser);
 
413
  _http_hdr->create(HTTP_TYPE_REQUEST);
 
414
  int err;
 
415
  const char *start = request;
 
416
  const char *end = start + request_size - 1;
 
417
 
 
418
  while (start < end) {
 
419
    err = _http_hdr->parse_req(&_http_parser, &start, end, false);
 
420
    if (err != PARSE_CONT) {
 
421
      break;
 
422
    }
 
423
    end = start + strlen(start);
 
424
  }
 
425
  http_parser_clear(&_http_parser);
 
426
  return 0;
 
427
}
 
428
 
 
429
int
 
430
UpdateEntry::ValidHeaderNameChar(char c)
 
431
{
 
432
  if ((c > 31) && (c < 127)) {
 
433
    if (ValidSeparatorChar(c)) {
 
434
      return 0;                 // Invalid
 
435
    } else {
 
436
      return 1;                 // Valid
 
437
    }
 
438
  } else {
 
439
    return 0;                   // Invalid
 
440
  }
 
441
}
 
442
 
 
443
int
 
444
UpdateEntry::ValidSeparatorChar(char c)
 
445
{
 
446
  switch (c) {
 
447
  case '(':
 
448
  case ')':
 
449
  case '<':
 
450
  case '>':
 
451
  case '@':
 
452
  case ',':
 
453
  case ';':
 
454
  case ':':
 
455
  case '\\':
 
456
  case '"':
 
457
  case '/':
 
458
  case '[':
 
459
  case ']':
 
460
  case '?':
 
461
  case '=':
 
462
  case '{':
 
463
  case '}':
 
464
  case ' ':
 
465
  case '\t':
 
466
    return 1;                   // Valid separator char
 
467
  default:
 
468
    return 0;
 
469
  }
 
470
}
 
471
 
 
472
int
 
473
UpdateEntry::ValidHour(char *s, char *e)
 
474
{
 
475
  NOWARN_UNUSED(e);
 
476
  // Note: string 's' is null terminated.
 
477
 
 
478
  _offset_hour = atoi(s);
 
479
  if ((_offset_hour >= MIN_OFFSET_HOUR) && (_offset_hour <= MAX_OFFSET_HOUR)) {
 
480
    return 0;                   // Valid data
 
481
  } else {
 
482
    return 1;                   // Invalid data
 
483
  }
 
484
}
 
485
 
 
486
int
 
487
UpdateEntry::ValidInterval(char *s, char *e)
 
488
{
 
489
  NOWARN_UNUSED(e);
 
490
  // Note: string 's' is null terminated.
 
491
 
 
492
  _interval = atoi(s);
 
493
  if ((_interval >= MIN_INTERVAL) && (_interval <= MAX_INTERVAL)) {
 
494
    return 0;                   // Valid data
 
495
  } else {
 
496
    return 1;                   // Invalid data
 
497
  }
 
498
  return 0;
 
499
}
 
500
 
 
501
int
 
502
UpdateEntry::ValidDepth(char *s, char *e)
 
503
{
 
504
  NOWARN_UNUSED(e);
 
505
  // Note: string 's' is null terminated.
 
506
 
 
507
  _max_depth = atoi(s);
 
508
  if ((_max_depth >= MIN_DEPTH) && (_max_depth <= MAX_DEPTH)) {
 
509
    return 0;                   // Valid data
 
510
  } else {
 
511
    return 1;                   // Invalid data
 
512
  }
 
513
  return 0;
 
514
}
 
515
 
 
516
void
 
517
UpdateEntry::SetTerminalStatus(int term_url)
 
518
{
 
519
  _terminal_url = term_url;
 
520
}
 
521
 
 
522
int
 
523
UpdateEntry::TerminalURL()
 
524
{
 
525
  return _terminal_url;
 
526
}
 
527
 
 
528
 
 
529
void
 
530
UpdateEntry::ComputeScheduleTime()
 
531
{
 
532
  ink_hrtime ht;
 
533
  time_t cur_time;
 
534
  time_t start_time_delta;
 
535
  struct tm cur_tm;
 
536
 
 
537
  if (_expired) {
 
538
    _expired = 0;
 
539
  } else {
 
540
    if (_start_time) {
 
541
      return;
 
542
    }
 
543
  }
 
544
  ht = ink_get_based_hrtime();
 
545
  cur_time = ht / HRTIME_SECOND;
 
546
  ink_localtime_r(&cur_time, &cur_tm);
 
547
 
 
548
  if (!_start_time) {
 
549
    // Initial case
 
550
    if (cur_tm.tm_hour == _offset_hour) {
 
551
      start_time_delta = 24 * SECONDS_PER_HOUR;
 
552
 
 
553
    } else if (cur_tm.tm_hour < _offset_hour) {
 
554
      start_time_delta = (_offset_hour - cur_tm.tm_hour) * SECONDS_PER_HOUR;
 
555
 
 
556
    } else {
 
557
      start_time_delta = ((24 - cur_tm.tm_hour) + _offset_hour) * SECONDS_PER_HOUR;
 
558
    }
 
559
    start_time_delta -= ((cur_tm.tm_min * SECONDS_PER_MIN) + cur_tm.tm_sec);
 
560
    _start_time = cur_time + start_time_delta;
 
561
 
 
562
  } else {
 
563
    // Compute next start time
 
564
    _start_time += _interval;
 
565
  }
 
566
}
 
567
 
 
568
int
 
569
UpdateEntry::ScheduleNow(time_t cur_time)
 
570
{
 
571
  if (cur_time >= _start_time) {
 
572
    _expired = 1;
 
573
    return 1;
 
574
  } else {
 
575
    return 0;
 
576
  }
 
577
}
 
578
 
 
579
/////////////////////////////////////////////////////////////////////////////
 
580
// Class UpdateConfigList
 
581
//      Container for UpdateEntry objects
 
582
/////////////////////////////////////////////////////////////////////////////
 
583
UpdateConfigList::UpdateConfigList():_entry_q_elements(0), _pending_q_elements(0), _hash_table(0)
 
584
{
 
585
}
 
586
 
 
587
UpdateConfigList::~UpdateConfigList()
 
588
{
 
589
  if (_hash_table) {
 
590
    delete[]_hash_table;
 
591
    _hash_table = NULL;
 
592
  }
 
593
}
 
594
 
 
595
void
 
596
UpdateConfigList::Add(UpdateEntry * e)
 
597
{
 
598
  _entry_q_elements++;
 
599
  _entry_q.enqueue(e);
 
600
}
 
601
 
 
602
int
 
603
UpdateConfigList::HashAdd(UpdateEntry * e)
 
604
{
 
605
  uint64_t folded64 = e->_url_md5.fold();
 
606
  ink_assert(folded64);
 
607
  int32_t index = folded64 % HASH_TABLE_SIZE;
 
608
 
 
609
  if (!_hash_table) {
 
610
    // One time initialization
 
611
 
 
612
    _hash_table = NEW(new UpdateEntry *[HASH_TABLE_SIZE]);
 
613
    memset((char *) _hash_table, 0, (sizeof(UpdateEntry *) * HASH_TABLE_SIZE));
 
614
  }
 
615
  // Add to hash table only if unique
 
616
 
 
617
  UpdateEntry *he = _hash_table[index];
 
618
  UpdateEntry **last_link = &_hash_table[index];
 
619
 
 
620
  while (he) {
 
621
    if (e->_url_md5 == he->_url_md5) {
 
622
      return 1;                 // duplicate detected
 
623
    } else {
 
624
      last_link = &he->_hash_link;
 
625
      he = he->_hash_link;
 
626
    }
 
627
  }
 
628
 
 
629
  // Entry is unique, add to hash list
 
630
 
 
631
  e->_hash_link = *last_link;
 
632
  *last_link = e;
 
633
 
 
634
  // Add to entry queue
 
635
 
 
636
  Add(e);
 
637
 
 
638
  return 0;                     // Entry added
 
639
}
 
640
 
 
641
UpdateEntry *
 
642
UpdateConfigList::Remove()
 
643
{
 
644
  UpdateEntry *e = _entry_q.dequeue();
 
645
  if (e) {
 
646
    _entry_q_elements--;
 
647
  }
 
648
  return e;
 
649
}
 
650
 
 
651
void
 
652
UpdateConfigList::AddPending(UpdateEntry * e)
 
653
{
 
654
  _pending_q_elements++;
 
655
  _pending_q.enqueue(e);
 
656
}
 
657
 
 
658
UpdateEntry *
 
659
UpdateConfigList::RemovePending()
 
660
{
 
661
  UpdateEntry *e = _pending_q.dequeue();
 
662
  if (e) {
 
663
    _pending_q_elements--;
 
664
  }
 
665
  return e;
 
666
}
 
667
 
 
668
/////////////////////////////////////////////////////////////////////////////
 
669
// Class UpdateManager
 
670
//      External interface to Update subsystem
 
671
/////////////////////////////////////////////////////////////////////////////
 
672
 
 
673
UpdateManager::UpdateManager():_CM(0), _SCH(0)
 
674
{
 
675
}
 
676
 
 
677
UpdateManager::~UpdateManager()
 
678
{
 
679
}
 
680
 
 
681
int
 
682
UpdateManager::start()
 
683
{
 
684
  // Initialize fundamental constants
 
685
 
 
686
  len_GET_METHOD = strlen(GET_METHOD);
 
687
  len_HTTP_VERSION = strlen(HTTP_VERSION);
 
688
  len_REQUEST_TERMINATOR = strlen(REQUEST_TERMINATOR);
 
689
  len_TERMINATOR = strlen(TERMINATOR);
 
690
  init_proto_schemes();
 
691
  init_supported_proto_schemes();
 
692
 
 
693
  _CM = NEW(new UpdateConfigManager);
 
694
  _CM->init();
 
695
 
 
696
  _SCH = NEW(new UpdateScheduler(_CM));
 
697
  _SCH->Init();
 
698
 
 
699
  return 0;
 
700
}
 
701
 
 
702
UpdateManager updateManager;
 
703
 
 
704
typedef int (UpdateConfigManager::*UpdateConfigManagerContHandler) (int, void *);
 
705
/////////////////////////////////////////////////////////////////////////////
 
706
// Class UpdateConfigManager
 
707
//      Handle Update subsystem global configuration and URL list updates
 
708
/////////////////////////////////////////////////////////////////////////////
 
709
UpdateConfigManager::UpdateConfigManager()
 
710
:Continuation(new_ProxyMutex()), _periodic_event(0), _filename(0)
 
711
{
 
712
  SET_HANDLER((UpdateConfigManagerContHandler)
 
713
              & UpdateConfigManager::ProcessUpdate);
 
714
}
 
715
 
 
716
UpdateConfigManager::~UpdateConfigManager()
 
717
{
 
718
}
 
719
 
 
720
static RecInt local_http_server_port = 0;
 
721
 
 
722
int
 
723
UpdateConfigManager::init()
 
724
{
 
725
  update_rsb = RecAllocateRawStatBlock((int) update_stat_count);
 
726
 
 
727
  UpdateEstablishStaticConfigInteger(local_http_server_port, "proxy.config.http.server_port");
 
728
 
 
729
  _CP_actual = NEW(new UpdateConfigParams);
 
730
 
 
731
  // Setup update handlers for each global configuration parameter
 
732
 
 
733
  UpdateEstablishStaticConfigInteger(_CP_actual->_enabled, "proxy.config.update.enabled");
 
734
 
 
735
  UpdateEstablishStaticConfigInteger(_CP_actual->_immediate_update, "proxy.config.update.force");
 
736
 
 
737
  UpdateEstablishStaticConfigInteger(_CP_actual->_retry_count, "proxy.config.update.retry_count");
 
738
 
 
739
  UpdateEstablishStaticConfigInteger(_CP_actual->_retry_interval, "proxy.config.update.retry_interval");
 
740
 
 
741
  UpdateEstablishStaticConfigInteger(_CP_actual->_concurrent_updates, "proxy.config.update.concurrent_updates");
 
742
 
 
743
  UpdateEstablishStaticConfigInteger(_CP_actual->_max_update_state_machines,
 
744
                                     "proxy.config.update.max_update_state_machines");
 
745
 
 
746
  UpdateEstablishStaticConfigInteger(_CP_actual->_memory_use_in_mb, "proxy.config.update.memory_use_mb");
 
747
 
 
748
  // Register Scheduled Update stats
 
749
 
 
750
  RecRegisterRawStat(update_rsb, RECT_PROCESS,
 
751
                     "proxy.process.update.successes",
 
752
                     RECD_INT, RECP_NON_PERSISTENT, (int) update_successes_stat, RecRawStatSyncCount);
 
753
  UPDATE_CLEAR_DYN_STAT(update_successes_stat);
 
754
 
 
755
  RecRegisterRawStat(update_rsb, RECT_PROCESS,
 
756
                     "proxy.process.update.no_actions",
 
757
                     RECD_INT, RECP_NON_PERSISTENT, (int) update_no_actions_stat, RecRawStatSyncCount);
 
758
  UPDATE_CLEAR_DYN_STAT(update_no_actions_stat);
 
759
 
 
760
  RecRegisterRawStat(update_rsb, RECT_PROCESS,
 
761
                     "proxy.process.update.fails",
 
762
                     RECD_INT, RECP_NON_PERSISTENT, (int) update_fails_stat, RecRawStatSyncCount);
 
763
  UPDATE_CLEAR_DYN_STAT(update_fails_stat);
 
764
 
 
765
  RecRegisterRawStat(update_rsb, RECT_PROCESS,
 
766
                     "proxy.process.update.unknown_status",
 
767
                     RECD_INT, RECP_NON_PERSISTENT, (int) update_unknown_status_stat, RecRawStatSyncCount);
 
768
  UPDATE_CLEAR_DYN_STAT(update_unknown_status_stat);
 
769
 
 
770
  RecRegisterRawStat(update_rsb, RECT_PROCESS,
 
771
                     "proxy.process.update.state_machines",
 
772
                     RECD_INT, RECP_NON_PERSISTENT, (int) update_state_machines_stat, RecRawStatSyncCount);
 
773
  UPDATE_CLEAR_DYN_STAT(update_state_machines_stat);
 
774
 
 
775
  Debug("update",
 
776
        "Update params: enable %d force %d rcnt %d rint %d updates %d "
 
777
        "max_sm %d mem %d",
 
778
        _CP_actual->_enabled, _CP_actual->_immediate_update,
 
779
        _CP_actual->_retry_count, _CP_actual->_retry_interval,
 
780
        _CP_actual->_concurrent_updates, _CP_actual->_max_update_state_machines, _CP_actual->_memory_use_in_mb);
 
781
 
 
782
  // Make working and actual global config copies equal
 
783
 
 
784
  _CP = NEW(new UpdateConfigParams(*_CP_actual));
 
785
 
 
786
  // Setup "update.config" update handler
 
787
 
 
788
  SetFileName((char *) "update.config");
 
789
  REC_RegisterConfigUpdateFunc("proxy.config.update.update_configuration", URL_list_update_callout, (void *) this);
 
790
 
 
791
  // Simulate configuration update to sync working and current databases
 
792
 
 
793
  handleEvent(EVENT_IMMEDIATE, (Event *) NULL);
 
794
 
 
795
  // Setup periodic to detect global config updates
 
796
 
 
797
  _periodic_event = eventProcessor.schedule_every(this, HRTIME_SECONDS(10));
 
798
 
 
799
  return 0;
 
800
}
 
801
 
 
802
int
 
803
UpdateConfigManager::GetConfigParams(Ptr<UpdateConfigParams> *P)
 
804
{
 
805
  MUTEX_TRY_LOCK(lock, mutex, this_ethread());
 
806
  if (!lock) {
 
807
    return 0;                   // Try again later
 
808
  } else {
 
809
    *P = _CP;
 
810
    return 1;                   // Success
 
811
  }
 
812
}
 
813
 
 
814
int
 
815
UpdateConfigManager::GetConfigList(Ptr<UpdateConfigList> *L)
 
816
{
 
817
  MUTEX_TRY_LOCK(lock, mutex, this_ethread());
 
818
  if (!lock) {
 
819
    return 0;                   // Try again later
 
820
  } else {
 
821
    *L = _CL;
 
822
    return 1;                   // Success
 
823
  }
 
824
}
 
825
 
 
826
int
 
827
UpdateConfigManager::URL_list_update_callout(const char *name, RecDataT data_type, RecData data, void *cookie)
 
828
{
 
829
  NOWARN_UNUSED(name);
 
830
  NOWARN_UNUSED(data_type);
 
831
  UpdateConfigManager *cm = (UpdateConfigManager *) cookie;
 
832
  cm->SetFileName((char *) data.rec_string);
 
833
 
 
834
 
 
835
  // URL update may block in file i/o.
 
836
  // Reschedule on ET_CACHE thread.
 
837
 
 
838
  eventProcessor.schedule_imm(cm, ET_CACHE);
 
839
 
 
840
  return 0;
 
841
}
 
842
 
 
843
int
 
844
UpdateConfigManager::ProcessUpdate(int event, Event * e)
 
845
{
 
846
  if (event == EVENT_IMMEDIATE) {
 
847
    ////////////////////////////////////////////////////////////////////
 
848
    // EVENT_IMMEDIATE -- URL list update
 
849
    ////////////////////////////////////////////////////////////////////
 
850
 
 
851
    UpdateConfigList *l = NULL;
 
852
 
 
853
    l = BuildUpdateList();
 
854
    if (l) {
 
855
      _CL = l;
 
856
    }
 
857
    return EVENT_DONE;
 
858
  }
 
859
 
 
860
  if (event == EVENT_INTERVAL) {
 
861
    ////////////////////////////////////////////////////////////////////
 
862
    // EVENT_INTERVAL  -- Global configuration update check
 
863
    ////////////////////////////////////////////////////////////////////
 
864
 
 
865
    UpdateConfigParams *p = NEW(new UpdateConfigParams(*_CP_actual));
 
866
 
 
867
    if (!(*_CP == *p)) {
 
868
      _CP = p;
 
869
      Debug("update", "enable %d force %d rcnt %d rint %d updates %d mem %d",
 
870
            p->_enabled, p->_immediate_update, p->_retry_count,
 
871
            p->_retry_interval, p->_concurrent_updates, p->_max_update_state_machines, p->_memory_use_in_mb);
 
872
    } else {
 
873
      delete p;
 
874
    }
 
875
    return EVENT_DONE;
 
876
  }
 
877
  // Unknown event, ignore it.
 
878
 
 
879
  Debug("update", "ProcessUpdate: Unknown event %d 0x%x", event, e);
 
880
  return EVENT_DONE;
 
881
}
 
882
 
 
883
UpdateConfigList *
 
884
UpdateConfigManager::BuildUpdateList()
 
885
{
 
886
  // Build pathname to "update.config" and open file
 
887
 
 
888
  char ConfigFilePath[PATH_NAME_MAX];
 
889
  if (_filename) {
 
890
    ink_strncpy(ConfigFilePath, system_config_directory, sizeof(ConfigFilePath));
 
891
    strncat(ConfigFilePath, "/", sizeof(ConfigFilePath) - strlen(ConfigFilePath) - 1);
 
892
    strncat(ConfigFilePath, _filename, sizeof(ConfigFilePath) - strlen(ConfigFilePath) - 1);
 
893
  } else {
 
894
    return (UpdateConfigList *) NULL;
 
895
  }
 
896
 
 
897
#ifdef _WIN32
 
898
  // O_BINARY to avoid translation of CR-LF
 
899
  int fd = open(ConfigFilePath, O_RDONLY | O_BINARY);
 
900
#else
 
901
  int fd = open(ConfigFilePath, O_RDONLY);
 
902
#endif
 
903
  if (fd < 0) {
 
904
    Warning("read update.config, open failed");
 
905
    SignalWarning(MGMT_SIGNAL_CONFIG_ERROR, "read update.config, open failed");
 
906
    return (UpdateConfigList *) NULL;
 
907
  }
 
908
  return ParseConfigFile(fd);
 
909
}
 
910
 
 
911
int
 
912
UpdateConfigManager::GetDataLine(int fd, int bufsize, char *buf, int field_delimiters, int delimiter)
 
913
{
 
914
  char *line = buf;
 
915
  int linesize = bufsize;
 
916
  int bytes_read = 0;
 
917
  int rlen;
 
918
 
 
919
  while ((rlen = ink_file_fd_readline(fd, linesize, line)) > 0) {
 
920
    ////////////////////////////////////////////////////////////////////
 
921
    // Notes:
 
922
    //      1) ink_file_fd_readline() null terminates returned buffer
 
923
    //      2) Input processing guarantees that the item delimiter '\'
 
924
    //         does not exist in any data field.
 
925
    ////////////////////////////////////////////////////////////////////
 
926
 
 
927
    // Just return data if we have a comment line
 
928
 
 
929
    if (!bytes_read && *line == '#') {
 
930
      return rlen;
 
931
    }
 
932
    bytes_read += rlen;
 
933
 
 
934
    // Determine if we have a complete line.
 
935
 
 
936
    char *p = buf;
 
937
    int delimiters_found = 0;
 
938
 
 
939
    while (*p) {
 
940
      if (*p == delimiter) {
 
941
        delimiters_found++;
 
942
      }
 
943
      p++;
 
944
    }
 
945
    if (delimiters_found == field_delimiters) {
 
946
      // We have a complete line.
 
947
      return bytes_read;
 
948
 
 
949
    } else if ((delimiters_found == (field_delimiters - 1))
 
950
               && (*(p - 1) == '\n')) {
 
951
      // End of line not delimited.
 
952
      // Fix it and consider it a complete line.
 
953
 
 
954
      *(p - 1) = '\\';
 
955
      return bytes_read;
 
956
    }
 
957
    // Resume read
 
958
    line += rlen;
 
959
    linesize -= rlen;
 
960
  }
 
961
  return 0;
 
962
}
 
963
 
 
964
UpdateConfigList *
 
965
UpdateConfigManager::ParseConfigFile(int f)
 
966
{
 
967
  /*
 
968
     "update.config" line syntax:
 
969
     <URL>\<Request Headers>\<Offset Hour>\<Interval>\<Recursion depth>\
 
970
   */
 
971
 
 
972
  enum
 
973
  { F_URL, F_HEADERS, F_HOUR, F_INTERVAL, F_DEPTH, F_ITEMS };
 
974
  char *p_start[F_ITEMS];
 
975
  char *p_end[F_ITEMS];
 
976
 
 
977
  char line[MAX_LINE_LENGTH];
 
978
  char *p;
 
979
 
 
980
  int ln = 0;
 
981
  int i;
 
982
 
 
983
  UpdateEntry *e = NULL;
 
984
  UpdateConfigList *ul = NEW(new UpdateConfigList);
 
985
 
 
986
  while (GetDataLine(f, sizeof(line) - 1, line, F_ITEMS, '\\') > 0) {
 
987
    ++ln;
 
988
    if (*line == '#') {
 
989
      continue;
 
990
    } else {
 
991
      p = line;
 
992
    }
 
993
 
 
994
    // Extract fields
 
995
 
 
996
    for (i = 0; i < F_ITEMS; ++i) {
 
997
      p_start[i] = p;
 
998
      p_end[i] = strchr(p, '\\');
 
999
      *p_end[i] = 0;            // Null terminate string
 
1000
 
 
1001
      if (p_end[i]) {
 
1002
        p = p_end[i] + 1;
 
1003
      } else {
 
1004
        Warning("read update.config, invalid syntax, line %d", ln);
 
1005
        SignalWarning(MGMT_SIGNAL_CONFIG_ERROR, "read update.config, invalid syntax");
 
1006
        break;
 
1007
      }
 
1008
    }
 
1009
    if (i < F_ITEMS) {
 
1010
      // Syntax error
 
1011
      goto abort_processing;
 
1012
    }
 
1013
    // Validate data fields
 
1014
 
 
1015
    e = NEW(new UpdateEntry);
 
1016
 
 
1017
    ////////////////////////////////////
 
1018
    // Validate URL
 
1019
    ////////////////////////////////////
 
1020
    if (e->ValidURL(p_start[F_URL], p_end[F_URL])) {
 
1021
      Warning("read update.config, invalid URL field, line %d", ln);
 
1022
      SignalWarning(MGMT_SIGNAL_CONFIG_ERROR, "read update.config, invalid URL field");
 
1023
      goto abort_processing;
 
1024
    }
 
1025
    ////////////////////////////////////
 
1026
    // Validate headers
 
1027
    ////////////////////////////////////
 
1028
    if (e->ValidHeaders(p_start[F_HEADERS], p_end[F_HEADERS])) {
 
1029
      Warning("read update.config, invalid headers field, line %d", ln);
 
1030
      SignalWarning(MGMT_SIGNAL_CONFIG_ERROR, "read update.config, invalid headers field");
 
1031
      goto abort_processing;
 
1032
    }
 
1033
    /////////////////////////////////////////////////////////////
 
1034
    // Convert request (URL+Headers) into HTTPHdr format.
 
1035
    /////////////////////////////////////////////////////////////
 
1036
    if (e->BuildHttpRequest()) {
 
1037
      Warning("read update.config, header processing error, line %d", ln);
 
1038
      SignalWarning(MGMT_SIGNAL_CONFIG_ERROR, "read update.config, header processing error");
 
1039
      goto abort_processing;
 
1040
    }
 
1041
    ////////////////////////////////////
 
1042
    // Validate hour
 
1043
    ////////////////////////////////////
 
1044
    if (e->ValidHour(p_start[F_HOUR], p_end[F_HOUR])) {
 
1045
      Warning("read update.config, invalid hour field, line %d", ln);
 
1046
      SignalWarning(MGMT_SIGNAL_CONFIG_ERROR, "read update.config, invalid hour field");
 
1047
      goto abort_processing;
 
1048
    }
 
1049
    ////////////////////////////////////
 
1050
    // Validate interval
 
1051
    ////////////////////////////////////
 
1052
    if (e->ValidInterval(p_start[F_INTERVAL], p_end[F_INTERVAL])) {
 
1053
      Warning("read update.config, invalid interval field, line %d", ln);
 
1054
      SignalWarning(MGMT_SIGNAL_CONFIG_ERROR, "read update.config, invalid interval field");
 
1055
      goto abort_processing;
 
1056
    }
 
1057
    ////////////////////////////////////
 
1058
    // Validate recursion depth
 
1059
    ////////////////////////////////////
 
1060
    if (e->ValidDepth(p_start[F_DEPTH], p_end[F_DEPTH])) {
 
1061
      Warning("read update.config, invalid depth field, line %d", ln);
 
1062
      SignalWarning(MGMT_SIGNAL_CONFIG_ERROR, "read update.config, invalid depth field");
 
1063
      goto abort_processing;
 
1064
    }
 
1065
    // Valid entry, add to list
 
1066
 
 
1067
    e->Init();
 
1068
    Debug("update",
 
1069
          "[%d] [%s] [%s] nhdrs %d hour %d interval %d depth %d",
 
1070
          e->_id, e->_url, e->_request_headers, e->_num_request_headers, e->_offset_hour, e->_interval, e->_max_depth);
 
1071
    ul->Add(e);
 
1072
    e = NULL;
 
1073
  }
 
1074
 
 
1075
  // All file entries are valid.
 
1076
 
 
1077
  close(f);
 
1078
  return ul;
 
1079
 
 
1080
abort_processing:
 
1081
  close(f);
 
1082
  if (e) {
 
1083
    delete e;
 
1084
  }
 
1085
  if (ul) {
 
1086
    delete ul;
 
1087
  }
 
1088
  return (UpdateConfigList *) NULL;
 
1089
}
 
1090
 
 
1091
/////////////////////////////////////////////////////////////////////////////
 
1092
// Class UpdateScheduler
 
1093
//      Handle scheduling of UpdateEntry objects
 
1094
/////////////////////////////////////////////////////////////////////////////
 
1095
UpdateScheduler::UpdateScheduler(UpdateConfigManager * c)
 
1096
:Continuation(new_ProxyMutex()), _periodic_event(0),
 
1097
_recursive_update(0), _CM(c), _schedule_event_callbacks(0), _update_state_machines(0), _base_EN(0), _parent_US(0)
 
1098
{
 
1099
  SET_HANDLER((UpdateSchedulerContHandler)
 
1100
              & UpdateScheduler::ScheduleEvent);
 
1101
}
 
1102
 
 
1103
UpdateScheduler::~UpdateScheduler()
 
1104
{
 
1105
}
 
1106
 
 
1107
int
 
1108
UpdateScheduler::Init()
 
1109
{
 
1110
  _recursive_update = 0;
 
1111
  _periodic_event = eventProcessor.schedule_every(this, HRTIME_SECONDS(10));
 
1112
  return 0;
 
1113
}
 
1114
 
 
1115
int
 
1116
UpdateScheduler::Init(UpdateScheduler * us, UpdateEntry * ue, Ptr<UpdateConfigParams> p)
 
1117
{
 
1118
  ink_assert(ue->_indirect_list->Entries());
 
1119
 
 
1120
  _recursive_update = 1;
 
1121
  _CP = p;
 
1122
  _CL = ue->_indirect_list;
 
1123
  _base_EN = ue;
 
1124
  _parent_US = us;
 
1125
 
 
1126
  // Schedule entries for update by moving entries to pending queue.
 
1127
 
 
1128
  UpdateEntry *e;
 
1129
  while ((e = _CL->Remove())) {
 
1130
    _CL->AddPending(e);
 
1131
  }
 
1132
  _periodic_event = eventProcessor.schedule_every(this, HRTIME_SECONDS(10));
 
1133
  return 0;
 
1134
}
 
1135
 
 
1136
int
 
1137
UpdateScheduler::ScheduleEvent(int event, void *e)
 
1138
{
 
1139
  UpdateEntry *ue = NULL;
 
1140
  int update_complete = 1;
 
1141
 
 
1142
  if (event == EVENT_IMMEDIATE) {
 
1143
    //////////////////////////////////////////////////////////////////////
 
1144
    // Callback on update completion from Update State Machine
 
1145
    //////////////////////////////////////////////////////////////////////
 
1146
    ue = (UpdateEntry *) e;
 
1147
 
 
1148
    switch (ue->_update_event_status) {
 
1149
    case UPDATE_EVENT_SUCCESS:
 
1150
      {
 
1151
        Debug("update", "%s update complete, UPDATE_EVENT_SUCCESS id: %d", (_recursive_update ? "(R)" : ""), ue->_id);
 
1152
        UPDATE_INCREMENT_DYN_STAT(update_successes_stat);
 
1153
 
 
1154
        if ((ue->_max_depth > 0) && ue->_indirect_list) {
 
1155
          if (ue->_indirect_list->Entries()) {
 
1156
            //////////////////////////////////////////////////////////
 
1157
            // Recursive update case.
 
1158
            // At this point, we have a list of URLs which was
 
1159
            // recursively derived from the base URL.
 
1160
            // Instantiate UpdateScheduler to process this URL list.
 
1161
            //////////////////////////////////////////////////////////
 
1162
            Debug("update", "Starting UpdateScheduler for id: %d [%s]", ue->_id, ue->_url);
 
1163
            UpdateScheduler *us = NEW(new UpdateScheduler());
 
1164
            us->Init(this, ue, _CP);
 
1165
            update_complete = 0;
 
1166
 
 
1167
          } else {
 
1168
            ue->_indirect_list = NULL;
 
1169
          }
 
1170
        }
 
1171
        break;
 
1172
      }
 
1173
    case UPDATE_EVENT_SUCCESS_NOACTION:
 
1174
      {
 
1175
        Debug("update",
 
1176
              "%s update complete, UPDATE_EVENT_SUCCESS_NOACTION id: %d", (_recursive_update ? "(R)" : ""), ue->_id);
 
1177
        UPDATE_INCREMENT_DYN_STAT(update_no_actions_stat);
 
1178
        break;
 
1179
      }
 
1180
    case UPDATE_EVENT_FAILED:
 
1181
      {
 
1182
        Debug("update", "%s update complete, UPDATE_EVENT_FAILED id: %d", (_recursive_update ? "(R)" : ""), ue->_id);
 
1183
        UPDATE_INCREMENT_DYN_STAT(update_fails_stat);
 
1184
        break;
 
1185
      }
 
1186
    default:
 
1187
      {
 
1188
        Debug("update",
 
1189
              "%s update complete, unknown status %d, id: %d",
 
1190
              (_recursive_update ? "(R)" : ""), ue->_update_event_status, ue->_id);
 
1191
        UPDATE_INCREMENT_DYN_STAT(update_unknown_status_stat);
 
1192
        break;
 
1193
      }
 
1194
    }                           // End of switch
 
1195
 
 
1196
    if (update_complete) {
 
1197
      if (!_recursive_update) {
 
1198
        /////////////////////////////////////////////////////////
 
1199
        // Recompute expire time and place entry back on list
 
1200
        /////////////////////////////////////////////////////////
 
1201
 
 
1202
        ue->ComputeScheduleTime();
 
1203
        _CL->Add(ue);           // Place back on list
 
1204
 
 
1205
      } else {
 
1206
        delete ue;
 
1207
      }
 
1208
      --_update_state_machines;
 
1209
      UPDATE_DECREMENT_DYN_STAT(update_state_machines_stat);
 
1210
    }
 
1211
    ////////////////////////////////////////////////////////////////
 
1212
    // Start another update SM if scheduling is allowed
 
1213
    // and an entry exists on the pending list.
 
1214
    ////////////////////////////////////////////////////////////////
 
1215
 
 
1216
    if (Schedule() < 0) {
 
1217
      // Scheduling allowed, but nothing to schedule
 
1218
      if (_update_state_machines == 0) {
 
1219
        //////////////////////////////////////////////////////////////
 
1220
        // No more active updates, deallocate config/entry structures
 
1221
        //////////////////////////////////////////////////////////////
 
1222
 
 
1223
        _CP = NULL;
 
1224
        _CL = NULL;
 
1225
 
 
1226
        if (_recursive_update) {
 
1227
          //
 
1228
          // Recursive list update is now complete.
 
1229
          // Callback parent UpdateScheduler.
 
1230
          //
 
1231
          _periodic_event->cancel();
 
1232
          _base_EN->_indirect_list = NULL;
 
1233
          _base_EN->_update_event_status = UPDATE_EVENT_SUCCESS;
 
1234
 
 
1235
          SET_HANDLER((UpdateSchedulerContHandler)
 
1236
                      & UpdateScheduler::ChildExitEventHandler);
 
1237
          handleEvent(EVENT_IMMEDIATE, 0);
 
1238
        }
 
1239
      }
 
1240
    }
 
1241
    return EVENT_DONE;
 
1242
  }
 
1243
  //////////////////////////////////////
 
1244
  // Periodic event callback
 
1245
  //////////////////////////////////////
 
1246
  if (event == EVENT_INTERVAL) {
 
1247
    ++_schedule_event_callbacks;
 
1248
  } else {
 
1249
    // Unknown event, ignore it.
 
1250
    Debug("update", "UpdateScheduler::ScheduleEvent unknown event %d", event);
 
1251
    return EVENT_DONE;
 
1252
  }
 
1253
 
 
1254
  if (!_CP && !_CL) {
 
1255
    // No updates pending, attempt to schedule any expired updates
 
1256
 
 
1257
    if (!_CM->GetConfigParams(&_CP)) {
 
1258
      return EVENT_CONT;        // Missed lock, try at next event
 
1259
    }
 
1260
    if (!_CM->GetConfigList(&_CL)) {
 
1261
      _CP = NULL;
 
1262
      return EVENT_CONT;        // Missed lock, try at next event
 
1263
    }
 
1264
    // Cannot do anything unless we have valid params and list
 
1265
 
 
1266
    if (!_CP || !_CL) {
 
1267
      _CP = NULL;
 
1268
      _CL = NULL;
 
1269
      return EVENT_CONT;        // try at next event
 
1270
    }
 
1271
    // Determine if the subsystem is enabled
 
1272
 
 
1273
    if (!_CP->IsEnabled()) {
 
1274
      _CP = NULL;
 
1275
      _CL = NULL;
 
1276
      return EVENT_CONT;        // try at next event
 
1277
    }
 
1278
 
 
1279
  } else {
 
1280
    ///////////////////////////////////////////////////////////////////
 
1281
    // Updates pending from last schedule event, attempt to restart
 
1282
    // additional update SM(s).
 
1283
    ///////////////////////////////////////////////////////////////////
 
1284
 
 
1285
    Schedule();
 
1286
    return EVENT_CONT;
 
1287
  }
 
1288
  ink_release_assert(!_update_state_machines);
 
1289
 
 
1290
  ///////////////////////////////////////////////////////
 
1291
  // Scan entry list and schedule expired updates
 
1292
  ///////////////////////////////////////////////////////
 
1293
 
 
1294
  ink_hrtime ht = ink_get_based_hrtime();
 
1295
  time_t cur_time = ht / HRTIME_SECOND;
 
1296
  Queue<UpdateEntry> no_action_q;
 
1297
  int time_expired;
 
1298
 
 
1299
  while ((ue = _CL->Remove())) {
 
1300
    time_expired = ue->ScheduleNow(cur_time);
 
1301
    if (time_expired || _CP->ImmediateUpdate()) {
 
1302
      if (Schedule(ue) > 0) {
 
1303
        Debug("update", "%s and started id: %d", time_expired ? "expired" : "force expire", ue->_id);
 
1304
      } else {
 
1305
        Debug("update", "%s with deferred start id: %d", time_expired ? "expired" : "force expire", ue->_id);
 
1306
      }
 
1307
 
 
1308
    } else {
 
1309
      no_action_q.enqueue(ue);
 
1310
    }
 
1311
  }
 
1312
 
 
1313
  // Place no_action_q elements back on list
 
1314
 
 
1315
  while ((ue = no_action_q.dequeue())) {
 
1316
    _CL->Add(ue);
 
1317
  }
 
1318
 
 
1319
  if (!_update_state_machines && !_CL->_pending_q.head) {
 
1320
    // Nothing active or pending.
 
1321
    // Drop references to config/param structures.
 
1322
 
 
1323
    _CP = NULL;
 
1324
    _CL = NULL;
 
1325
  }
 
1326
  return EVENT_DONE;
 
1327
}
 
1328
 
 
1329
int
 
1330
UpdateScheduler::ChildExitEventHandler(int event, Event * e)
 
1331
{
 
1332
  NOWARN_UNUSED(e);
 
1333
  switch (event) {
 
1334
  case EVENT_IMMEDIATE:
 
1335
  case EVENT_INTERVAL:
 
1336
    {
 
1337
      MUTEX_TRY_LOCK(lock, _parent_US->mutex, this_ethread());
 
1338
      if (lock) {
 
1339
        Debug("update", "Child UpdateScheduler exit id: %d", _base_EN->_id);
 
1340
        _parent_US->handleEvent(EVENT_IMMEDIATE, _base_EN);
 
1341
        delete this;
 
1342
 
 
1343
      } else {
 
1344
        // Lock miss, try again later.
 
1345
        eventProcessor.schedule_in(this, HRTIME_MSECONDS(10));
 
1346
      }
 
1347
      break;
 
1348
    }
 
1349
  default:
 
1350
    {
 
1351
      ink_release_assert(!"UpdateScheduler::ChildExitEventHandler invalid event");
 
1352
    }                           // End of case
 
1353
  }                             // End of switch
 
1354
 
 
1355
  return EVENT_DONE;
 
1356
}
 
1357
 
 
1358
int
 
1359
UpdateScheduler::Schedule(UpdateEntry * e)
 
1360
{
 
1361
  // Return > 0,  UpdateEntry scheduled
 
1362
  // Return == 0, Scheduling not allowed
 
1363
  // Return < 0,  Scheduling allowed, but nothing to schedule
 
1364
 
 
1365
  UpdateSM *usm;
 
1366
  UpdateEntry *ue = e;
 
1367
  int allow_schedule;
 
1368
  RecInt count, sum;
 
1369
  int max_concurrent_updates;
 
1370
 
 
1371
  UPDATE_READ_DYN_STAT(update_state_machines_stat, count, sum);
 
1372
  if (_CP->ConcurrentUpdates() < _CP->MaxUpdateSM()) {
 
1373
    max_concurrent_updates = _CP->ConcurrentUpdates();
 
1374
  } else {
 
1375
    max_concurrent_updates = _CP->MaxUpdateSM();
 
1376
  }
 
1377
  allow_schedule = (sum < max_concurrent_updates);
 
1378
 
 
1379
  if (allow_schedule) {
 
1380
    ue = ue ? ue : _CL->RemovePending();
 
1381
    if (ue) {
 
1382
      ++_update_state_machines;
 
1383
      UPDATE_INCREMENT_DYN_STAT(update_state_machines_stat);
 
1384
      usm = NEW(new UpdateSM(this, _CP, ue));
 
1385
      usm->Start();
 
1386
 
 
1387
      Debug("update", "%s %s start update id: %d [%s]",
 
1388
            (_recursive_update ? "(R)" : ""), (e ? "directed" : "speculative"), ue->_id, ue->_url);
 
1389
 
 
1390
      return 1;                 // UpdateEntry scheduled
 
1391
    } else {
 
1392
      return -1;                // Scheduling allowed but nothing to schedule
 
1393
    }
 
1394
 
 
1395
  } else {
 
1396
    if (ue) {
 
1397
      _CL->AddPending(ue);
 
1398
    }
 
1399
    return 0;                   // Scheduling not allowed
 
1400
  }
 
1401
}
 
1402
 
 
1403
/////////////////////////////////////////////////////////////////////////////
 
1404
// Class UpdateSM
 
1405
//      State machine which handles object update action
 
1406
/////////////////////////////////////////////////////////////////////////////
 
1407
UpdateSM::UpdateSM(UpdateScheduler * us, Ptr<UpdateConfigParams> p, UpdateEntry * e)
 
1408
:Continuation(new_ProxyMutex()), _state(USM_INIT), _return_status(0), _retries(0)
 
1409
{
 
1410
  SET_HANDLER((UpdateSMContHandler) & UpdateSM::HandleSMEvent);
 
1411
  _US = us;
 
1412
  _CP = p;
 
1413
  _EN = e;
 
1414
}
 
1415
 
 
1416
UpdateSM::~UpdateSM()
 
1417
{
 
1418
  _CP = NULL;                   // drop reference
 
1419
}
 
1420
 
 
1421
void
 
1422
UpdateSM::Start()
 
1423
{
 
1424
  eventProcessor.schedule_imm(this, ET_CACHE);
 
1425
}
 
1426
 
 
1427
int
 
1428
UpdateSM::HandleSMEvent(int event, Event * e)
 
1429
{
 
1430
  NOWARN_UNUSED(e);
 
1431
  while (1) {
 
1432
    switch (_state) {
 
1433
    case USM_INIT:
 
1434
      {
 
1435
        ////////////////////////////////////////////////////////////////////
 
1436
        // Cluster considerations.
 
1437
        // For non-recursive URL(s), only process it if the cluster
 
1438
        // hash returns this node.  Recursive URL(s) are processed by
 
1439
        // all nodes in the cluster.
 
1440
        ////////////////////////////////////////////////////////////////////
 
1441
        if (_EN->_max_depth > 0) {
 
1442
          // Recursive URL(s) are processed by all nodes.
 
1443
          _state = USM_PROCESS_URL;
 
1444
          break;
 
1445
        }
 
1446
 
 
1447
        INK_MD5 url_md5;
 
1448
        Cache::generate_key(&url_md5, &_EN->_URLhandle, (_EN->_num_request_headers ? _EN->_http_hdr : NULL));
 
1449
        Cache::generate_key(&url_md5, &_EN->_URLhandle, _EN->_http_hdr);
 
1450
        ClusterMachine *m = cluster_machine_at_depth(cache_hash(url_md5));
 
1451
        if (m) {
 
1452
          // URL hashed to remote node, do nothing.
 
1453
          _state = USM_EXIT;
 
1454
          _EN->_update_event_status = UPDATE_EVENT_SUCCESS_NOACTION;
 
1455
          break;
 
1456
        } else {
 
1457
          // URL hashed to local node, start processing.
 
1458
          _state = USM_PROCESS_URL;
 
1459
          break;
 
1460
        }
 
1461
      }
 
1462
    case USM_PROCESS_URL:
 
1463
      {
 
1464
        ///////////////////////////////////
 
1465
        // Dispatch to target handler
 
1466
        ///////////////////////////////////
 
1467
        int n;
 
1468
        int scheme_len;
 
1469
        const char *scheme;
 
1470
        _state = USM_PROCESS_URL_COMPLETION;
 
1471
        scheme = _EN->_URLhandle.scheme_get(&scheme_len);
 
1472
        for (n = 0; n < N_SCHEMES; ++n) {
 
1473
          if (scheme == *scheme_dispatch_table[n].scheme) {
 
1474
            _EN->_scheme_index = n;
 
1475
            if ((*scheme_dispatch_table[n].func) (this)) {
 
1476
              break;            // Error in initiation
 
1477
            }
 
1478
            return EVENT_CONT;
 
1479
          }
 
1480
        }
 
1481
        // Error in initiation or bad scheme.
 
1482
 
 
1483
        _state = USM_EXIT;
 
1484
        _EN->_update_event_status = UPDATE_EVENT_FAILED;
 
1485
        break;
 
1486
      }
 
1487
    case USM_PROCESS_URL_COMPLETION:
 
1488
      {
 
1489
        ///////////////////////////////////
 
1490
        // Await URL update completion
 
1491
        ///////////////////////////////////
 
1492
        _state = USM_EXIT;
 
1493
        _EN->_update_event_status = event;
 
1494
        (*scheme_post_dispatch_table[_EN->_scheme_index].func) (this);
 
1495
        break;
 
1496
      }
 
1497
    case USM_EXIT:
 
1498
      {
 
1499
        /////////////////////////////////////////////
 
1500
        // Operation complete
 
1501
        /////////////////////////////////////////////
 
1502
        if ((_return_status == UPDATE_EVENT_FAILED)
 
1503
            && (_retries < _CP->RetryCount())) {
 
1504
 
 
1505
          // Retry operation
 
1506
 
 
1507
          ++_retries;
 
1508
          _state = USM_PROCESS_URL;
 
1509
          eventProcessor.schedule_in(this, HRTIME_SECONDS(_CP->RetryInterval()), ET_CACHE);
 
1510
          return EVENT_DONE;
 
1511
 
 
1512
        } else {
 
1513
          MUTEX_TRY_LOCK(lock, _US->mutex, this_ethread());
 
1514
          if (lock) {
 
1515
            _US->handleEvent(EVENT_IMMEDIATE, (void *) _EN);
 
1516
            delete this;
 
1517
            return EVENT_DONE;
 
1518
 
 
1519
          } else {
 
1520
            // Missed lock, try again later
 
1521
            eventProcessor.schedule_in(this, HRTIME_MSECONDS(10), ET_CACHE);
 
1522
            return EVENT_CONT;
 
1523
          }
 
1524
        }
 
1525
      }
 
1526
    }                           // End of switch
 
1527
  }                             // End of while
 
1528
 
 
1529
  return EVENT_CONT;
 
1530
}
 
1531
 
 
1532
struct dispatch_entry scheme_dispatch_table[UpdateSM::N_SCHEMES] = {
 
1533
  {&URL_SCHEME_HTTP, UpdateSM::http_scheme},
 
1534
  {&URL_SCHEME_RTSP, UpdateSM::rtsp_scheme}
 
1535
};
 
1536
 
 
1537
struct dispatch_entry scheme_post_dispatch_table[UpdateSM::N_SCHEMES] = {
 
1538
  {&URL_SCHEME_HTTP, UpdateSM::http_scheme_postproc},
 
1539
  {&URL_SCHEME_RTSP, UpdateSM::rtsp_scheme_postproc}
 
1540
};
 
1541
 
 
1542
int
 
1543
UpdateSM::http_scheme(UpdateSM * sm)
 
1544
{
 
1545
  if (sm->_EN->_max_depth > 0) {
 
1546
    ////////////////////////////////////
 
1547
    // Recursive Update
 
1548
    ////////////////////////////////////
 
1549
    Debug("update", "Start recursive HTTP GET id: %d [%s]", sm->_EN->_id, sm->_EN->_url);
 
1550
    sm->_EN->_indirect_list = NEW(new UpdateConfigList);
 
1551
    RecursiveHttpGet *RHttpGet = NEW(new RecursiveHttpGet);
 
1552
 
 
1553
    RHttpGet->Init(sm, sm->_EN->_url, sm->_EN->_request_headers,
 
1554
                   &sm->_EN->_URLhandle, sm->_EN->_http_hdr,
 
1555
                   sm->_EN->_max_depth, sm->_EN->_indirect_list, &update_allowable_html_tags[0]);
 
1556
  } else {
 
1557
    ////////////////////////////////////
 
1558
    // One URL update
 
1559
    ////////////////////////////////////
 
1560
    Debug("update", "Start HTTP GET id: %d [%s]", sm->_EN->_id, sm->_EN->_url);
 
1561
    HttpUpdateSM *current_reader;
 
1562
 
 
1563
    current_reader = HttpUpdateSM::allocate();
 
1564
    current_reader->init();
 
1565
    // TODO: Do anything with the returned Action* ?
 
1566
    current_reader->start_scheduled_update(sm, sm->_EN->_http_hdr);
 
1567
  }
 
1568
  return 0;
 
1569
}
 
1570
 
 
1571
int
 
1572
UpdateSM::http_scheme_postproc(UpdateSM * sm)
 
1573
{
 
1574
  // Map HttpUpdateSM return event code to internal status code
 
1575
 
 
1576
  switch (sm->_EN->_update_event_status) {
 
1577
  case UPDATE_EVENT_SUCCESS:
 
1578
  case UPDATE_EVENT_FAILED:
 
1579
    // Returned only by RecursiveHttpGet
 
1580
    sm->_return_status = sm->_EN->_update_event_status;
 
1581
    break;
 
1582
 
 
1583
  case HTTP_SCH_UPDATE_EVENT_WRITTEN:
 
1584
  case HTTP_SCH_UPDATE_EVENT_UPDATED:
 
1585
  case HTTP_SCH_UPDATE_EVENT_DELETED:
 
1586
  case HTTP_SCH_UPDATE_EVENT_NOT_CACHED:
 
1587
  case HTTP_SCH_UPDATE_EVENT_NO_ACTION:
 
1588
    sm->_EN->_update_event_status = UPDATE_EVENT_SUCCESS;
 
1589
    sm->_return_status = UPDATE_EVENT_SUCCESS;
 
1590
    break;
 
1591
 
 
1592
  case HTTP_SCH_UPDATE_EVENT_ERROR:
 
1593
  default:
 
1594
    sm->_EN->_update_event_status = UPDATE_EVENT_FAILED;
 
1595
    sm->_return_status = UPDATE_EVENT_FAILED;
 
1596
    break;
 
1597
  }
 
1598
  return 0;
 
1599
}
 
1600
 
 
1601
// Not used anywhere.
 
1602
 
 
1603
int
 
1604
UpdateSM::rtsp_scheme(UpdateSM * sm)
 
1605
{
 
1606
#ifdef GO_AWAY
 
1607
  int ret = INKMCOPreload(sm, INKContCreate(rtsp_progress_cont, NULL),
 
1608
                          sm->_EN->_url,
 
1609
                          (5 * 30720) /* MAX_PRELOAD_BANDWIDTH */ ,
 
1610
                          5);
 
1611
  if (ret) {
 
1612
    Debug("update", "Start RTSP GET id: %d", sm->_EN->_id);
 
1613
  }
 
1614
  return ret;
 
1615
#else
 
1616
  NOWARN_UNUSED(sm);
 
1617
#endif
 
1618
  return 0;
 
1619
}
 
1620
 
 
1621
int
 
1622
UpdateSM::rtsp_scheme_postproc(UpdateSM * sm)
 
1623
{
 
1624
  // Map MCO return event code to internal status code
 
1625
 
 
1626
  if (sm->_EN->_update_event_status == VC_EVENT_READ_COMPLETE) {
 
1627
    sm->_EN->_update_event_status = UPDATE_EVENT_SUCCESS;
 
1628
    sm->_return_status = UPDATE_EVENT_SUCCESS;
 
1629
  } else {
 
1630
    sm->_EN->_update_event_status = UPDATE_EVENT_FAILED;
 
1631
    sm->_return_status = UPDATE_EVENT_FAILED;
 
1632
  }
 
1633
  return 0;
 
1634
}
 
1635
 
 
1636
/////////////////////////////////////////////////////////////////////////////
 
1637
// Class RecursiveHttpGet
 
1638
//      Generate URL list by recursively traversing non-terminal URL(s)
 
1639
//      up to the specified depth.
 
1640
/////////////////////////////////////////////////////////////////////////////
 
1641
char
 
1642
  HtmlParser::default_zero_char = '\0';
 
1643
 
 
1644
RecursiveHttpGet::RecursiveHttpGet()
 
1645
:Continuation(new_ProxyMutex()), _id(0), _caller_cont(0),
 
1646
_request_headers(0), _http_hdr(0), _recursion_depth(0), _OL(0), _group_link_head(0), _active_child_state_machines(0)
 
1647
{
 
1648
  SET_HANDLER((RecursiveHttpGetContHandler)
 
1649
              & RecursiveHttpGet::RecursiveHttpGetEvent);
 
1650
}
 
1651
 
 
1652
RecursiveHttpGet::~RecursiveHttpGet()
 
1653
{
 
1654
  _CL = NULL;
 
1655
}
 
1656
 
 
1657
void
 
1658
RecursiveHttpGet::Init(Continuation * cont, char *url, char *request_headers,
 
1659
                       URL * url_data, HTTPHdr * http_hdr, int recursion_depth,
 
1660
                       Ptr<UpdateConfigList> L, struct html_tag *allowed_html_tags)
 
1661
{
 
1662
  /////////////////////////////////////////////////////////////////////////
 
1663
  // Note: URL and request header data pointers are assumed to be
 
1664
  //       valid during the life of this class.
 
1665
  /////////////////////////////////////////////////////////////////////////
 
1666
  _id = ink_atomic_increment(&global_id, 1);
 
1667
  _caller_cont = cont;
 
1668
  _request_headers = request_headers;
 
1669
  _url_data = url_data;
 
1670
  _http_hdr = http_hdr;
 
1671
  _recursion_depth = recursion_depth;
 
1672
  _CL = L;
 
1673
  _OL = ObjectReloadContAllocator.alloc();
 
1674
  _OL->Init(this, url, strlen(url), _request_headers, (_request_headers ? strlen(_request_headers) : 0), 1, 1);
 
1675
 
 
1676
  html_parser.Init(url, allowed_html_tags);
 
1677
 
 
1678
  Debug("update", "Start recursive read rid: %d [%s]", _id, html_parser._url);
 
1679
}
 
1680
 
 
1681
int
 
1682
RecursiveHttpGet::RecursiveHttpGetEvent(int event, Event * d)
 
1683
{
 
1684
  char *url, *url_end;
 
1685
  int status;
 
1686
  UpdateEntry *ue;
 
1687
  IOBufferReader *r = (IOBufferReader *) d;
 
1688
 
 
1689
  switch (event) {
 
1690
  case NET_EVENT_OPEN_FAILED:
 
1691
    {
 
1692
      Debug("update", "RecursiveHttpGetEvent connect failed id: %d [%s]", _id, html_parser._url);
 
1693
      break;
 
1694
    }
 
1695
  case VC_EVENT_ERROR:
 
1696
    {
 
1697
      Debug("update", "RecursiveHttpGetEvent connect event error id: %d [%s]", _id, html_parser._url);
 
1698
      break;
 
1699
    }
 
1700
  case VC_EVENT_READ_READY:
 
1701
  case VC_EVENT_READ_COMPLETE:
 
1702
  case VC_EVENT_EOS:
 
1703
    {
 
1704
      while ((status = html_parser.ParseHtml(r, &url, &url_end))) {
 
1705
        // Validate given URL.
 
1706
 
 
1707
        ue = NEW(new UpdateEntry);
 
1708
        if (ue->ValidURL(url, url_end + 1 /* Point to null */ )) {
 
1709
          delete ue;
 
1710
          ue = NULL;
 
1711
 
 
1712
        } else {
 
1713
          // Complete remaining UpdateEntry initializations
 
1714
 
 
1715
          ue->_request_headers = xstrdup(_request_headers);
 
1716
          ue->BuildHttpRequest();
 
1717
          ue->Init(1);          // Derived URL
 
1718
 
 
1719
          // Discard remote URL(s)
 
1720
          int ue_host_len;
 
1721
          const char *ue_host = ue->_URLhandle.host_get(&ue_host_len);
 
1722
          int url_host_len;
 
1723
          const char *url_host = _url_data->host_get(&url_host_len);
 
1724
 
 
1725
          if (ue_host == NULL || url_host == NULL || ptr_len_casecmp(ue_host, ue_host_len, url_host, url_host_len)) {
 
1726
            delete ue;
 
1727
            ue = NULL;
 
1728
            continue;
 
1729
          }
 
1730
          // I think we're generating the cache key just to get
 
1731
          //   a hash of the URL.  Used to use Cache::generate_key
 
1732
          //   that no longer works with vary_on_user_agent
 
1733
          //   isn't turned on
 
1734
//              Cache::generate_key(&ue->_url_md5, &ue->_URLhandle, _http_hdr);
 
1735
          ue->_URLhandle.MD5_get(&ue->_url_md5);
 
1736
 
 
1737
          if (_CL->HashAdd(ue)) {
 
1738
            // Entry already exists
 
1739
 
 
1740
            delete ue;
 
1741
            ue = NULL;
 
1742
 
 
1743
          } else {
 
1744
            // Entry is unique and has been added to hash table.
 
1745
            // Set terminal URL status and add to current
 
1746
            // recursion level list.
 
1747
 
 
1748
            ue->SetTerminalStatus(((status < 0) ? 1 : 0));
 
1749
            Debug("update", "Recursive find rid: %d id: %d %s\n [%s]",
 
1750
                  _id, ue->_id, (ue->TerminalURL()? "T " : ""), ue->_url);
 
1751
 
 
1752
            if (_group_link_head) {
 
1753
              ue->_group_link = _group_link_head;
 
1754
              _group_link_head = ue;
 
1755
            } else {
 
1756
              _group_link_head = ue;
 
1757
              ue->_group_link = NULL;
 
1758
            }
 
1759
          }
 
1760
        }
 
1761
      }
 
1762
      ink_release_assert(r->read_avail() == 0);
 
1763
 
 
1764
      if ((event == VC_EVENT_READ_COMPLETE)
 
1765
          || (event == VC_EVENT_EOS)) {
 
1766
        break;
 
1767
 
 
1768
      } else {
 
1769
        return EVENT_CONT;
 
1770
      }
 
1771
    }
 
1772
  case UPDATE_EVENT_SUCCESS:
 
1773
  case UPDATE_EVENT_FAILED:
 
1774
    {
 
1775
      // Child state machine completed.
 
1776
 
 
1777
      ink_release_assert(_active_child_state_machines > 0);
 
1778
      _active_child_state_machines--;
 
1779
      break;
 
1780
    }
 
1781
  default:
 
1782
    {
 
1783
      ink_release_assert(!"RecursiveHttpGetEvent invalid event");
 
1784
      return EVENT_DONE;
 
1785
 
 
1786
    }                           // End of case
 
1787
  }                             // End of switch
 
1788
 
 
1789
  if (_group_link_head) {
 
1790
    // At this point, we have a list of valid terminal
 
1791
    // and non-terminal URL(s).
 
1792
    // Sequentially initiate the read on the non-terminal URL(s).
 
1793
 
 
1794
    while (_group_link_head) {
 
1795
      ue = _group_link_head;
 
1796
      _group_link_head = ue->_group_link;
 
1797
 
 
1798
      if (!ue->TerminalURL()) {
 
1799
        if (_recursion_depth <= 1) {
 
1800
          continue;
 
1801
        }
 
1802
 
 
1803
        Debug("update", "(R) start non-terminal HTTP GET rid: %d id: %d [%s]", _id, ue->_id, ue->_url);
 
1804
 
 
1805
        _active_child_state_machines++;
 
1806
        RecursiveHttpGet *RHttpGet = NEW(new RecursiveHttpGet());
 
1807
        RHttpGet->Init(this, ue->_url, _request_headers,
 
1808
                       _url_data, _http_hdr, (_recursion_depth - 1), _CL, &update_allowable_html_tags[0]);
 
1809
        return EVENT_CONT;
 
1810
 
 
1811
      }
 
1812
    }
 
1813
  }
 
1814
  // All child state machines have completed, tell our parent
 
1815
  // and delete ourself.
 
1816
 
 
1817
  SET_HANDLER((RecursiveHttpGetContHandler)
 
1818
              & RecursiveHttpGet::ExitEventHandler);
 
1819
  handleEvent(EVENT_IMMEDIATE, 0);
 
1820
  return EVENT_DONE;
 
1821
}
 
1822
 
 
1823
int
 
1824
RecursiveHttpGet::ExitEventHandler(int event, Event * e)
 
1825
{
 
1826
  NOWARN_UNUSED(e);
 
1827
  switch (event) {
 
1828
  case EVENT_IMMEDIATE:
 
1829
  case EVENT_INTERVAL:
 
1830
    {
 
1831
      MUTEX_TRY_LOCK(lock, _caller_cont->mutex, this_ethread());
 
1832
      if (lock) {
 
1833
        Debug("update", "Exiting recursive read rid: %d [%s]", _id, html_parser._url);
 
1834
        _caller_cont->handleEvent(UPDATE_EVENT_SUCCESS, 0);
 
1835
        delete this;
 
1836
 
 
1837
      } else {
 
1838
        // Lock miss, try again later.
 
1839
        eventProcessor.schedule_in(this, HRTIME_MSECONDS(10));
 
1840
      }
 
1841
      break;
 
1842
    }
 
1843
  default:
 
1844
    {
 
1845
      ink_release_assert(!"RecursiveHttpGet::ExitEventHandler invalid event");
 
1846
    }                           // End of case
 
1847
  }                             // End of switch
 
1848
 
 
1849
  return EVENT_DONE;
 
1850
}
 
1851
 
 
1852
int
 
1853
HtmlParser::ParseHtml(IOBufferReader * r, char **url, char **url_end)
 
1854
{
 
1855
  int status;
 
1856
  while (1) {
 
1857
    if ((status = ScanHtmlForURL(r, url, url_end))) {
 
1858
      status = ConstructURL(url, url_end);
 
1859
      if (status)
 
1860
        return status;
 
1861
    } else {
 
1862
      return 0;                 // No more bytes
 
1863
    }
 
1864
  }
 
1865
}
 
1866
 
 
1867
int
 
1868
HtmlParser::ScanHtmlForURL(IOBufferReader * r, char **url, char **url_end)
 
1869
{
 
1870
  unsigned char c;
 
1871
  int n = 0;
 
1872
 
 
1873
  while (1) {
 
1874
    switch (_scan_state) {
 
1875
    case SCAN_INIT:
 
1876
      {
 
1877
        _tag.clear();
 
1878
 
 
1879
        _attr.clear();
 
1880
        _attr_value.clear();
 
1881
        _attr_value_hash_char_index = -1;
 
1882
        _attr_value_quoted = 0;
 
1883
        _attr_matched = false;
 
1884
 
 
1885
        _scan_state = SCAN_START;
 
1886
        n = -1;
 
1887
        break;
 
1888
      }
 
1889
    case SCAN_START:
 
1890
      {
 
1891
        while ((n = r->read((char *) &c, 1))) {
 
1892
          if (c == '<') {
 
1893
            _scan_state = FIND_TAG_START;
 
1894
            break;
 
1895
          }
 
1896
        }
 
1897
        break;
 
1898
      }
 
1899
    case FIND_TAG_START:
 
1900
      {
 
1901
        while ((n = r->read((char *) &c, 1))) {
 
1902
          if (!isspace(c)) {
 
1903
            if (c == '>') {
 
1904
              ////////////////////////////////////////////////////
 
1905
              // '< >' with >= 0 embedded spaces, ignore it.
 
1906
              ////////////////////////////////////////////////////
 
1907
              _scan_state = SCAN_INIT;
 
1908
              break;
 
1909
 
 
1910
            } else {
 
1911
              _tag(_tag.length()) = c;
 
1912
              _scan_state = COPY_TAG;
 
1913
              break;
 
1914
            }
 
1915
          }
 
1916
        }
 
1917
        break;
 
1918
      }
 
1919
    case COPY_TAG:
 
1920
      {
 
1921
        while ((n = r->read((char *) &c, 1))) {
 
1922
          if (!isspace(c)) {
 
1923
            if (c == '>') {
 
1924
              /////////////////////////////
 
1925
              // <tag>, ignore it
 
1926
              /////////////////////////////
 
1927
              _scan_state = SCAN_INIT;
 
1928
              break;
 
1929
 
 
1930
            } else if (c == '=') {
 
1931
              ///////////////////////////////
 
1932
              // <tag=something>, ignore it
 
1933
              ///////////////////////////////
 
1934
              _scan_state = SCAN_INIT;
 
1935
              break;
 
1936
 
 
1937
            } else {
 
1938
              if (_tag.length() < MAX_TAG_NAME_LENGTH) {
 
1939
                _tag(_tag.length()) = c;
 
1940
 
 
1941
              } else {
 
1942
                ///////////////////////////////////
 
1943
                // Tag name to long, ignore it
 
1944
                ///////////////////////////////////
 
1945
                _scan_state = SCAN_INIT;
 
1946
                break;
 
1947
              }
 
1948
            }
 
1949
 
 
1950
          } else {
 
1951
            _tag(_tag.length()) = 0;
 
1952
            if (strcmp(_tag, HTML_COMMENT_TAG) == 0) {
 
1953
              _scan_state = IGNORE_COMMENT_START;
 
1954
            } else {
 
1955
              _scan_state = FIND_ATTR_START;
 
1956
            }
 
1957
            break;
 
1958
          }
 
1959
        }
 
1960
        break;
 
1961
      }
 
1962
    case IGNORE_COMMENT_START:
 
1963
      {
 
1964
        _comment_end_ptr = (char *) HTML_COMMENT_END;
 
1965
        _scan_state = IGNORE_COMMENT;
 
1966
        break;
 
1967
      }
 
1968
    case IGNORE_COMMENT:
 
1969
      {
 
1970
        while ((n = r->read((char *) &c, 1))) {
 
1971
          if (!isspace(c)) {
 
1972
            if (c == *_comment_end_ptr) {
 
1973
              _comment_end_ptr++;
 
1974
              if (!*_comment_end_ptr) {
 
1975
                _scan_state = SCAN_INIT;
 
1976
                break;
 
1977
              }
 
1978
            } else {
 
1979
              _comment_end_ptr = (char *) HTML_COMMENT_END;
 
1980
            }
 
1981
          }
 
1982
        }
 
1983
        break;
 
1984
      }
 
1985
    case FIND_ATTR_START:
 
1986
      {
 
1987
        while ((n = r->read((char *) &c, 1))) {
 
1988
          if (!isspace(c)) {
 
1989
            if (c == '>') {
 
1990
              ////////////////////////////////////////////////
 
1991
              // <tag > with >=1 embedded spaces, ignore it
 
1992
              ////////////////////////////////////////////////
 
1993
              _scan_state = SCAN_INIT;
 
1994
              break;
 
1995
 
 
1996
            } else if (c == '=') {
 
1997
              //////////////////////////////////////////////////////////
 
1998
              // <tag =something> with >=1 embedded spaces, ignore it
 
1999
              //////////////////////////////////////////////////////////
 
2000
              _scan_state = SCAN_INIT;
 
2001
              break;
 
2002
 
 
2003
            } else {
 
2004
              _attr(_attr.length()) = c;
 
2005
              _scan_state = COPY_ATTR;
 
2006
              break;
 
2007
            }
 
2008
          }
 
2009
        }
 
2010
        break;
 
2011
      }
 
2012
    case COPY_ATTR:
 
2013
      {
 
2014
        while ((n = r->read((char *) &c, 1))) {
 
2015
          if (!isspace(c)) {
 
2016
            if (c == '>') {
 
2017
              /////////////////////////////
 
2018
              // <tag attr>, ignore it
 
2019
              /////////////////////////////
 
2020
              _scan_state = SCAN_INIT;
 
2021
              break;
 
2022
 
 
2023
            } else if (c == '=') {
 
2024
              ///////////////////////////////
 
2025
              // <tag attr=something>
 
2026
              ///////////////////////////////
 
2027
              _attr(_attr.length()) = 0;
 
2028
              _scan_state = FIND_ATTR_VALUE_START;
 
2029
              break;
 
2030
 
 
2031
            } else {
 
2032
              if (_attr.length() < MAX_ATTR_NAME_LENGTH) {
 
2033
                _attr(_attr.length()) = c;
 
2034
 
 
2035
              } else {
 
2036
                ///////////////////////////////////
 
2037
                // Attr name to long, ignore it
 
2038
                ///////////////////////////////////
 
2039
                _scan_state = SCAN_INIT;
 
2040
                break;
 
2041
              }
 
2042
            }
 
2043
 
 
2044
          } else {
 
2045
            _attr(_attr.length()) = 0;
 
2046
            _scan_state = FIND_ATTR_VALUE_DELIMITER;
 
2047
            break;
 
2048
          }
 
2049
        }
 
2050
        break;
 
2051
      }
 
2052
    case FIND_ATTR_VALUE_DELIMITER:
 
2053
      {
 
2054
        while ((n = r->read((char *) &c, 1))) {
 
2055
          if (isspace(c) || (c == '=')) {
 
2056
            if (c == '=') {
 
2057
              _scan_state = FIND_ATTR_VALUE_START;
 
2058
              break;
 
2059
            }
 
2060
          } else {
 
2061
            _scan_state = SCAN_INIT;
 
2062
            break;
 
2063
          }
 
2064
        }
 
2065
        break;
 
2066
      }
 
2067
    case FIND_ATTR_VALUE_START:
 
2068
      {
 
2069
        while ((n = r->read((char *) &c, 1))) {
 
2070
          if (!isspace(c)) {
 
2071
            if (c == '>') {
 
2072
              /////////////////////////////
 
2073
              // <tag attr= >, ignore
 
2074
              /////////////////////////////
 
2075
              _scan_state = SCAN_INIT;
 
2076
              break;
 
2077
 
 
2078
            } else if ((c == '\'') || (c == '\"')) {
 
2079
              _attr_value_quoted = c;
 
2080
              _scan_state = COPY_ATTR_VALUE;
 
2081
              break;
 
2082
 
 
2083
            } else {
 
2084
              _attr_value_quoted = 0;
 
2085
              _attr_value(_attr_value.length()) = c;
 
2086
              _scan_state = COPY_ATTR_VALUE;
 
2087
              break;
 
2088
            }
 
2089
          }
 
2090
        }
 
2091
        break;
 
2092
      }
 
2093
    case COPY_ATTR_VALUE:
 
2094
      {
 
2095
        while ((n = r->read((char *) &c, 1))) {
 
2096
          if (_attr_value_quoted) {
 
2097
            if (c == _attr_value_quoted) {
 
2098
              ///////////////////////////////////////////
 
2099
              // We have a complete <tag attr='value'
 
2100
              ///////////////////////////////////////////
 
2101
              _attr_value(_attr_value.length()) = 0;
 
2102
              _scan_state = VALIDATE_ENTRY;
 
2103
              break;
 
2104
 
 
2105
            } else if (c == '\n') {
 
2106
              _scan_state = TERMINATE_COPY_ATTR_VALUE;
 
2107
              break;
 
2108
            } else {
 
2109
              _attr_value(_attr_value.length()) = c;
 
2110
              if (c == '#') {
 
2111
                _attr_value_hash_char_index = _attr_value.length() - 1;
 
2112
              }
 
2113
            }
 
2114
 
 
2115
          } else {
 
2116
            if (isspace(c)) {
 
2117
              ///////////////////////////////////////////
 
2118
              // We have a complete <tag attr=value
 
2119
              ///////////////////////////////////////////
 
2120
              _attr_value(_attr_value.length()) = 0;
 
2121
              _scan_state = VALIDATE_ENTRY;
 
2122
              break;
 
2123
 
 
2124
            } else if (c == '>') {
 
2125
              /////////////////////////////////////////
 
2126
              // We have a complete <tag attr=value>
 
2127
              /////////////////////////////////////////
 
2128
              _attr_value(_attr_value.length()) = 0;
 
2129
              _scan_state = VALIDATE_ENTRY_RESTART;
 
2130
              break;
 
2131
 
 
2132
            } else {
 
2133
              _attr_value(_attr_value.length()) = c;
 
2134
              if (c == '#') {
 
2135
                _attr_value_hash_char_index = _attr_value.length() - 1;
 
2136
              }
 
2137
            }
 
2138
          }
 
2139
        }
 
2140
        break;
 
2141
      }
 
2142
    case VALIDATE_ENTRY:
 
2143
    case VALIDATE_ENTRY_RESTART:
 
2144
      {
 
2145
        if (_scan_state == VALIDATE_ENTRY) {
 
2146
          _scan_state = RESUME_ATTR_VALUE_SCAN;
 
2147
        } else {
 
2148
          _scan_state = SCAN_INIT;
 
2149
        }
 
2150
        if (AllowTagAttrValue()) {
 
2151
          if (ExtractURL(url, url_end)) {
 
2152
            return 1;           // valid URL
 
2153
          }
 
2154
        }
 
2155
        break;                  // resume scan
 
2156
      }
 
2157
    case RESUME_ATTR_VALUE_SCAN:
 
2158
      {
 
2159
        _attr.clear();
 
2160
        _attr_value.clear();
 
2161
        _attr_value_hash_char_index = -1;
 
2162
        _attr_value_quoted = 0;
 
2163
 
 
2164
        _scan_state = FIND_ATTR_START;
 
2165
        n = -2;
 
2166
        break;
 
2167
      }
 
2168
    case TERMINATE_COPY_ATTR_VALUE:
 
2169
      {
 
2170
        while ((n = r->read((char *) &c, 1))) {
 
2171
          if (c == _attr_value_quoted) {
 
2172
            _scan_state = RESUME_ATTR_VALUE_SCAN;
 
2173
            break;
 
2174
          }
 
2175
        }
 
2176
        break;
 
2177
      }
 
2178
    default:
 
2179
      {
 
2180
        ink_release_assert(!"HtmlParser::ScanHtmlForURL bad state");
 
2181
      }
 
2182
    }                           // end of switch
 
2183
 
 
2184
    if (n == 0) {
 
2185
      return 0;                 // No more data
 
2186
    }
 
2187
 
 
2188
  }                             // end of while
 
2189
}
 
2190
 
 
2191
int
 
2192
HtmlParser::AllowTagAttrValue()
 
2193
{
 
2194
  struct html_tag *p_tag = allowable_html_tags;
 
2195
  struct html_tag *p_attr = allowable_html_attrs;
 
2196
 
 
2197
  if (!_tag || !_attr)
 
2198
    return 0;
 
2199
 
 
2200
  while (p_tag->tag && p_tag->attr) {
 
2201
    if (!strcasecmp(_tag, p_tag->tag)
 
2202
        && !strcasecmp(_attr, p_tag->attr)) {
 
2203
      if (p_attr == NULL || p_attr->tag == NULL)
 
2204
        return 1;
 
2205
      else if (_attr_matched) {
 
2206
        return 1;
 
2207
      } else {
 
2208
        // attributes don't match
 
2209
        return 0;
 
2210
      }
 
2211
    } else {
 
2212
      if (p_attr && p_attr->tag && p_attr->attr && _attr_value.length() > 0) {
 
2213
        if (!strcasecmp(_attr, p_attr->tag)
 
2214
            && !strcasecmp(_attr_value, p_attr->attr)) {
 
2215
          _attr_matched = true;
 
2216
        }
 
2217
      }
 
2218
      p_tag++;
 
2219
      if (p_attr)
 
2220
        p_attr++;
 
2221
    }
 
2222
  }
 
2223
  return 0;
 
2224
}
 
2225
 
 
2226
int
 
2227
HtmlParser::ValidProtoScheme(char *p)
 
2228
{
 
2229
  int n;
 
2230
  for (n = 0; proto_schemes[n].tag; ++n) {
 
2231
    if (!strncasecmp(p, proto_schemes[n].tag, proto_schemes[n].tag_len)) {
 
2232
      return 1;
 
2233
    }
 
2234
  }
 
2235
  return 0;
 
2236
}
 
2237
 
 
2238
int
 
2239
HtmlParser::ValidSupportedProtoScheme(char *p)
 
2240
{
 
2241
  int n;
 
2242
  for (n = 0; supported_proto_schemes[n].tag; ++n) {
 
2243
    if (!strncasecmp(p, supported_proto_schemes[n].tag, supported_proto_schemes[n].tag_len)) {
 
2244
      return 1;
 
2245
    }
 
2246
  }
 
2247
  return 0;
 
2248
}
 
2249
 
 
2250
int
 
2251
HtmlParser::ExtractURL(char **url, char **url_end)
 
2252
{
 
2253
  intptr_t n;
 
2254
 
 
2255
  // '#' considerations
 
2256
  if (_attr_value_hash_char_index >= 0) {
 
2257
    if (!_attr_value_hash_char_index) {
 
2258
      return 0;                 // No URL
 
2259
 
 
2260
    } else {
 
2261
      // '#' terminates _attr_value
 
2262
      _attr_value.set_length(_attr_value_hash_char_index + 1);
 
2263
      _attr_value[_attr_value_hash_char_index] = 0;
 
2264
    }
 
2265
  }
 
2266
 
 
2267
  if (!strcasecmp(_tag, "base") && !strcasecmp(_attr, "href")) {
 
2268
    if (_html_doc_base) {
 
2269
      _html_doc_base.clear();
 
2270
    }
 
2271
    for (n = 0; n < _attr_value.length(); ++n) {
 
2272
      _html_doc_base(_html_doc_base.length()) = _attr_value[n];
 
2273
    }
 
2274
    _html_doc_base(_html_doc_base.length()) = 0;
 
2275
    return 0;                   // No URL
 
2276
 
 
2277
  } else if (!strcasecmp(_tag, "meta") && !strcasecmp(_attr, "content")) {
 
2278
    /////////////////////////////////////////////////////////////////
 
2279
    // General form:
 
2280
    //      <META HTTP-EQUIV=Refresh CONTENT="0; URL=index.html">
 
2281
    /////////////////////////////////////////////////////////////////
 
2282
    if (_attr_value.length()) {
 
2283
      // Locate start of URL
 
2284
      for (n = 0; n < _attr_value.length(); ++n) {
 
2285
        if (!ParseRules::is_digit((unsigned char) _attr_value[n])) {
 
2286
          break;
 
2287
        }
 
2288
      }
 
2289
      if ((n < _attr_value.length()) && (((unsigned char) _attr_value[n]) == ';')) {
 
2290
 
 
2291
        for (; n < _attr_value.length(); ++n) {
 
2292
          if (!isspace((unsigned char) _attr_value[n])) {
 
2293
            break;
 
2294
          }
 
2295
        }
 
2296
        if ((n < _attr_value.length()) && (!strncasecmp(&_attr_value[n], "URL=", 4))) {
 
2297
          n += 4;
 
2298
          if ((n < _attr_value.length())
 
2299
              && ((_attr_value.length() - n) > 1)) {
 
2300
            *url = &_attr_value[n];
 
2301
            *url_end = &_attr_value[_attr_value.length() - 2];
 
2302
            return 1;
 
2303
          }
 
2304
        }
 
2305
      }
 
2306
      return 0;                 // No URL
 
2307
 
 
2308
    } else {
 
2309
      return 0;                 // No URL
 
2310
    }
 
2311
  }
 
2312
 
 
2313
  if (_attr_value.length() > 1) {
 
2314
    *url = &_attr_value[(intptr_t)0];
 
2315
    *url_end = &_attr_value[_attr_value.length() - 2];
 
2316
    return 1;
 
2317
 
 
2318
  } else {
 
2319
    return 0;                   // No URL
 
2320
  }
 
2321
}
 
2322
 
 
2323
int
 
2324
HtmlParser::ConstructURL(char **url, char **url_end)
 
2325
{
 
2326
  unsigned char *p_url = (unsigned char *) *url;
 
2327
  unsigned char *p_url_end = (unsigned char *) *url_end;
 
2328
 
 
2329
  /////////////////////////////////////////////////////////////////////
 
2330
  // Handle the <a href="[spaces]URI"> case by skipping over spaces
 
2331
  /////////////////////////////////////////////////////////////////////
 
2332
  while (p_url < p_url_end) {
 
2333
    if (isspace(*p_url)) {
 
2334
      ++p_url;
 
2335
    } else {
 
2336
      break;
 
2337
    }
 
2338
  }
 
2339
 
 
2340
  ////////////////////////////////////////////////////
 
2341
  // Determine if we have a relative or absolute URI
 
2342
  ////////////////////////////////////////////////////
 
2343
  int relative_URL = 0;
 
2344
  int http_needed = 0;
 
2345
  if (ValidProtoScheme((char *) p_url)) {
 
2346
    if (!strncasecmp((char *) p_url, "http:", 5)
 
2347
        && (strncasecmp((char *) p_url, "http://", 7) != 0)) {
 
2348
 
 
2349
      //////////////////////////////////////////////////////////
 
2350
      // Bad relative URI references of the form http:URL.
 
2351
      // Skip over the "http:" part.
 
2352
      //////////////////////////////////////////////////////////
 
2353
      p_url += strlen("http:");
 
2354
      if (p_url > p_url_end) {
 
2355
        return 0;               // Invalid URL
 
2356
      }
 
2357
      relative_URL = 1;
 
2358
    }
 
2359
  } else {
 
2360
    relative_URL = 1;
 
2361
    // problem found with www.slashdot.com
 
2362
    if (strncasecmp((char *) p_url, "//", 2) == 0)
 
2363
      http_needed = 1;
 
2364
  }
 
2365
 
 
2366
  //////////////////////////////////////////////
 
2367
  // Only handle supported protocol schemes
 
2368
  //////////////////////////////////////////////
 
2369
  if (!relative_URL && !ValidSupportedProtoScheme((char *) p_url)) {
 
2370
    return 0;                   // Invalid URL
 
2371
  }
 
2372
 
 
2373
  if (relative_URL) {
 
2374
    ////////////////////////////////////
 
2375
    // Compute document base path
 
2376
    ////////////////////////////////////
 
2377
    DynArray<char>*base = 0;
 
2378
    DynArray<char>*absolute_url = 0;
 
2379
 
 
2380
    if (http_needed) {
 
2381
      absolute_url = PrependString("http:", 5, (char *) p_url, (p_url_end - p_url + 2));
 
2382
    } else if (_html_doc_base.length()) {
 
2383
      ///////////////////////////////////////////////////////////////
 
2384
      // Document base specified via <base href="...">
 
2385
      ///////////////////////////////////////////////////////////////
 
2386
      base = MakeURL(_url, _html_doc_base, _html_doc_base.length(), !ValidProtoScheme(_html_doc_base));
 
2387
      absolute_url = MakeURL(*base, (char *) p_url, (p_url_end - p_url + 2), 1);
 
2388
    } else {
 
2389
      absolute_url = MakeURL(_url, (char *) p_url, (p_url_end - p_url + 2), 1);
 
2390
    }
 
2391
    _result.clear();
 
2392
    _result = *absolute_url;
 
2393
    absolute_url->detach();
 
2394
 
 
2395
    // fix INKqa07208; need to reclaim memory
 
2396
    delete absolute_url;
 
2397
    if (base)
 
2398
      delete base;
 
2399
 
 
2400
    *url = &_result[(intptr_t)0];
 
2401
    *url_end = &_result[_result.length() - 3];  // -1 (real len)
 
2402
    // -1 (skip null)
 
2403
    // -1 (zero base)
 
2404
  } else {
 
2405
    *url = (char *) p_url;
 
2406
    *url_end = (char *) p_url_end;
 
2407
  }
 
2408
 
 
2409
  //////////////////////////////////////////////////////////////////
 
2410
  // Determine if we have a terminal or non-terminal URL.
 
2411
  // URL ending with '/', .htm or .html is considered non-terminal.
 
2412
  //    Return < 0 ==> Terminal URL
 
2413
  //    Return > 0 ==> Non terminal URL
 
2414
  //////////////////////////////////////////////////////////////////
 
2415
  if (!strncasecmp((char *) (p_url_end - 4), ".html", 5)
 
2416
      || !strncasecmp((char *) (p_url_end - 3), ".htm", 4)
 
2417
      || !strncasecmp((char *) (p_url_end), "/", 1)) {
 
2418
    return 1;                   // Non-terminal URL
 
2419
  } else {
 
2420
    return -1;                  // Terminal URL
 
2421
  }
 
2422
}
 
2423
 
 
2424
DynArray<char>*
 
2425
HtmlParser::MakeURL(char *url, char *sub, int subsize, int relative_url)
 
2426
{
 
2427
  int i, n;
 
2428
  int skip_slashslash;
 
2429
 
 
2430
  DynArray<char>*result = NEW(new DynArray<char>(&default_zero_char, 128));
 
2431
 
 
2432
  if (relative_url) {
 
2433
    if (*sub != '/') {
 
2434
 
 
2435
      int url_len = strlen(url);
 
2436
 
 
2437
      // Locate last '/' in url
 
2438
      for (i = url_len; i && url[i] != '/'; i--);
 
2439
 
 
2440
      if (i && (url[i] == url[i - 1])) {
 
2441
        // http://hostname case with no terminating '/'
 
2442
 
 
2443
        for (n = 0; n < url_len; ++n) {
 
2444
          (*result) (result->length()) = url[n];
 
2445
        }
 
2446
        (*result) (result->length()) = '/';
 
2447
 
 
2448
      } else {
 
2449
        for (n = 0; n < (i + 1); ++n) {
 
2450
          (*result) (result->length()) = url[n];
 
2451
        }
 
2452
      }
 
2453
 
 
2454
      for (n = 0; n < subsize; ++n) {
 
2455
        (*result) (result->length()) = sub[n];
 
2456
      }
 
2457
      (*result) (result->length()) = '\0';
 
2458
 
 
2459
    } else {
 
2460
      i = 0;
 
2461
      do {
 
2462
        // Locate leading '/'
 
2463
        for (; url[i] && url[i] != '/'; i++);
 
2464
 
 
2465
        if (!url[i]) {
 
2466
          break;
 
2467
        }
 
2468
        // Skip over '<scheme>://'
 
2469
        skip_slashslash = ((url[i] == url[i + 1]) && (url[i + 1] == '/'));
 
2470
 
 
2471
        if (skip_slashslash) {
 
2472
          i += 2;
 
2473
        }
 
2474
      } while (skip_slashslash);
 
2475
 
 
2476
      for (n = 0; n < (i - 1); ++n) {
 
2477
        (*result) (result->length()) = url[n];
 
2478
      }
 
2479
 
 
2480
      if (url[n] != '/') {
 
2481
        (*result) (result->length()) = url[n];
 
2482
      }
 
2483
 
 
2484
      for (n = 0; n < subsize; ++n) {
 
2485
        (*result) (result->length()) = sub[n];
 
2486
      }
 
2487
      (*result) (result->length()) = '\0';
 
2488
    }
 
2489
 
 
2490
  } else {
 
2491
    for (n = 0; n < subsize; ++n) {
 
2492
      (*result) (result->length()) = sub[n];
 
2493
    }
 
2494
    (*result) (result->length()) = '\0';
 
2495
  }
 
2496
  return result;
 
2497
}
 
2498
 
 
2499
DynArray<char>*
 
2500
HtmlParser::PrependString(const char *pre, int presize, char *sub, int subsize)
 
2501
{
 
2502
  int n;
 
2503
 
 
2504
  DynArray<char>*result = NEW(new DynArray<char>(&default_zero_char, 128));
 
2505
 
 
2506
  for (n = 0; n < presize; ++n) {
 
2507
    (*result) (result->length()) = pre[n];
 
2508
  }
 
2509
  for (n = 0; n < subsize; ++n) {
 
2510
    (*result) (result->length()) = sub[n];
 
2511
  }
 
2512
  (*result) (result->length()) = '\0';
 
2513
 
 
2514
  return result;
 
2515
}
 
2516
 
 
2517
///////////////////////////////////////////////////////////////////
 
2518
// Class ObjectReloadCont
 
2519
//      Background load URL into local cache
 
2520
///////////////////////////////////////////////////////////////////
 
2521
ClassAllocator<ObjectReloadCont> ObjectReloadContAllocator("ObjectReloadCont");
 
2522
 
 
2523
ObjectReloadCont::ObjectReloadCont():Continuation(0),
 
2524
_caller_cont(0), _request_id(0), _send_data(0),
 
2525
_receive_data(0), _start_event(0),
 
2526
_state(START), _cur_action(0), _netvc(0), _write_vio(0), _read_vio(0), _read_event_callback(0)
 
2527
{
 
2528
  SET_HANDLER((ObjectReloadContHandler) & ObjectReloadCont::ObjectReloadEvent);
 
2529
}
 
2530
 
 
2531
ObjectReloadCont::~ObjectReloadCont()
 
2532
{
 
2533
}
 
2534
 
 
2535
void
 
2536
ObjectReloadCont::Init(Continuation * cont, char *url, int url_len,
 
2537
                       char *headers, int headers_len, int http_case, int read_event_callback)
 
2538
{
 
2539
  int total_len;
 
2540
 
 
2541
  mutex = new_ProxyMutex();
 
2542
  _caller_cont = cont;
 
2543
  _request_id = ink_atomic_increment(&global_id, 1);
 
2544
  _read_event_callback = read_event_callback;
 
2545
 
 
2546
  // Setup send data buffer by prepending the HTTP GET method to the
 
2547
  // given NULL terminated URL and terminating with HTTP version
 
2548
 
 
2549
  if (http_case) {
 
2550
    if (headers_len) {
 
2551
      total_len = len_GET_METHOD + url_len + len_HTTP_VERSION + len_TERMINATOR + headers_len + len_REQUEST_TERMINATOR;
 
2552
    } else {
 
2553
      total_len = len_GET_METHOD + url_len + len_HTTP_VERSION + len_REQUEST_TERMINATOR;
 
2554
    }
 
2555
    _send_data = new_MIOBuffer(buffer_size_to_index(total_len + 1));    // allow for NULL
 
2556
 
 
2557
    memcpy(_send_data->end(), GET_METHOD, len_GET_METHOD);
 
2558
    memcpy(&(_send_data->end())[len_GET_METHOD], url, url_len);
 
2559
    memcpy(&(_send_data->end())[len_GET_METHOD + url_len], HTTP_VERSION, len_HTTP_VERSION);
 
2560
 
 
2561
    if (headers_len) {
 
2562
      memcpy(&(_send_data->end())[len_GET_METHOD + url_len + len_HTTP_VERSION], TERMINATOR, len_TERMINATOR);
 
2563
      memcpy(&(_send_data->end())[len_GET_METHOD + url_len + len_HTTP_VERSION + len_TERMINATOR], headers, headers_len);
 
2564
      memcpy(&(_send_data->end())[len_GET_METHOD + url_len +
 
2565
                                  len_HTTP_VERSION + len_TERMINATOR +
 
2566
                                  headers_len], REQUEST_TERMINATOR, len_REQUEST_TERMINATOR);
 
2567
 
 
2568
      // Add NULL for Debug URL output
 
2569
      (_send_data->end())[len_GET_METHOD + url_len +
 
2570
                          len_HTTP_VERSION + len_TERMINATOR + headers_len + len_REQUEST_TERMINATOR] = 0;
 
2571
    } else {
 
2572
      memcpy(&(_send_data->end())[len_GET_METHOD + url_len +
 
2573
                                  len_HTTP_VERSION], REQUEST_TERMINATOR, len_REQUEST_TERMINATOR);
 
2574
 
 
2575
      // Add NULL for Debug URL output
 
2576
      (_send_data->end())[len_GET_METHOD + url_len + len_HTTP_VERSION + len_REQUEST_TERMINATOR] = 0;
 
2577
    }
 
2578
    _send_data->fill(total_len);
 
2579
 
 
2580
  } else {
 
2581
    // Unhandled case... TODO: Do we need to actually handle this?
 
2582
    ink_debug_assert(false);
 
2583
  }
 
2584
  handleEvent(EVENT_IMMEDIATE, (void *) NULL);
 
2585
}
 
2586
 
 
2587
void
 
2588
ObjectReloadCont::free()
 
2589
{
 
2590
  mutex = 0;
 
2591
  if (_send_data) {
 
2592
    free_MIOBuffer(_send_data);
 
2593
    _send_data = 0;
 
2594
  }
 
2595
  if (_receive_data) {
 
2596
    free_MIOBuffer(_receive_data);
 
2597
    _receive_data = 0;
 
2598
  }
 
2599
}
 
2600
 
 
2601
int
 
2602
ObjectReloadCont::ObjectReloadEvent(int event, void *d)
 
2603
{
 
2604
  switch (_state) {
 
2605
  case START:
 
2606
    {
 
2607
      // Schedule connect to localhost:<proxy port>
 
2608
      Debug("update-reload", "Connect start id=%d", _request_id);
 
2609
      _state = ObjectReloadCont::ATTEMPT_CONNECT;
 
2610
      MUTEX_TRY_LOCK(lock, this->mutex, this_ethread());
 
2611
      ink_release_assert(lock);
 
2612
      _cur_action = netProcessor.connect_re(this, inet_addr("127.0.0.1"), local_http_server_port);
 
2613
      return EVENT_DONE;
 
2614
    }
 
2615
  case ATTEMPT_CONNECT:
 
2616
    {
 
2617
      if (event != NET_EVENT_OPEN) {
 
2618
        // Connect error, terminate processing
 
2619
        Debug("update-reload", "Connect fail id=%d", _request_id);
 
2620
        CallBackUser(event, 0);
 
2621
        free();
 
2622
        ObjectReloadContAllocator.free(this);
 
2623
        return EVENT_DONE;
 
2624
      }
 
2625
      _netvc = (class NetVConnection *) d;
 
2626
 
 
2627
      // Start URL write
 
2628
      Debug("update-reload", "Write start id=%d [%s]", _request_id, _send_data->start());
 
2629
      _state = ObjectReloadCont::WRITING_URL;
 
2630
      IOBufferReader *r = _send_data->alloc_reader();
 
2631
      _write_vio = _netvc->do_io_write(this, r->read_avail(), r);
 
2632
      return EVENT_DONE;
 
2633
    }
 
2634
  case WRITING_URL:
 
2635
    {
 
2636
      ink_release_assert(_write_vio == (VIO *) d);
 
2637
      if (event == VC_EVENT_WRITE_READY) {
 
2638
        _write_vio->reenable();
 
2639
        return EVENT_DONE;
 
2640
      } else if (event == VC_EVENT_WRITE_COMPLETE) {
 
2641
        // Write successful, start read
 
2642
        Debug("update-reload", "Read start id=%d", _request_id);
 
2643
        _state = ObjectReloadCont::READING_DATA;
 
2644
        _receive_data = new_MIOBuffer(max_iobuffer_size);
 
2645
        _receive_data_reader = _receive_data->alloc_reader();
 
2646
        _read_vio = _netvc->do_io_read(this, INT64_MAX, _receive_data);
 
2647
        return EVENT_DONE;
 
2648
      } else {
 
2649
        // Write error, terminate processing
 
2650
        Debug("update-reload", "Write fail id=%d", _request_id);
 
2651
        _netvc->do_io(VIO::CLOSE);
 
2652
        CallBackUser(event, 0);
 
2653
        free();
 
2654
        ObjectReloadContAllocator.free(this);
 
2655
        return EVENT_DONE;
 
2656
      }
 
2657
    }
 
2658
  case READING_DATA:
 
2659
    {
 
2660
      ink_release_assert(_read_vio == (VIO *) d);
 
2661
      switch (event) {
 
2662
      case VC_EVENT_READ_READY:
 
2663
        {
 
2664
          if (_read_event_callback) {
 
2665
            _caller_cont->handleEvent(event, _receive_data_reader);
 
2666
 
 
2667
          } else {
 
2668
            int64_t read_bytes = _receive_data_reader->read_avail();
 
2669
            _receive_data_reader->consume(read_bytes);
 
2670
            _read_vio->reenable();
 
2671
          }
 
2672
          return EVENT_CONT;
 
2673
        }
 
2674
      case VC_EVENT_READ_COMPLETE:
 
2675
      case VC_EVENT_EOS:
 
2676
        {
 
2677
          if (_read_event_callback) {
 
2678
            _caller_cont->handleEvent(event, _receive_data_reader);
 
2679
          }
 
2680
          // Object injected into local cache
 
2681
          Debug("update-reload", "Fill success id=%d", _request_id);
 
2682
          break;
 
2683
        }
 
2684
      default:
 
2685
        {
 
2686
          Debug("update-reload", "Fill read fail id=%d", _request_id);
 
2687
          CallBackUser(event, 0);
 
2688
          break;
 
2689
        }
 
2690
      }                         // End of switch
 
2691
 
 
2692
      _netvc->do_io(VIO::CLOSE);
 
2693
      free();
 
2694
      ObjectReloadContAllocator.free(this);
 
2695
      return EVENT_DONE;
 
2696
    }
 
2697
  default:
 
2698
    {
 
2699
      ink_release_assert(!"ObjectReloadEvent invalid state");
 
2700
    }
 
2701
 
 
2702
  }                             // End of switch
 
2703
  return 0;
 
2704
}
 
2705
 
 
2706
int
 
2707
ObjectReloadCont::CallBackUser(int event, void *d)
 
2708
{
 
2709
  _caller_cont->handleEvent(event, d);
 
2710
  return 0;
 
2711
}
 
2712
 
 
2713
// End of Update.cc