1 /** @file
2 
3   A brief file description
4 
5   @section license License
6 
7   Licensed to the Apache Software Foundation (ASF) under one
8   or more contributor license agreements.  See the NOTICE file
9   distributed with this work for additional information
10   regarding copyright ownership.  The ASF licenses this file
11   to you under the Apache License, Version 2.0 (the
12   "License"); you may not use this file except in compliance
13   with the License.  You may obtain a copy of the License at
14 
15       http://www.apache.org/licenses/LICENSE-2.0
16 
17   Unless required by applicable law or agreed to in writing, software
18   distributed under the License is distributed on an "AS IS" BASIS,
19   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
20   See the License for the specific language governing permissions and
21   limitations under the License.
22  */
23 
24 /***************************************************************************
25  LogField.cc
26 
27  This file implements the LogField object, which is the central
28  representation of a logging field.
29  ***************************************************************************/
30 #include "tscore/ink_platform.h"
31 
32 #include "MIME.h"
33 #include "LogUtils.h"
34 #include "LogField.h"
35 #include "LogBuffer.h"
36 #include "LogAccess.h"
37 #include "Log.h"
38 
39 // clang-format off
40 //
41 static const char *container_names[] = {
42   "not-a-container",
43   "cqh",
44   "psh",
45   "pqh",
46   "ssh",
47   "cssh",
48   "ecqh",
49   "epsh",
50   "epqh",
51   "essh",
52   "ecssh",
53   "icfg",
54   "scfg",
55   "record",
56   "ms",
57   "msdms",
58 };
59 
60 static const char *aggregate_names[] = {
61   "not-an-agg-op",
62   "COUNT",
63   "SUM",
64   "AVG",
65   "FIRST",
66   "LAST",
67 };
68 
69 // clang-format on
70 
LogSlice(char * str)71 LogSlice::LogSlice(char *str)
72 {
73   char *a, *b, *c;
74 
75   m_enable = false;
76   m_start  = 0;
77   m_end    = INT_MAX;
78 
79   if ((a = strchr(str, '[')) == nullptr) {
80     return;
81   }
82 
83   *a++ = '\0';
84   if ((b = strchr(a, ':')) == nullptr) {
85     return;
86   }
87 
88   *b++ = '\0';
89   if ((c = strchr(b, ']')) == nullptr) {
90     return;
91   }
92 
93   m_enable = true;
94 
95   // eat space
96   while (a != b && *a == ' ') {
97     a++;
98   }
99 
100   if (a != b) {
101     m_start = atoi(a);
102   }
103 
104   // eat space
105   while (b != c && *b == ' ') {
106     b++;
107   }
108 
109   if (b != c) {
110     m_end = atoi(b);
111   }
112 }
113 
114 int
toStrOffset(int strlen,int * offset)115 LogSlice::toStrOffset(int strlen, int *offset)
116 {
117   int i, j, len;
118 
119   // letf index
120   if (m_start >= 0) {
121     i = m_start;
122   } else {
123     i = m_start + strlen;
124   }
125 
126   if (i >= strlen) {
127     return 0;
128   }
129 
130   if (i < 0) {
131     i = 0;
132   }
133 
134   // right index
135   if (m_end >= 0) {
136     j = m_end;
137   } else {
138     j = m_end + strlen;
139   }
140 
141   if (j <= 0) {
142     return 0;
143   }
144 
145   if (j > strlen) {
146     j = strlen;
147   }
148 
149   // available length
150   len = j - i;
151 
152   if (len > 0) {
153     *offset = i;
154   }
155 
156   return len;
157 }
158 
159 /*-------------------------------------------------------------------------
160   LogField::LogField
161   -------------------------------------------------------------------------*/
162 
163 namespace
164 {
165 struct cmp_str {
166   bool
operator ()__anon9b62b7cd0111::cmp_str167   operator()(ts::ConstBuffer a, ts::ConstBuffer b) const
168   {
169     return ptr_len_casecmp(a._ptr, a._size, b._ptr, b._size) < 0;
170   }
171 };
172 } // namespace
173 
174 using milestone_map = std::map<ts::ConstBuffer, TSMilestonesType, cmp_str>;
175 static milestone_map m_milestone_map;
176 
177 struct milestone {
178   const char *msname;
179   TSMilestonesType mstype;
180 };
181 
182 static const milestone milestones[] = {
183   {"TS_MILESTONE_UA_BEGIN", TS_MILESTONE_UA_BEGIN},
184   {"TS_MILESTONE_UA_FIRST_READ", TS_MILESTONE_UA_FIRST_READ},
185   {"TS_MILESTONE_UA_READ_HEADER_DONE", TS_MILESTONE_UA_READ_HEADER_DONE},
186   {"TS_MILESTONE_UA_BEGIN_WRITE", TS_MILESTONE_UA_BEGIN_WRITE},
187   {"TS_MILESTONE_UA_CLOSE", TS_MILESTONE_UA_CLOSE},
188   {"TS_MILESTONE_SERVER_FIRST_CONNECT", TS_MILESTONE_SERVER_FIRST_CONNECT},
189   {"TS_MILESTONE_SERVER_CONNECT", TS_MILESTONE_SERVER_CONNECT},
190   {"TS_MILESTONE_SERVER_CONNECT_END", TS_MILESTONE_SERVER_CONNECT_END},
191   {"TS_MILESTONE_SERVER_BEGIN_WRITE", TS_MILESTONE_SERVER_BEGIN_WRITE},
192   {"TS_MILESTONE_SERVER_FIRST_READ", TS_MILESTONE_SERVER_FIRST_READ},
193   {"TS_MILESTONE_SERVER_READ_HEADER_DONE", TS_MILESTONE_SERVER_READ_HEADER_DONE},
194   {"TS_MILESTONE_SERVER_CLOSE", TS_MILESTONE_SERVER_CLOSE},
195   {"TS_MILESTONE_CACHE_OPEN_READ_BEGIN", TS_MILESTONE_CACHE_OPEN_READ_BEGIN},
196   {"TS_MILESTONE_CACHE_OPEN_READ_END", TS_MILESTONE_CACHE_OPEN_READ_END},
197   {"TS_MILESTONE_CACHE_OPEN_WRITE_BEGIN", TS_MILESTONE_CACHE_OPEN_WRITE_BEGIN},
198   {"TS_MILESTONE_CACHE_OPEN_WRITE_END", TS_MILESTONE_CACHE_OPEN_WRITE_END},
199   {"TS_MILESTONE_DNS_LOOKUP_BEGIN", TS_MILESTONE_DNS_LOOKUP_BEGIN},
200   {"TS_MILESTONE_DNS_LOOKUP_END", TS_MILESTONE_DNS_LOOKUP_END},
201   {"TS_MILESTONE_SM_START", TS_MILESTONE_SM_START},
202   {"TS_MILESTONE_SM_FINISH", TS_MILESTONE_SM_FINISH},
203   {"TS_MILESTONE_PLUGIN_ACTIVE", TS_MILESTONE_PLUGIN_ACTIVE},
204   {"TS_MILESTONE_PLUGIN_TOTAL", TS_MILESTONE_PLUGIN_TOTAL},
205   {"TS_MILESTONE_TLS_HANDSHAKE_START", TS_MILESTONE_TLS_HANDSHAKE_START},
206   {"TS_MILESTONE_TLS_HANDSHAKE_END", TS_MILESTONE_TLS_HANDSHAKE_END},
207 };
208 
209 void
init_milestone_container()210 LogField::init_milestone_container()
211 {
212   if (m_milestone_map.empty()) {
213     for (unsigned i = 0; i < countof(milestones); ++i) {
214       m_milestone_map.insert(
215         std::make_pair(ts::ConstBuffer(milestones[i].msname, strlen(milestones[i].msname)), milestones[i].mstype));
216     }
217   }
218 }
219 
220 // Generic field ctor
LogField(const char * name,const char * symbol,Type type,MarshalFunc marshal,UnmarshalFunc unmarshal,SetFunc _setfunc)221 LogField::LogField(const char *name, const char *symbol, Type type, MarshalFunc marshal, UnmarshalFunc unmarshal, SetFunc _setfunc)
222   : m_name(ats_strdup(name)),
223     m_symbol(ats_strdup(symbol)),
224     m_type(type),
225     m_container(NO_CONTAINER),
226     m_marshal_func(marshal),
227     m_unmarshal_func(unmarshal),
228     m_unmarshal_func_map(nullptr),
229     m_agg_op(NO_AGGREGATE),
230     m_agg_cnt(0),
231     m_agg_val(0),
232     m_milestone1(TS_MILESTONE_LAST_ENTRY),
233     m_milestone2(TS_MILESTONE_LAST_ENTRY),
234     m_time_field(false),
235     m_alias_map(nullptr),
236     m_set_func(_setfunc)
237 {
238   ink_assert(m_name != nullptr);
239   ink_assert(m_symbol != nullptr);
240   ink_assert(m_type >= 0 && m_type < N_TYPES);
241   ink_assert(m_marshal_func != (MarshalFunc) nullptr);
242 
243   m_time_field = (strcmp(m_symbol, "cqts") == 0 || strcmp(m_symbol, "cqth") == 0 || strcmp(m_symbol, "cqtq") == 0 ||
244                   strcmp(m_symbol, "cqtn") == 0 || strcmp(m_symbol, "cqtd") == 0 || strcmp(m_symbol, "cqtt") == 0);
245 }
246 
LogField(const char * name,const char * symbol,Type type,MarshalFunc marshal,UnmarshalFuncWithMap unmarshal,const Ptr<LogFieldAliasMap> & map,SetFunc _setfunc)247 LogField::LogField(const char *name, const char *symbol, Type type, MarshalFunc marshal, UnmarshalFuncWithMap unmarshal,
248                    const Ptr<LogFieldAliasMap> &map, SetFunc _setfunc)
249   : m_name(ats_strdup(name)),
250     m_symbol(ats_strdup(symbol)),
251     m_type(type),
252     m_container(NO_CONTAINER),
253     m_marshal_func(marshal),
254     m_unmarshal_func(nullptr),
255     m_unmarshal_func_map(unmarshal),
256     m_agg_op(NO_AGGREGATE),
257     m_agg_cnt(0),
258     m_agg_val(0),
259     m_milestone1(TS_MILESTONE_LAST_ENTRY),
260     m_milestone2(TS_MILESTONE_LAST_ENTRY),
261     m_time_field(false),
262     m_alias_map(map),
263     m_set_func(_setfunc)
264 {
265   ink_assert(m_name != nullptr);
266   ink_assert(m_symbol != nullptr);
267   ink_assert(m_type >= 0 && m_type < N_TYPES);
268   ink_assert(m_marshal_func != (MarshalFunc) nullptr);
269   ink_assert(m_alias_map);
270 
271   m_time_field = (strcmp(m_symbol, "cqts") == 0 || strcmp(m_symbol, "cqth") == 0 || strcmp(m_symbol, "cqtq") == 0 ||
272                   strcmp(m_symbol, "cqtn") == 0 || strcmp(m_symbol, "cqtd") == 0 || strcmp(m_symbol, "cqtt") == 0);
273 }
274 
275 TSMilestonesType
milestone_from_m_name()276 LogField::milestone_from_m_name()
277 {
278   milestone_map::iterator it;
279   TSMilestonesType result = TS_MILESTONE_LAST_ENTRY;
280 
281   it = m_milestone_map.find(ts::ConstBuffer(m_name, strlen(m_name)));
282   if (it != m_milestone_map.end()) {
283     result = it->second;
284   }
285 
286   return result;
287 }
288 
289 int
milestones_from_m_name(TSMilestonesType * ms1,TSMilestonesType * ms2)290 LogField::milestones_from_m_name(TSMilestonesType *ms1, TSMilestonesType *ms2)
291 {
292   milestone_map::iterator it;
293   ts::ConstBuffer ms1_name, ms2_name(m_name, strlen(m_name));
294 
295   ms1_name = ms2_name.splitOn('-');
296 
297   it = m_milestone_map.find(ms1_name);
298   if (it != m_milestone_map.end()) {
299     *ms1 = it->second;
300   } else {
301     return -1;
302   }
303 
304   it = m_milestone_map.find(ms2_name);
305   if (it != m_milestone_map.end()) {
306     *ms2 = it->second;
307   } else {
308     return -1;
309   }
310 
311   return 0;
312 }
313 
314 // Container field ctor
LogField(const char * field,Container container,SetFunc _setfunc)315 LogField::LogField(const char *field, Container container, SetFunc _setfunc)
316   : m_name(ats_strdup(field)),
317     m_symbol(ats_strdup(container_names[container])),
318     m_type(LogField::STRING),
319     m_container(container),
320     m_marshal_func(nullptr),
321     m_unmarshal_func(nullptr),
322     m_unmarshal_func_map(nullptr),
323     m_agg_op(NO_AGGREGATE),
324     m_agg_cnt(0),
325     m_agg_val(0),
326     m_milestone1(TS_MILESTONE_LAST_ENTRY),
327     m_milestone2(TS_MILESTONE_LAST_ENTRY),
328     m_time_field(false),
329     m_alias_map(nullptr),
330     m_set_func(nullptr)
331 {
332   ink_assert(m_name != nullptr);
333   ink_assert(m_symbol != nullptr);
334   ink_assert(m_type >= 0 && m_type < N_TYPES);
335 
336   m_time_field = (strcmp(m_symbol, "cqts") == 0 || strcmp(m_symbol, "cqth") == 0 || strcmp(m_symbol, "cqtq") == 0 ||
337                   strcmp(m_symbol, "cqtn") == 0 || strcmp(m_symbol, "cqtd") == 0 || strcmp(m_symbol, "cqtt") == 0);
338 
339   switch (m_container) {
340   case CQH:
341   case PSH:
342   case PQH:
343   case SSH:
344   case CSSH:
345   case ECQH:
346   case EPSH:
347   case EPQH:
348   case ESSH:
349   case ECSSH:
350   case SCFG:
351     m_unmarshal_func = reinterpret_cast<UnmarshalFunc>(&(LogAccess::unmarshal_str));
352     break;
353 
354   case ICFG:
355     m_unmarshal_func = &(LogAccess::unmarshal_int_to_str);
356     break;
357 
358   case RECORD:
359     m_unmarshal_func = &(LogAccess::unmarshal_record);
360     break;
361 
362   case MS:
363     m_milestone1 = milestone_from_m_name();
364     if (TS_MILESTONE_LAST_ENTRY == m_milestone1) {
365       Note("Invalid milestone name in LogField ctor: %s", m_name);
366     }
367     m_unmarshal_func = &(LogAccess::unmarshal_int_to_str);
368     break;
369 
370   case MSDMS: {
371     int rv = milestones_from_m_name(&m_milestone1, &m_milestone2);
372     if (0 != rv) {
373       Note("Invalid milestone range in LogField ctor: %s", m_name);
374     }
375     m_unmarshal_func = &(LogAccess::unmarshal_int_to_str);
376     break;
377   }
378 
379   default:
380     Note("Invalid container type in LogField ctor: %d", container);
381   }
382 }
383 
384 // Copy ctor
LogField(const LogField & rhs)385 LogField::LogField(const LogField &rhs)
386   : m_name(ats_strdup(rhs.m_name)),
387     m_symbol(ats_strdup(rhs.m_symbol)),
388     m_type(rhs.m_type),
389     m_container(rhs.m_container),
390     m_marshal_func(rhs.m_marshal_func),
391     m_unmarshal_func(rhs.m_unmarshal_func),
392     m_unmarshal_func_map(rhs.m_unmarshal_func_map),
393     m_agg_op(rhs.m_agg_op),
394     m_agg_cnt(0),
395     m_agg_val(0),
396     m_milestone1(TS_MILESTONE_LAST_ENTRY),
397     m_milestone2(TS_MILESTONE_LAST_ENTRY),
398     m_time_field(rhs.m_time_field),
399     m_alias_map(rhs.m_alias_map),
400     m_set_func(rhs.m_set_func)
401 {
402   ink_assert(m_name != nullptr);
403   ink_assert(m_symbol != nullptr);
404   ink_assert(m_type >= 0 && m_type < N_TYPES);
405 }
406 
407 /*-------------------------------------------------------------------------
408   LogField::~LogField
409   -------------------------------------------------------------------------*/
~LogField()410 LogField::~LogField()
411 {
412   ats_free(m_name);
413   ats_free(m_symbol);
414 }
415 
416 /*-------------------------------------------------------------------------
417   LogField::marshal_len
418 
419   This routine will find the marshalling length (in bytes) for this field,
420   given a LogAccess object.  It does this by using the property of the
421   marshalling routines that if the marshal buffer is NULL, only the size
422   requirement is returned.
423   -------------------------------------------------------------------------*/
424 unsigned
marshal_len(LogAccess * lad)425 LogField::marshal_len(LogAccess *lad)
426 {
427   if (m_container == NO_CONTAINER) {
428     return (lad->*m_marshal_func)(nullptr);
429   }
430 
431   switch (m_container) {
432   case CQH:
433   case PSH:
434   case PQH:
435   case SSH:
436   case CSSH:
437     return lad->marshal_http_header_field(m_container, m_name, nullptr);
438 
439   case ECQH:
440   case EPSH:
441   case EPQH:
442   case ESSH:
443   case ECSSH:
444     return lad->marshal_http_header_field_escapify(m_container, m_name, nullptr);
445 
446   case ICFG:
447     return lad->marshal_config_int_var(m_name, nullptr);
448 
449   case SCFG:
450     return lad->marshal_config_str_var(m_name, nullptr);
451 
452   case RECORD:
453     return lad->marshal_record(m_name, nullptr);
454 
455   case MS:
456     return lad->marshal_milestone(m_milestone1, nullptr);
457 
458   case MSDMS:
459     return lad->marshal_milestone_diff(m_milestone1, m_milestone2, nullptr);
460 
461   default:
462     return 0;
463   }
464 }
465 
466 bool
isContainerUpdateFieldSupported(Container container)467 LogField::isContainerUpdateFieldSupported(Container container)
468 {
469   switch (container) {
470   case CQH:
471   case PSH:
472   case PQH:
473   case SSH:
474   case CSSH:
475   case ECQH:
476   case EPSH:
477   case EPQH:
478   case ESSH:
479   case ECSSH:
480   case SCFG:
481     return true;
482   default:
483     return false;
484   }
485 }
486 
487 void
updateField(LogAccess * lad,char * buf,int len)488 LogField::updateField(LogAccess *lad, char *buf, int len)
489 {
490   if (m_container == NO_CONTAINER) {
491     return (lad->*m_set_func)(buf, len);
492   } else {
493     if (isContainerUpdateFieldSupported(m_container)) {
494       return set_http_header_field(lad, m_container, this->m_name, buf, len);
495     } else {
496       // no set function defined for the container
497     }
498   }
499 }
500 
501 /*-------------------------------------------------------------------------
502   LogField::marshal
503 
504   This routine will marshsal the given field into the buffer provided.
505   -------------------------------------------------------------------------*/
506 unsigned
marshal(LogAccess * lad,char * buf)507 LogField::marshal(LogAccess *lad, char *buf)
508 {
509   if (m_container == NO_CONTAINER) {
510     return (lad->*m_marshal_func)(buf);
511   }
512 
513   switch (m_container) {
514   case CQH:
515   case PSH:
516   case PQH:
517   case SSH:
518   case CSSH:
519     return lad->marshal_http_header_field(m_container, m_name, buf);
520 
521   case ECQH:
522   case EPSH:
523   case EPQH:
524   case ESSH:
525   case ECSSH:
526     return lad->marshal_http_header_field_escapify(m_container, m_name, buf);
527 
528   case ICFG:
529     return lad->marshal_config_int_var(m_name, buf);
530 
531   case SCFG:
532     return lad->marshal_config_str_var(m_name, buf);
533 
534   case RECORD:
535     return lad->marshal_record(m_name, buf);
536 
537   case MS:
538     return lad->marshal_milestone(m_milestone1, buf);
539 
540   case MSDMS:
541     return lad->marshal_milestone_diff(m_milestone1, m_milestone2, buf);
542 
543   default:
544     return 0;
545   }
546 }
547 
548 /*-------------------------------------------------------------------------
549   LogField::marshal_agg
550   -------------------------------------------------------------------------*/
551 unsigned
marshal_agg(char * buf)552 LogField::marshal_agg(char *buf)
553 {
554   ink_assert(buf != nullptr);
555   int64_t avg = 0;
556 
557   switch (m_agg_op) {
558   case eCOUNT:
559     LogAccess::marshal_int(buf, m_agg_cnt);
560     break;
561 
562   case eSUM:
563   case eFIRST:
564   case eLAST:
565     LogAccess::marshal_int(buf, m_agg_val);
566     break;
567 
568   case eAVG:
569     if (m_agg_cnt) {
570       avg = m_agg_val / m_agg_cnt;
571     }
572     LogAccess::marshal_int(buf, avg);
573     break;
574 
575   default:
576     Note("Cannot marshal aggregate field %s; "
577          "invalid aggregate operator: %d",
578          m_symbol, m_agg_op);
579     return 0;
580   }
581 
582   m_agg_val = 0;
583   m_agg_cnt = 0;
584 
585   return INK_MIN_ALIGN;
586 }
587 
588 /*-------------------------------------------------------------------------
589   LogField::unmarshal
590 
591   This routine will invoke the proper unmarshalling routine to return a
592   string that represents the ASCII value of the field.
593   -------------------------------------------------------------------------*/
594 unsigned
unmarshal(char ** buf,char * dest,int len)595 LogField::unmarshal(char **buf, char *dest, int len)
596 {
597   if (!m_alias_map) {
598     if (m_unmarshal_func == reinterpret_cast<UnmarshalFunc>(LogAccess::unmarshal_str) ||
599         m_unmarshal_func == reinterpret_cast<UnmarshalFunc>(LogAccess::unmarshal_http_text)) {
600       UnmarshalFuncWithSlice func = reinterpret_cast<UnmarshalFuncWithSlice>(m_unmarshal_func);
601       return (*func)(buf, dest, len, &m_slice);
602     }
603     return (*m_unmarshal_func)(buf, dest, len);
604   } else {
605     return (*m_unmarshal_func_map)(buf, dest, len, m_alias_map);
606   }
607 }
608 
609 /*-------------------------------------------------------------------------
610   LogField::display
611   -------------------------------------------------------------------------*/
612 void
display(FILE * fd)613 LogField::display(FILE *fd)
614 {
615   static const char *names[LogField::N_TYPES] = {"sINT", "dINT", "STR", "IP"};
616 
617   fprintf(fd, "    %30s %10s %5s\n", m_name, m_symbol, names[m_type]);
618 }
619 
620 /*-------------------------------------------------------------------------
621   LogField::operator==
622 
623   This operator does only care of the name and m_symbol, may need
624   do check on others layter.
625   -------------------------------------------------------------------------*/
626 bool
operator ==(LogField & rhs)627 LogField::operator==(LogField &rhs)
628 {
629   if (strcmp(name(), rhs.name()) || strcmp(symbol(), rhs.symbol())) {
630     return false;
631   } else {
632     return true;
633   }
634 }
635 
636 /*-------------------------------------------------------------------------
637   -------------------------------------------------------------------------*/
638 void
set_aggregate_op(LogField::Aggregate agg_op)639 LogField::set_aggregate_op(LogField::Aggregate agg_op)
640 {
641   if (agg_op > 0 && agg_op < LogField::N_AGGREGATES) {
642     m_agg_op = agg_op;
643   } else {
644     Note("Invalid aggregate operator identifier: %d", agg_op);
645     m_agg_op = NO_AGGREGATE;
646   }
647 }
648 
649 void
update_aggregate(int64_t val)650 LogField::update_aggregate(int64_t val)
651 {
652   switch (m_agg_op) {
653   case eCOUNT:
654   case eSUM:
655   case eAVG:
656     m_agg_val += val;
657     m_agg_cnt++;
658     break;
659 
660   case eFIRST:
661     if (m_agg_cnt == 0) {
662       m_agg_val = val;
663       m_agg_cnt++;
664     }
665     break;
666 
667   case eLAST:
668     m_agg_val = val;
669     m_agg_cnt++;
670     break;
671 
672   default:
673     Note("Cannot update aggregate field; invalid operator %d", m_agg_op);
674     return;
675   }
676 
677   Debug("log-agg",
678         "Aggregate field %s updated with val %" PRId64 ", "
679         "new val = %" PRId64 ", cnt = %" PRId64 "",
680         m_symbol, val, m_agg_val, m_agg_cnt);
681 }
682 
683 LogField::Container
valid_container_name(char * name)684 LogField::valid_container_name(char *name)
685 {
686   for (unsigned i = 1; i < countof(container_names); i++) {
687     if (strcmp(name, container_names[i]) == 0) {
688       return static_cast<LogField::Container>(i);
689     }
690   }
691 
692   return LogField::NO_CONTAINER;
693 }
694 
695 LogField::Aggregate
valid_aggregate_name(char * name)696 LogField::valid_aggregate_name(char *name)
697 {
698   for (unsigned i = 1; i < countof(aggregate_names); i++) {
699     if (strcmp(name, aggregate_names[i]) == 0) {
700       return static_cast<LogField::Aggregate>(i);
701     }
702   }
703 
704   return LogField::NO_AGGREGATE;
705 }
706 
707 bool
fieldlist_contains_aggregates(const char * fieldlist)708 LogField::fieldlist_contains_aggregates(const char *fieldlist)
709 {
710   const char *match;
711 
712   for (unsigned i = 1; i < countof(aggregate_names); i++) {
713     if ((match = strstr(fieldlist, aggregate_names[i])) != nullptr) {
714       // verify that the aggregate string is not part of a container field name.
715       if ((strchr(fieldlist, '{') == nullptr) && (strchr(match, '}') == nullptr)) {
716         return true;
717       }
718     }
719   }
720 
721   return false;
722 }
723 
724 void
set_http_header_field(LogAccess * lad,LogField::Container container,char * field,char * buf,int len)725 LogField::set_http_header_field(LogAccess *lad, LogField::Container container, char *field, char *buf, int len)
726 {
727   return lad->set_http_header_field(container, field, buf, len);
728 }
729 
730 /*-------------------------------------------------------------------------
731   LogFieldList
732 
733   It is ASSUMED that each element on this list has been allocated from the
734   heap with "new" and that each element is on at most ONE list.  To enforce
735   this, items are copied by default, using the copy ctor.
736   -------------------------------------------------------------------------*/
737 LogFieldList::LogFieldList() = default;
738 
~LogFieldList()739 LogFieldList::~LogFieldList()
740 {
741   clear();
742 }
743 
744 void
clear()745 LogFieldList::clear()
746 {
747   LogField *f;
748   while ((f = m_field_list.dequeue())) {
749     delete f; // safe given the semantics stated above
750   }
751   m_marshal_len = 0;
752   _badSymbols.clear();
753 }
754 
755 void
add(LogField * field,bool copy)756 LogFieldList::add(LogField *field, bool copy)
757 {
758   ink_assert(field != nullptr);
759 
760   if (copy) {
761     m_field_list.enqueue(new LogField(*field));
762   } else {
763     m_field_list.enqueue(field);
764   }
765 
766   if (field->type() == LogField::sINT) {
767     m_marshal_len += INK_MIN_ALIGN;
768   }
769 }
770 
771 LogField *
find_by_name(const char * name) const772 LogFieldList::find_by_name(const char *name) const
773 {
774   for (LogField *f = first(); f; f = next(f)) {
775     if (!strcmp(f->name(), name)) {
776       return f;
777     }
778   }
779   return nullptr;
780 }
781 
782 LogField *
find_by_symbol(const char * symbol) const783 LogFieldList::find_by_symbol(const char *symbol) const
784 {
785   LogField *field = nullptr;
786 
787   if (auto it = Log::field_symbol_hash.find(symbol); it != Log::field_symbol_hash.end() && it->second) {
788     field = it->second;
789     Debug("log-field-hash", "Field %s found", field->symbol());
790     return field;
791   }
792   // trusty old method
793 
794   for (field = first(); field; field = next(field)) {
795     if (!strcmp(field->symbol(), symbol)) {
796       return field;
797     }
798   }
799 
800   return nullptr;
801 }
802 
803 unsigned
marshal_len(LogAccess * lad)804 LogFieldList::marshal_len(LogAccess *lad)
805 {
806   int bytes = 0;
807   for (LogField *f = first(); f; f = next(f)) {
808     if (f->type() != LogField::sINT) {
809       const int len = f->marshal_len(lad);
810       ink_release_assert(len >= INK_MIN_ALIGN);
811       bytes += len;
812     }
813   }
814   return m_marshal_len + bytes;
815 }
816 
817 unsigned
marshal(LogAccess * lad,char * buf)818 LogFieldList::marshal(LogAccess *lad, char *buf)
819 {
820   char *ptr;
821   int bytes = 0;
822   for (LogField *f = first(); f; f = next(f)) {
823     ptr = &buf[bytes];
824     bytes += f->marshal(lad, ptr);
825     ink_assert(bytes % INK_MIN_ALIGN == 0);
826   }
827   return bytes;
828 }
829 
830 unsigned
marshal_agg(char * buf)831 LogFieldList::marshal_agg(char *buf)
832 {
833   char *ptr;
834   int bytes = 0;
835   for (LogField *f = first(); f; f = next(f)) {
836     ptr = &buf[bytes];
837     bytes += f->marshal_agg(ptr);
838   }
839   return bytes;
840 }
841 
842 unsigned
count()843 LogFieldList::count()
844 {
845   unsigned cnt = 0;
846   for (LogField *f = first(); f; f = next(f)) {
847     cnt++;
848   }
849   return cnt;
850 }
851 
852 void
display(FILE * fd)853 LogFieldList::display(FILE *fd)
854 {
855   for (LogField *f = first(); f; f = next(f)) {
856     f->display(fd);
857   }
858 }
859 
860 void
addBadSymbol(std::string_view badSymbol)861 LogFieldList::addBadSymbol(std::string_view badSymbol)
862 {
863   if (_badSymbols.size() > 0) {
864     _badSymbols += ", ";
865   }
866   _badSymbols += badSymbol;
867 }
868