Browse code

sbc message logging b/f: propagate logger to register cache

use call profile to propagate logger instance

Václav Kubart authored on 14/08/2013 07:48:34
Showing 10 changed files
... ...
@@ -793,7 +793,8 @@ bool _RegisterCache::findAEByContact(const string& contact_uri,
793 793
 
794 794
 
795 795
 int _RegisterCache::parseAoR(RegisterCacheCtx& ctx,
796
-			     const AmSipRequest& req)
796
+			     const AmSipRequest& req,
797
+                             msg_logger *logger)
797 798
 {
798 799
   if(ctx.aor_parsed)
799 800
     return 0;
... ...
@@ -802,7 +803,7 @@ int _RegisterCache::parseAoR(RegisterCacheCtx& ctx,
802 803
   size_t end_from = 0;
803 804
   if(!from_parser.parse_contact(req.from,0,end_from)) {
804 805
     DBG("error parsing AoR: '%s'\n",req.from.c_str());
805
-    AmBasicSipDialog::reply_error(req,400,"Bad request - bad From HF");
806
+    AmBasicSipDialog::reply_error(req,400,"Bad request - bad From HF", "", logger);
806 807
     return -1;
807 808
   }
808 809
 
... ...
@@ -810,7 +811,7 @@ int _RegisterCache::parseAoR(RegisterCacheCtx& ctx,
810 811
   DBG("parsed AOR: '%s'",ctx.from_aor.c_str());
811 812
 
812 813
   if(ctx.from_aor.empty()) {
813
-    AmBasicSipDialog::reply_error(req,400,"Bad request - bad From HF");
814
+    AmBasicSipDialog::reply_error(req,400,"Bad request - bad From HF", "", logger);
814 815
     return -1;
815 816
   }
816 817
   ctx.aor_parsed = true;
... ...
@@ -819,7 +820,8 @@ int _RegisterCache::parseAoR(RegisterCacheCtx& ctx,
819 820
 }
820 821
 
821 822
 int _RegisterCache::parseContacts(RegisterCacheCtx& ctx,
822
-				  const AmSipRequest& req)
823
+				  const AmSipRequest& req,
824
+                                  msg_logger *logger)
823 825
 {
824 826
   if(ctx.contacts_parsed)
825 827
     return 0;
... ...
@@ -827,7 +829,7 @@ int _RegisterCache::parseContacts(RegisterCacheCtx& ctx,
827 829
   if ((RegisterDialog::parseContacts(req.contact, ctx.contacts) < 0) ||
828 830
       (ctx.contacts.size() == 0)) {
829 831
     AmBasicSipDialog::reply_error(req, 400, "Bad Request", 
830
-				  "Warning: Malformed contact\r\n");
832
+				  "Warning: Malformed contact\r\n", logger);
831 833
     return -1;
832 834
   }
833 835
   ctx.contacts_parsed = true;
... ...
@@ -835,7 +837,8 @@ int _RegisterCache::parseContacts(RegisterCacheCtx& ctx,
835 837
 }
836 838
 
837 839
 int _RegisterCache::parseExpires(RegisterCacheCtx& ctx,
838
-				 const AmSipRequest& req)
840
+				 const AmSipRequest& req,
841
+                                 msg_logger *logger)
839 842
 {
840 843
   if(ctx.expires_parsed)
841 844
     return 0;
... ...
@@ -844,7 +847,7 @@ int _RegisterCache::parseExpires(RegisterCacheCtx& ctx,
844 847
   string expires_str = getHeader(req.hdrs, "Expires");
845 848
   if (!expires_str.empty() && str2i(expires_str, ctx.requested_expires)) {
846 849
     AmBasicSipDialog::reply_error(req, 400, "Bad Request", 
847
-				  "Warning: Malformed expires\r\n");
850
+				  "Warning: Malformed expires\r\n", logger);
848 851
     return true; // error reply sent
849 852
   }
850 853
   ctx.expires_parsed = true;
... ...
@@ -852,7 +855,8 @@ int _RegisterCache::parseExpires(RegisterCacheCtx& ctx,
852 855
 }
853 856
 
854 857
 bool _RegisterCache::throttleRegister(RegisterCacheCtx& ctx,
855
-				      const AmSipRequest& req)
858
+				      const AmSipRequest& req,
859
+                                      msg_logger *logger)
856 860
 {
857 861
   if (req.method != SIP_METH_REGISTER) {
858 862
     ERROR("unsupported method '%s'\n", req.method.c_str());
... ...
@@ -865,9 +869,9 @@ bool _RegisterCache::throttleRegister(RegisterCacheCtx& ctx,
865 869
     return false; // fwd
866 870
   }
867 871
 
868
-  if ((parseAoR(ctx,req) < 0) ||
869
-      (parseContacts(ctx,req) < 0) ||
870
-      (parseExpires(ctx,req) < 0)) {
872
+  if ((parseAoR(ctx,req, logger) < 0) ||
873
+      (parseContacts(ctx,req, logger) < 0) ||
874
+      (parseExpires(ctx,req, logger) < 0)) {
871 875
     DBG("could not parse AoR, Contact or Expires\n");
872 876
     return true; // error reply sent
873 877
   }
... ...
@@ -898,7 +902,7 @@ bool _RegisterCache::throttleRegister(RegisterCacheCtx& ctx,
898 902
     else {
899 903
       if(!str2long(expires_it->second,contact_expires)) {
900 904
 	AmBasicSipDialog::reply_error(req, 400, "Bad Request",
901
-				      "Warning: Malformed expires\r\n");
905
+				      "Warning: Malformed expires\r\n", logger);
902 906
 	return true; // error reply sent
903 907
       }
904 908
 
... ...
@@ -976,19 +980,20 @@ bool _RegisterCache::throttleRegister(RegisterCacheCtx& ctx,
976 980
   contact_hdr += CRLF;
977 981
 
978 982
   // send 200 reply
979
-  AmBasicSipDialog::reply_error(req, 200, "OK", contact_hdr);
983
+  AmBasicSipDialog::reply_error(req, 200, "OK", contact_hdr, logger);
980 984
   return true;
981 985
 }
982 986
 
983 987
 bool _RegisterCache::saveSingleContact(RegisterCacheCtx& ctx,
984
-				       const AmSipRequest& req)
988
+				       const AmSipRequest& req,
989
+                                       msg_logger *logger)
985 990
 {
986 991
   if (req.method != SIP_METH_REGISTER) {
987 992
     ERROR("unsupported method '%s'\n", req.method.c_str());
988 993
     return false;
989 994
   }
990 995
 
991
-  if(parseAoR(ctx,req) < 0) {
996
+  if(parseAoR(ctx,req, logger) < 0) {
992 997
     return true;
993 998
   }
994 999
 
... ...
@@ -1023,7 +1028,7 @@ bool _RegisterCache::saveSingleContact(RegisterCacheCtx& ctx,
1023 1028
     // unregister everything
1024 1029
     star_contact = true;
1025 1030
 
1026
-    if(parseExpires(ctx,req) < 0) {
1031
+    if(parseExpires(ctx,req, logger) < 0) {
1027 1032
       return true;
1028 1033
     }
1029 1034
 
... ...
@@ -1033,13 +1038,13 @@ bool _RegisterCache::saveSingleContact(RegisterCacheCtx& ctx,
1033 1038
       return true;
1034 1039
     }
1035 1040
   }
1036
-  else if ((parseContacts(ctx,req) < 0) ||
1037
-	   (parseExpires(ctx,req) < 0)) {
1041
+  else if ((parseContacts(ctx,req, logger) < 0) ||
1042
+	   (parseExpires(ctx,req, logger) < 0)) {
1038 1043
     return true; // error reply sent
1039 1044
   }
1040 1045
   else if (ctx.contacts.size() != 1) {
1041 1046
     AmBasicSipDialog::reply_error(req, 403, "Forbidden",
1042
-				  "Warning: only one contact allowed\r\n");
1047
+				  "Warning: only one contact allowed\r\n", logger);
1043 1048
     return true; // error reply sent
1044 1049
   }
1045 1050
   else {
... ...
@@ -1050,7 +1055,7 @@ bool _RegisterCache::saveSingleContact(RegisterCacheCtx& ctx,
1050 1055
 	  contact->params["expires"].c_str());
1051 1056
       if(str2i(contact->params["expires"],contact_expires)) {
1052 1057
 	AmBasicSipDialog::reply_error(req, 400, "Bad Request",
1053
-				      "Warning: Malformed expires\r\n");
1058
+				      "Warning: Malformed expires\r\n", logger);
1054 1059
 	return true; // error reply sent
1055 1060
       }
1056 1061
       DBG("contact_expires = %u",contact_expires);
... ...
@@ -1063,7 +1068,7 @@ bool _RegisterCache::saveSingleContact(RegisterCacheCtx& ctx,
1063 1068
   if(!contact_expires) {
1064 1069
     // unregister AoR
1065 1070
     remove(ctx.from_aor);
1066
-    AmBasicSipDialog::reply_error(req, 200, "OK");
1071
+    AmBasicSipDialog::reply_error(req, 200, "OK", "", logger);
1067 1072
     return true;
1068 1073
   }
1069 1074
   assert(contact);  
... ...
@@ -233,9 +233,9 @@ protected:
233 233
 				  const string& remote_ip,
234 234
 				  unsigned short remote_port);
235 235
 
236
-  int parseAoR(RegisterCacheCtx& ctx, const AmSipRequest& req);
237
-  int parseContacts(RegisterCacheCtx& ctx, const AmSipRequest& req);
238
-  int parseExpires(RegisterCacheCtx& ctx, const AmSipRequest& req);
236
+  int parseAoR(RegisterCacheCtx& ctx, const AmSipRequest& req, msg_logger *logger);
237
+  int parseContacts(RegisterCacheCtx& ctx, const AmSipRequest& req, msg_logger *logger);
238
+  int parseExpires(RegisterCacheCtx& ctx, const AmSipRequest& req, msg_logger *logger);
239 239
 
240 240
   void setAliasUATimer(AliasEntry* alias_e);
241 241
   void removeAliasUATimer(AliasEntry* alias_e);
... ...
@@ -327,7 +327,8 @@ public:
327 327
    * - if request is not a REGISTER
328 328
    */
329 329
   bool throttleRegister(RegisterCacheCtx& ctx,
330
-			const AmSipRequest& req);
330
+			const AmSipRequest& req,
331
+                        msg_logger *logger);
331 332
 
332 333
   /**
333 334
    * Save a single REGISTER contact into cache
... ...
@@ -343,7 +344,8 @@ public:
343 344
    *       (REGISTER w/o contacts)
344 345
    */
345 346
   bool saveSingleContact(RegisterCacheCtx& ctx,
346
-			const AmSipRequest& req);
347
+			const AmSipRequest& req,
348
+                        msg_logger *logger);
347 349
 };
348 350
 
349 351
 typedef singleton<_RegisterCache> RegisterCache;
... ...
@@ -288,7 +288,6 @@ SBCCallProfile* SBCFactory::getActiveProfileMatch(const AmSipRequest& req,
288 288
   return &prof_it->second;
289 289
 }
290 290
 
291
-
292 291
 AmSession* SBCFactory::onInvite(const AmSipRequest& req, const string& app_name,
293 292
 				const map<string,string>& app_params)
294 293
 {
... ...
@@ -303,6 +302,7 @@ AmSession* SBCFactory::onInvite(const AmSipRequest& req, const string& app_name,
303 302
   }
304 303
 
305 304
   const SBCCallProfile& call_profile = *p_call_profile;
305
+
306 306
   if(!call_profile.refuse_with.empty()) {
307 307
     if(call_profile.refuse(ctx, req) < 0) {
308 308
       profiles_mut.unlock();
... ...
@@ -314,6 +314,9 @@ AmSession* SBCFactory::onInvite(const AmSipRequest& req, const string& app_name,
314 314
 
315 315
   SBCCallLeg* b2b_dlg = callLegCreator->create(call_profile);
316 316
 
317
+  msg_logger* logger = b2b_dlg->getCallProfile().get_logger(req);
318
+  if (logger && call_profile.log_sip) req.log(logger);
319
+
317 320
   if (call_profile.auth_aleg_enabled) {
318 321
     // adding auth handler
319 322
     AmSessionEventHandlerFactory* uac_auth_f =
... ...
@@ -365,6 +368,9 @@ void SBCFactory::onOoDRequest(const AmSipRequest& req)
365 368
   SBCCallProfile call_profile(*p_call_profile);
366 369
   profiles_mut.unlock();
367 370
 
371
+  msg_logger* logger = call_profile.get_logger(req);
372
+  if (logger && call_profile.log_sip) req.log(logger);
373
+
368 374
   ctx.call_profile = &call_profile;
369 375
   call_profile.eval_cc_list(ctx,req);
370 376
 
... ...
@@ -374,20 +380,16 @@ void SBCFactory::onOoDRequest(const AmSipRequest& req)
374 380
     return;
375 381
   }
376 382
 
377
-  msg_logger *logger = NULL;
378
-
379 383
   // fix up variables
380 384
   call_profile.replace_cc_values(ctx,req,NULL);
381
-  if(!SBCFactory::CCRoute(req,cc_modules,call_profile, logger)) {
385
+  if(!SBCFactory::CCRoute(req,cc_modules,call_profile)) {
382 386
     oodHandlingTerminated(req, cc_modules, call_profile);
383 387
     //ERROR("routing failed\n");
384
-    if (logger) delete logger;
385 388
     return;
386 389
   }
387 390
 
388 391
   if (!call_profile.refuse_with.empty()) {
389 392
     oodHandlingTerminated(req, cc_modules, call_profile);
390
-    if (logger) delete logger;
391 393
     if(call_profile.refuse(ctx, req) < 0) {
392 394
       throw AmSession::Exception(500, SIP_REPLY_SERVER_INTERNAL_ERROR);
393 395
     }
... ...
@@ -408,19 +410,17 @@ void SBCFactory::onOoDRequest(const AmSipRequest& req)
408 410
   else {
409 411
     relay = simpleRelayCreator->createGenericRelay(call_profile, cc_modules);
410 412
   }
411
-  if (logger) inc_ref(logger);
412 413
   if (call_profile.log_sip) {
413
-    relay.first->setMsgLogger(logger);
414
-    relay.second->setMsgLogger(logger);
414
+    relay.first->setMsgLogger(call_profile.get_logger(req));
415
+    relay.second->setMsgLogger(call_profile.get_logger(req));
415 416
   }
416 417
 
417 418
   if(SBCSimpleRelay::start(relay,req,call_profile)) {
418 419
     AmSipDialog::reply_error(req, 500, SIP_REPLY_SERVER_INTERNAL_ERROR, 
419
-			     "", call_profile.log_sip ? logger: NULL);
420
+			     "", call_profile.log_sip ? call_profile.get_logger(req): NULL);
420 421
     delete relay.first;
421 422
     delete relay.second;
422 423
   }
423
-  if (logger) dec_ref(logger);
424 424
 }
425 425
 
426 426
 void SBCFactory::invoke(const string& method, const AmArg& args, 
... ...
@@ -685,8 +685,7 @@ void SBCFactory::postControlCmd(const AmArg& args, AmArg& ret) {
685 685
 
686 686
 bool SBCFactory::CCRoute(const AmSipRequest& req,
687 687
 			 vector<AmDynInvoke*>& cc_modules,
688
-			 SBCCallProfile& call_profile,
689
-                         msg_logger* &logger)
688
+			 SBCCallProfile& call_profile)
690 689
 {
691 690
   vector<AmDynInvoke*>::iterator cc_mod=cc_modules.begin();
692 691
 
... ...
@@ -731,35 +730,6 @@ bool SBCFactory::CCRoute(const AmSipRequest& req,
731 730
       return false;
732 731
     }
733 732
 
734
-    if (!logger && !call_profile.msg_logger_path.empty()) {
735
-
736
-      ParamReplacerCtx ctx(&call_profile);
737
-      call_profile.msg_logger_path = 
738
-	ctx.replaceParameters(call_profile.msg_logger_path,
739
-			      "msg_logger_path",req);
740
-
741
-      if(!call_profile.msg_logger_path.empty()) {
742
-	pcap_logger *log = new pcap_logger();
743
-	if(log->open(call_profile.msg_logger_path.c_str())) {
744
-	  ERROR("could not open message logger\n");
745
-          delete log;
746
-	  call_profile.msg_logger_path.clear();
747
-	}
748
-        else logger = log;
749
-      }
750
-
751
-      if(logger && call_profile.log_sip) {
752
-        req.tt.lock_bucket();
753
-        const sip_trans* t = req.tt.get_trans();
754
-        if (t) {
755
-          sip_msg* msg = t->msg;
756
-          logger->log(msg->buf,msg->len,&msg->remote_ip,
757
-              &msg->local_ip,msg->u.request->method_str);
758
-        }
759
-        req.tt.unlock_bucket();
760
-      }
761
-    }
762
-
763 733
     // evaluate ret
764 734
     if (isArgArray(ret)) {
765 735
       for (size_t i=0;i<ret.size();i++) {
... ...
@@ -801,7 +771,7 @@ bool SBCFactory::CCRoute(const AmSipRequest& req,
801 771
 	  AmBasicSipDialog::reply_error(req,
802 772
 	        ret[i][SBC_CC_REFUSE_CODE].asInt(), 
803 773
 		ret[i][SBC_CC_REFUSE_REASON].asCStr(),
804
-		headers, call_profile.log_sip ? logger: NULL);
774
+		headers, call_profile.log_sip ? call_profile.get_logger(req): NULL);
805 775
 
806 776
 	  return false;
807 777
 	}
... ...
@@ -91,8 +91,7 @@ class SBCFactory: public AmSessionFactory,
91 91
   
92 92
   bool CCRoute(const AmSipRequest& req,
93 93
 	       vector<AmDynInvoke*>& cc_modules,
94
-	       SBCCallProfile& call_profile,
95
-               msg_logger *&logger);
94
+	       SBCCallProfile& call_profile);
96 95
 
97 96
  public:
98 97
   DECLARE_MODULE_INSTANCE(SBCFactory);
... ...
@@ -1079,12 +1079,10 @@ bool SBCCallLeg::CCStart(const AmSipRequest& req) {
1079 1079
       return false;
1080 1080
     }
1081 1081
 
1082
-    if (!logger && !call_profile.msg_logger_path.empty()) {
1082
+    if (!logger) {
1083 1083
       // open the logger if not already opened
1084
-      ParamReplacerCtx ctx(&call_profile);
1085
-      string log_path = ctx.replaceParameters(call_profile.msg_logger_path,
1086
-					      "msg_logger_path",req);
1087
-      if (openLogger(log_path)) logRequest(req);
1084
+      msg_logger *l = call_profile.get_logger(req);
1085
+      if (l) setLogger(l);
1088 1086
     }
1089 1087
 
1090 1088
     // evaluate ret
... ...
@@ -37,6 +37,8 @@
37 37
 #include "SDPFilter.h"
38 38
 #include "RegisterCache.h"
39 39
 
40
+#include "sip/pcap_logger.h"
41
+
40 42
 typedef vector<SdpPayload>::iterator PayloadIterator;
41 43
 static string payload2str(const SdpPayload &p);
42 44
 
... ...
@@ -1548,6 +1550,32 @@ bool SBCCallProfile::TranscoderSettings::evaluate(ParamReplacerCtx& ctx,
1548 1550
   return true;
1549 1551
 }
1550 1552
 
1553
+void SBCCallProfile::create_logger(const AmSipRequest& req)
1554
+{
1555
+  if (msg_logger_path.empty()) return;
1556
+
1557
+  ParamReplacerCtx ctx(this);
1558
+  string log_path = ctx.replaceParameters(msg_logger_path, "msg_logger_path", req);
1559
+  if (log_path.empty()) return;
1560
+
1561
+  file_msg_logger *log = new pcap_logger();
1562
+
1563
+  if(log->open(log_path.c_str()) != 0) {
1564
+    // open error
1565
+    delete log;
1566
+    return;
1567
+  }
1568
+
1569
+  // opened successfully
1570
+  logger.reset(log);
1571
+}
1572
+
1573
+msg_logger* SBCCallProfile::get_logger(const AmSipRequest& req)
1574
+{
1575
+  if (!logger.get() && !msg_logger_path.empty()) create_logger(req);
1576
+  return logger.get();
1577
+}
1578
+
1551 1579
 //////////////////////////////////////////////////////////////////////////////////
1552 1580
 
1553 1581
 bool PayloadDesc::match(const SdpPayload &p) const
... ...
@@ -31,6 +31,7 @@
31 31
 #include "ampi/UACAuthAPI.h"
32 32
 #include "ParamReplacer.h"
33 33
 #include "atomic_types.h"
34
+#include "sip/msg_logger.h"
34 35
 
35 36
 #include <set>
36 37
 #include <string>
... ...
@@ -59,6 +60,24 @@ struct CCInterface {
59 60
 typedef std::list<CCInterface> CCInterfaceListT;
60 61
 typedef CCInterfaceListT::iterator CCInterfaceListIteratorT;
61 62
 
63
+template <class T>
64
+class ref_counted_ptr
65
+{
66
+  private:
67
+    T *ptr;
68
+
69
+  public:
70
+    void reset(T *p) { if (ptr) dec_ref(ptr); ptr = p; if (ptr) inc_ref(ptr); }
71
+    T *get() const { return ptr; }
72
+
73
+    ref_counted_ptr(): ptr(0) { }
74
+    ~ref_counted_ptr() { if (ptr) dec_ref(ptr); }
75
+
76
+    ref_counted_ptr(const ref_counted_ptr &other): ptr(other.ptr) { if (ptr) inc_ref(ptr); }
77
+    ref_counted_ptr &operator=(const ref_counted_ptr &other) { reset(other.ptr); return *this; }
78
+
79
+};
80
+
62 81
 class PayloadDesc {
63 82
   protected:
64 83
     std::string name;
... ...
@@ -257,10 +276,20 @@ struct SBCCallProfile
257 276
 
258 277
   // todo: RTP transcoding mode
259 278
 
279
+ private:
260 280
   // message logging feature
261 281
   string msg_logger_path;
282
+  ref_counted_ptr<msg_logger> logger;
283
+
284
+  void create_logger(const AmSipRequest& req);
285
+
286
+ public:
262 287
   bool log_rtp;
263 288
   bool log_sip;
289
+  bool has_logger() { return logger.get() != NULL; }
290
+  msg_logger* get_logger(const AmSipRequest& req);
291
+  void set_logger_path(const std::string path) { msg_logger_path = path; }
292
+  const string &get_logger_path() { return msg_logger_path; }
264 293
 
265 294
   SBCCallProfile()
266 295
   : auth_enabled(false),
... ...
@@ -298,12 +298,9 @@ void AmBasicSipDialog::onRxRequest(const AmSipRequest& req)
298 298
   DBG("AmBasicSipDialog::onRxRequest(req = %s)\n", req.method.c_str());
299 299
 
300 300
   if(logger && (req.method != SIP_METH_ACK)) {
301
-    req.tt.lock_bucket();
302
-    const sip_trans* t = req.tt.get_trans();
303
-    sip_msg* msg = t->msg;
304
-    logger->log(msg->buf,msg->len,&msg->remote_ip,
305
-		&msg->local_ip,msg->u.request->method_str);
306
-    req.tt.unlock_bucket();
301
+    // log only non-initial received requests, the initial one is already logged
302
+    // or will be logged at application level (problem with SBCSimpleRelay)
303
+    if (!callid.empty()) req.log(logger);
307 304
   }
308 305
 
309 306
   if(!onRxReqSanity(req))
... ...
@@ -3,6 +3,9 @@
3 3
 #include "AmUtils.h"
4 4
 #include "AmSipMsg.h"
5 5
 #include "AmSipHeaders.h"
6
+#include "sip/sip_trans.h"
7
+#include "sip/sip_parser.h"
8
+#include "sip/msg_logger.h"
6 9
 
7 10
 string getHeader(const string& hdrs,const string& hdr_name, bool single)
8 11
 {
... ...
@@ -232,6 +235,20 @@ string AmSipRequest::print() const
232 235
   return buf;
233 236
 }
234 237
 
238
+
239
+void AmSipRequest::log(msg_logger *logger) const
240
+{
241
+  tt.lock_bucket();
242
+  const sip_trans* t = tt.get_trans();
243
+  if (t) {
244
+    sip_msg* msg = t->msg;
245
+    logger->log(msg->buf,msg->len,&msg->remote_ip,
246
+        &msg->local_ip,msg->u.request->method_str);
247
+  }
248
+  tt.unlock_bucket();
249
+}
250
+
251
+
235 252
 string AmSipReply::print() const
236 253
 {
237 254
   string buf;
... ...
@@ -87,6 +87,7 @@ class AmSipRequest : public _AmSipMsgInDlg
87 87
   ~AmSipRequest() { }
88 88
   
89 89
   string print() const;
90
+  void log(msg_logger *logger) const;
90 91
 };
91 92
 
92 93
 string getHeader(const string& hdrs,const string& hdr_name, bool single = false);