On 02/24/2014 01:40 AM, Amos Jeffries wrote:
> On 2014-02-24 10:12, Tsantilas Christos wrote:
>> Hi all,
>>
>> This patch add the url_rewrite_extras for redirector helpers.
>> The url_rewrite_extras is a "quoted string" with logformat %macro
>> support. It is appended to request line for redirector helpers.
>>
>> Example usage:
>>    url_rewrite_extras "Note1=%{Note1}note Note2=%{Note2}note"
>>
>> The url_rewrtite_extras it is similar to the "key_extras" authenticator
>> helpers options. Originally developed to allow notes exchange between
>> authenicator helpers and redirector helpers.
>>
>> In this patch we add a new type for cf.data the TokenOrQuotedString to
>> allow new configurations use quoted strings for the new option without
>> explicitly turning configuration_includes_quoted_values on.
>>
>> This is a Measurement Factory project.
> 
> 
> in src/cf.data.pre:
>  * why are you calling this new directive an alias of a non-existent
> "format" directive?
> "
> +NAME: url_rewrite_extras format
> "
> 
> A few design bits:
> 
> 1) Please make the redirectors existing parameters except URL the
> default value for this new directive. There is no reason to force those
> parameters to always be sent and for some users they are more pain than
> benefit as-is.
> 
> eg:
>  new helper protocol format:   [channel-ID] URL [' ' extras]
> 
> new directive DEFAULT value:  "%>a/%>A %un %>rm myip=%la myport=%lp"

Well I believe it is not bad idea....
I implement this one in this patch.
De we need others opinion? we are changing helpers protocol....

> 
> 
> 2) Please also make this same change mirrored for the store-ID helper.

OK.
My first patch was wrong because it was changed the request format for
store-ID helpers too.
It should be OK now.


> 
> 
> Amos
> 
> 

Add url_rewrite_extras and store_id_extras for redirector and store_id helpers

The url_rewrite_extras/store_id_extras is a "quoted string" with logformat
%macro support. It is used to modify the request line for redirector and
storeId helpers.

The url rewrite and store_id helpers request format now is:
       url [<SP> extras]
and the default value for extras is:
  "%>a/%>A %un %>rm myip=%la myport=%lp"

Example usage:
   url_rewrite_extras "Note1=%{Note1}note Note2=%{Note2}note"

This is a Measurement Factory project.
=== modified file 'src/SquidConfig.h'
--- src/SquidConfig.h	2014-01-12 17:51:12 +0000
+++ src/SquidConfig.h	2014-02-24 15:57:49 +0000
@@ -523,40 +523,44 @@
         char *flags;
         acl_access *cert_error;
         SSL_CTX *sslContext;
         sslproxy_cert_sign *cert_sign;
         sslproxy_cert_adapt *cert_adapt;
     } ssl_client;
 #endif
 
     char *accept_filter;
     int umask;
     int max_filedescriptors;
     int workers;
     CpuAffinityMap *cpuAffinityMap;
 
 #if USE_LOADABLE_MODULES
     wordlist *loadable_module_names;
 #endif
 
     int client_ip_max_connections;
 
+    char *redirector_extras;
+
+    char *storeId_extras;
+
     struct {
         int v4_first;       ///< Place IPv4 first in the order of DNS results.
         ssize_t packet_max; ///< maximum size EDNS advertised for DNS replies.
     } dns;
 
 };
 
 extern SquidConfig Config;
 
 class SquidConfig2
 {
 public:
     struct {
         int enable_purge;
         int mangle_request_headers;
     } onoff;
     uid_t effectiveUserID;
     gid_t effectiveGroupID;
 };
 

=== modified file 'src/cache_cf.cc'
--- src/cache_cf.cc	2014-02-10 09:59:19 +0000
+++ src/cache_cf.cc	2014-02-23 20:44:06 +0000
@@ -3020,40 +3020,55 @@
     if (!token) {
         self_destruct();
         return;
     }
 
     while (*token && xisspace(*token))
         ++token;
 
     if (!*token) {
         self_destruct();
         return;
     }
 
     *var = xstrdup((char *) token);
 }
 
 #define dump_eol dump_string
 #define free_eol free_string
 
 static void
+parse_TokenOrQuotedString(char **var)
+{
+    char *token = ConfigParser::NextQuotedToken();
+    safe_free(*var);
+
+    if (token == NULL)
+        self_destruct();
+
+    *var = xstrdup(token);
+}
+
+#define dump_TokenOrQuotedString dump_string
+#define free_TokenOrQuotedString free_string
+
+static void
 dump_time_t(StoreEntry * entry, const char *name, time_t var)
 {
     storeAppendPrintf(entry, "%s %d seconds\n", name, (int) var);
 }
 
 void
 parse_time_t(time_t * var)
 {
     time_msec_t tval;
     parseTimeLine(&tval, T_SECOND_STR, false);
     *var = static_cast<time_t>(tval/1000);
 }
 
 static void
 free_time_t(time_t * var)
 {
     *var = 0;
 }
 
 static void

=== modified file 'src/cf.data.depend'
--- src/cf.data.depend	2013-08-29 09:21:53 +0000
+++ src/cf.data.depend	2014-02-23 20:44:21 +0000
@@ -38,39 +38,40 @@
 adaptation_service_chain_type	icap_service ecap_service
 icap_access_type	icap_class acl
 icap_class_type		icap_service
 icap_service_type
 icap_service_failure_limit
 ecap_service_type
 int
 kb_int64_t
 kb_size_t
 logformat
 YesNoNone
 memcachemode
 note			acl
 obsolete
 onoff
 peer
 peer_access		cache_peer acl
 pipelinePrefetch
 PortCfg
 QosConfig
+TokenOrQuotedString
 refreshpattern
 removalpolicy
 size_t
 IpAddress_list
 string
 string
 time_msec
 time_t
 tristate
 uri_whitespace
 u_short
 wccp2_method
 wccp2_amethod
 wccp2_service
 wccp2_service_info
 wordlist
 sslproxy_ssl_bump	acl
 sslproxy_cert_sign	acl
 sslproxy_cert_adapt	acl

=== modified file 'src/cf.data.pre'
--- src/cf.data.pre	2014-01-30 21:24:44 +0000
+++ src/cf.data.pre	2014-02-24 16:32:19 +0000
@@ -4590,41 +4590,41 @@
 	Enables turning ICMP pinger on and off with a simple
 	squid -k reconfigure.
 DOC_END
 
 
 COMMENT_START
  OPTIONS FOR URL REWRITING
  -----------------------------------------------------------------------------
 COMMENT_END
 
 NAME: url_rewrite_program redirect_program
 TYPE: wordlist
 LOC: Config.Program.redirect
 DEFAULT: none
 DOC_START
 	Specify the location of the executable URL rewriter to use.
 	Since they can perform almost any function there isn't one included.
 
 	For each requested URL, the rewriter will receive on line with the format
 
-	  [channel-ID <SP>] URL <SP> client_ip "/" fqdn <SP> user <SP> method [<SP> kv-pairs]<NL>
+	  [channel-ID <SP>] URL [<SP> extras]<NL>
 
 
 	After processing the request the helper must reply using the following format:
 
 	  [channel-ID <SP>] result [<SP> kv-pairs]
 
 	The result code can be:
 
 	  OK status=30N url="..."
 		Redirect the URL to the one supplied in 'url='.
 		'status=' is optional and contains the status code to send
 		the client in Squids HTTP response. It must be one of the
 		HTTP redirect status codes: 301, 302, 303, 307, 308.
 		When no status is given Squid will use 302.
 
 	  OK rewrite-url="..."
 		Rewrite the URL to the one supplied in 'rewrite-url='.
 		The new URL is fetched directly by Squid and returned to
 		the client as the response to its request.
 
@@ -4738,93 +4738,117 @@
 	This clause supports both fast and slow acl types.
 	See http://wiki.squid-cache.org/SquidFaq/SquidAcl for details.
 DOC_END
 
 NAME: url_rewrite_bypass redirector_bypass
 TYPE: onoff
 LOC: Config.onoff.redirector_bypass
 DEFAULT: off
 DOC_START
 	When this is 'on', a request will not go through the
 	redirector if all the helpers are busy.  If this is 'off'
 	and the redirector queue grows too large, Squid will exit
 	with a FATAL error and ask you to increase the number of
 	redirectors.  You should only enable this if the redirectors
 	are not critical to your caching system.  If you use
 	redirectors for access control, and you enable this option,
 	users may have access to pages they should not
 	be allowed to request.
 DOC_END
 
+NAME: url_rewrite_extras format
+TYPE: TokenOrQuotedString
+LOC: Config.redirector_extras
+DEFAULT: "%>a/%>A %un %>rm myip=%la myport=%lp"
+DOC_START
+	Specifies a string to be append to request line format for the
+	rewriter helper. "Quoted" format values may contain spaces and
+	logformat %macros. In theory, any logformat %macro can be used.
+	In practice, a %macro expands as a dash (-) if the helper request is
+	sent before the required macro information is available to Squid.
+DOC_END
+
 COMMENT_START
  OPTIONS FOR STORE ID
  -----------------------------------------------------------------------------
 COMMENT_END
 
 NAME: store_id_program storeurl_rewrite_program
 TYPE: wordlist
 LOC: Config.Program.store_id
 DEFAULT: none
 DOC_START
 	Specify the location of the executable StoreID helper to use.
 	Since they can perform almost any function there isn't one included.
 
 	For each requested URL, the helper will receive one line with the format
 
-	  [channel-ID <SP>] URL <SP> client_ip "/" fqdn <SP> user <SP> method [<SP> kv-pairs]<NL>
+	  [channel-ID <SP>] URL [<SP> extras]<NL>
 
 
 	After processing the request the helper must reply using the following format:
 
 	  [channel-ID <SP>] result [<SP> kv-pairs]
 
 	The result code can be:
 
 	  OK store-id="..."
 		Use the StoreID supplied in 'store-id='.
 
 	  ERR
 		The default is to use HTTP request URL as the store ID.
 
 	  BH
 		An internal error occured in the helper, preventing
 		a result being identified.
 
 
-	Helper programs should be prepared to receive and possibly ignore additional
-	kv-pairs with keys they do not support.
+	Helper programs should be prepared to receive and possibly ignore
+	additional whitespace-separated tokens on each input line.
 
 	When using the concurrency= option the protocol is changed by
 	introducing a query channel tag in front of the request/response.
 	The query channel tag is a number between 0 and concurrency-1.
 	This value must be echoed back unchanged to Squid as the first part
 	of the response relating to its request.
 
 	NOTE: when using StoreID refresh_pattern will apply to the StoreID
 	      returned from the helper and not the URL.
 
 	WARNING: Wrong StoreID value returned by a careless helper may result
 	         in the wrong cached response returned to the user.
 
 	By default, a StoreID helper is not used.
 DOC_END
 
+NAME: store_id_extras format
+TYPE: TokenOrQuotedString
+LOC: Config.storeId_extras
+DEFAULT: "%>a/%>A %un %>rm myip=%la myport=%lp"
+DOC_START
+        Specifies a string to be append to request line format for the
+        StoreId helper. "Quoted" format values may contain spaces and
+        logformat %macros. In theory, any logformat %macro can be used.
+        In practice, a %macro expands as a dash (-) if the helper request is
+        sent before the required macro information is available to Squid.
+DOC_END
+
 NAME: store_id_children storeurl_rewrite_children
 TYPE: HelperChildConfig
 DEFAULT: 20 startup=0 idle=1 concurrency=0
 LOC: Config.storeIdChildren
 DOC_START
 	The maximum number of StoreID helper processes to spawn. If you limit
 	it too few Squid will have to wait for them to process a backlog of
 	requests, slowing it down. If you allow too many they will use RAM
 	and other system resources noticably.
 	
 	The startup= and idle= options allow some measure of skew in your
 	tuning.
 	
 		startup=
 	
 	Sets a minimum of how many processes are to be spawned when Squid
 	starts or reconfigures. When set to zero the first request will
 	cause spawning of the first child process to handle it.
 	
 	Starting too few will cause an initial slowdown in traffic as Squid

=== modified file 'src/cf_gen.cc'
--- src/cf_gen.cc	2013-07-21 19:24:35 +0000
+++ src/cf_gen.cc	2014-02-24 16:35:34 +0000
@@ -141,40 +141,41 @@
 public:
     Type(const char *str) : name(str) {}
     ~Type() {}
 
     std::string name;
     TypeDepList depend;
 };
 
 typedef std::list<class Type> TypeList;
 
 static const char WS[] = " \t\n";
 static int gen_default(const EntryList &, std::ostream &);
 static void gen_parse(const EntryList &, std::ostream &);
 static void gen_dump(const EntryList &, std::ostream&);
 static void gen_free(const EntryList &, std::ostream&);
 static void gen_conf(const EntryList &, std::ostream&, bool verbose_output);
 static void gen_default_if_none(const EntryList &, std::ostream&);
 static void gen_default_postscriptum(const EntryList &, std::ostream&);
 static bool isDefined(const std::string &name);
 static const char *available_if(const std::string &name);
+static const char *gen_quote_escape(const std::string &var);
 
 static void
 checkDepend(const std::string &directive, const char *name, const TypeList &types, const EntryList &entries)
 {
     for (TypeList::const_iterator t = types.begin(); t != types.end(); ++t) {
         if (t->name.compare(name) != 0)
             continue;
         for (TypeDepList::const_iterator dep = t->depend.begin(); dep != t->depend.end(); ++dep) {
             EntryList::const_iterator entry = entries.begin();
             for (; entry != entries.end(); ++entry) {
                 if (entry->name.compare(*dep) == 0)
                     break;
             }
             if (entry == entries.end()) {
                 std::cerr << "ERROR: '" << directive << "' (" << name << ") depends on '" << *dep << "'\n";
                 exit(1);
             }
         }
         return;
     }
@@ -523,82 +524,82 @@
 
         if (!entry->loc.size()) {
             std::cerr << "NO LOCATION FOR " << entry->name << std::endl;
             rc |= 1;
             continue;
         }
 
         if (!entry->defaults.preset.size() && entry->defaults.if_none.empty()) {
             std::cerr << "NO DEFAULT FOR " << entry->name << std::endl;
             rc |= 1;
             continue;
         }
 
         if (!entry->defaults.preset.size() || entry->defaults.preset.front().compare("none") == 0) {
             fout << "    // No default for " << entry->name << std::endl;
         } else {
             if (entry->ifdef.size())
                 fout << "#if " << entry->ifdef << std::endl;
 
             for (LineList::const_iterator l = entry->defaults.preset.begin(); l != entry->defaults.preset.end(); ++l) {
-                fout << "    default_line(\"" << entry->name << " " << *l << "\");" << std::endl;
+                fout << "    default_line(\"" << entry->name << " " << gen_quote_escape(*l) << "\");" << std::endl;
             }
 
             if (entry->ifdef.size())
                 fout << "#endif" << std::endl;
         }
     }
 
     fout << "    cfg_filename = NULL;" << std::endl <<
     "}" << std::endl << std::endl;
     return rc;
 }
 
 static void
 gen_default_if_none(const EntryList &head, std::ostream &fout)
 {
     fout << "static void" << std::endl <<
     "defaults_if_none(void)" << std::endl <<
     "{" << std::endl <<
     "    cfg_filename = \"Default Configuration (if absent)\";" << std::endl <<
     "    config_lineno = 0;" << std::endl;
 
     for (EntryList::const_iterator entry = head.begin(); entry != head.end(); ++entry) {
         assert(entry->name.size());
 
         if (!entry->loc.size())
             continue;
 
         if (entry->defaults.if_none.empty())
             continue;
 
         if (!entry->defaults.preset.empty()) {
             std::cerr << "ERROR: " << entry->name << " has preset defaults. DEFAULT_IF_NONE cannot be true." << std::endl;
             exit(1);
         }
 
         if (entry->ifdef.size())
             fout << "#if " << entry->ifdef << std::endl;
 
         fout << "    if (check_null_" << entry->type << "(" << entry->loc << ")) {" << std::endl;
         for (LineList::const_iterator l = entry->defaults.if_none.begin(); l != entry->defaults.if_none.end(); ++l)
-            fout << "        default_line(\"" << entry->name << " " << *l <<"\");" << std::endl;
+            fout << "        default_line(\"" << entry->name << " " << gen_quote_escape(*l) <<"\");" << std::endl;
         fout << "    }" << std::endl;
 
         if (entry->ifdef.size())
             fout << "#endif" << std::endl;
     }
 
     fout << "    cfg_filename = NULL;" << std::endl <<
     "}" << std::endl << std::endl;
 }
 
 /// append configuration options specified by POSTSCRIPTUM lines
 static void
 gen_default_postscriptum(const EntryList &head, std::ostream &fout)
 {
     fout << "static void" << std::endl <<
     "defaults_postscriptum(void)" << std::endl <<
     "{" << std::endl <<
     "    cfg_filename = \"Default Configuration (postscriptum)\";" << std::endl <<
     "    config_lineno = 0;" << std::endl;
 
@@ -805,62 +806,81 @@
                 "#       " << available_if(entry->ifdef) << std::endl <<
                 "#" << std::endl;
             }
             enabled = 0;
         }
 
         // Display DOC_START section
         if (verbose_output && entry->doc.size()) {
             for (LineList::const_iterator line = entry->doc.begin(); line != entry->doc.end(); ++line) {
                 fout << "#" << *line << std::endl;
             }
         }
 
         if (entry->defaults.docs.size()) {
             // Display the DEFAULT_DOC line(s)
             def = entry->defaults.docs;
         } else {
             if (entry->defaults.preset.size() && entry->defaults.preset.front().compare("none") != 0) {
                 // Display DEFAULT: line(s)
                 for (LineList::const_iterator l = entry->defaults.preset.begin(); l != entry->defaults.preset.end(); ++l) {
-                    snprintf(buf, sizeof(buf), "%s %s", entry->name.c_str(), l->c_str());
+                    snprintf(buf, sizeof(buf), "%s %s", entry->name.c_str(), gen_quote_escape(*l));
                     def.push_back(buf);
                 }
             } else if (entry->defaults.if_none.size()) {
                 // Display DEFAULT_IF_NONE: line(s)
                 for (LineList::const_iterator l = entry->defaults.if_none.begin(); l != entry->defaults.if_none.end(); ++l) {
-                    snprintf(buf, sizeof(buf), "%s %s", entry->name.c_str(), l->c_str());
+                    snprintf(buf, sizeof(buf), "%s %s", entry->name.c_str(), gen_quote_escape(*l));
                     def.push_back(buf);
                 }
             }
         }
 
         // Display "none" if no default is set or comments to display
         if (def.empty() && entry->nocomment.empty() && entry->name.compare("comment") != 0)
             def.push_back("none");
 
         if (verbose_output && def.size()) {
             fout << "#Default:\n";
             while (def.size()) {
                 fout << "# " << def.front() << std::endl;
                 def.pop_front();
             }
             if (entry->doc.empty() && entry->nocomment.empty())
                 fout << std::endl;
         }
 
         if (verbose_output && entry->nocomment.size())
             fout << "#" << std::endl;
 
         if (enabled || verbose_output) {
             for (LineList::const_iterator line = entry->nocomment.begin(); line != entry->nocomment.end(); ++line) {
                 if (!enabled && line->at(0) != '#')
                     fout << "#";
                 fout << *line << std::endl;
             }
         }
 
         if (verbose_output && entry->doc.size()) {
             fout << std::endl;
         }
     }
 }
+
+static const char *
+gen_quote_escape(const std::string &var)
+{
+    static std::string esc;
+    esc.clear();
+
+    for (int i = 0; i < var.length(); ++i) {
+        switch (var[i]) {
+            case '"':
+            case '\\':
+                esc += '\\';
+            default:
+                esc += var[i];
+        }
+    }
+
+    return esc.c_str();
+}

=== modified file 'src/client_side_request.cc'
--- src/client_side_request.cc	2013-12-06 23:52:26 +0000
+++ src/client_side_request.cc	2014-02-21 17:07:39 +0000
@@ -879,41 +879,41 @@
 static void
 clientRedirectAccessCheckDone(allow_t answer, void *data)
 {
     ClientRequestContext *context = (ClientRequestContext *)data;
     ClientHttpRequest *http = context->http;
     context->acl_checklist = NULL;
 
     if (answer == ACCESS_ALLOWED)
         redirectStart(http, clientRedirectDoneWrapper, context);
     else {
         HelperReply nilReply;
         nilReply.result = HelperReply::Error;
         context->clientRedirectDone(nilReply);
     }
 }
 
 void
 ClientRequestContext::clientRedirectStart()
 {
     debugs(33, 5, HERE << "'" << http->uri << "'");
-
+    (void)SyncNotes(*http->al, *http->request);
     if (Config.accessList.redirector) {
         acl_checklist = clientAclChecklistCreate(Config.accessList.redirector, http);
         acl_checklist->nonBlockingCheck(clientRedirectAccessCheckDone, this);
     } else
         redirectStart(http, clientRedirectDoneWrapper, this);
 }
 
 /**
  * This methods handles Access checks result of StoreId access list.
  * Will handle as "ERR" (no change) in a case Access is not allowed.
  */
 static void
 clientStoreIdAccessCheckDone(allow_t answer, void *data)
 {
     ClientRequestContext *context = static_cast<ClientRequestContext *>(data);
     ClientHttpRequest *http = context->http;
     context->acl_checklist = NULL;
 
     if (answer == ACCESS_ALLOWED)
         storeIdStart(http, clientStoreIdDoneWrapper, context);

=== modified file 'src/redirect.cc'
--- src/redirect.cc	2013-11-23 00:58:42 +0000
+++ src/redirect.cc	2014-02-24 16:41:43 +0000
@@ -21,40 +21,41 @@
  *
  *  This program is distributed in the hope that it will be useful,
  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  *  GNU General Public License for more details.
  *
  *  You should have received a copy of the GNU General Public License
  *  along with this program; if not, write to the Free Software
  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
  *
  */
 
 #include "squid.h"
 #include "acl/Checklist.h"
 #include "client_side.h"
 #include "client_side_reply.h"
 #include "client_side_request.h"
 #include "comm/Connection.h"
 #include "fde.h"
 #include "fqdncache.h"
+#include "format/Format.h"
 #include "globals.h"
 #include "HttpRequest.h"
 #include "mgr/Registration.h"
 #include "redirect.h"
 #include "rfc1738.h"
 #include "SBuf.h"
 #include "SquidConfig.h"
 #include "Store.h"
 #if USE_AUTH
 #include "auth/UserRequest.h"
 #endif
 #if USE_SSL
 #include "ssl/support.h"
 #endif
 
 /// url maximum lengh + extra informations passed to redirector
 #define MAX_REDIRECTOR_REQUEST_STRLEN (MAX_URL + 1024)
 
 class RedirectStateData
 {
@@ -65,40 +66,42 @@
     void *data;
     SBuf orig_url;
 
     Ip::Address client_addr;
     const char *client_ident;
     const char *method_s;
     HLPCB *handler;
 
 private:
     CBDATA_CLASS2(RedirectStateData);
 };
 
 static HLPCB redirectHandleReply;
 static HLPCB storeIdHandleReply;
 static helper *redirectors = NULL;
 static helper *storeIds = NULL;
 static OBJH redirectStats;
 static OBJH storeIdStats;
 static int redirectorBypassed = 0;
 static int storeIdBypassed = 0;
+static Format::Format *redirectorExtrasFmt = NULL;
+static Format::Format *storeIdExtrasFmt = NULL;
 
 CBDATA_CLASS_INIT(RedirectStateData);
 
 RedirectStateData::RedirectStateData(const char *url) :
         data(NULL),
         orig_url(url),
         client_addr(),
         client_ident(NULL),
         method_s(NULL),
         handler(NULL)
 {
 }
 
 RedirectStateData::~RedirectStateData()
 {
 }
 
 static void
 redirectHandleReply(void *data, const HelperReply &reply)
 {
@@ -216,49 +219,47 @@
         storeAppendPrintf(sentry, "\nNumber of requests bypassed "
                           "because all redirectors were busy: %d\n", redirectorBypassed);
 }
 
 static void
 storeIdStats(StoreEntry * sentry)
 {
     if (storeIds == NULL) {
         storeAppendPrintf(sentry, "No StoreId helpers defined\n");
         return;
     }
 
     helperStats(sentry, storeIds, "StoreId helper Statistics");
 
     if (Config.onoff.store_id_bypass)
         storeAppendPrintf(sentry, "\nNumber of requests bypassed "
                           "because all StoreId helpers were busy: %d\n", storeIdBypassed);
 }
 
 static void
-constructHelperQuery(const char *name, helper *hlp, HLPCB *replyHandler, ClientHttpRequest * http, HLPCB *handler, void *data)
+constructHelperQuery(const char *name, helper *hlp, HLPCB *replyHandler, ClientHttpRequest * http, HLPCB *handler, void *data, Format::Format *requestExtrasFmt)
 {
     ConnStateData * conn = http->getConn();
     const char *fqdn;
     char buf[MAX_REDIRECTOR_REQUEST_STRLEN];
     int sz;
     Http::StatusCode status;
-    char claddr[MAX_IPSTRLEN];
-    char myaddr[MAX_IPSTRLEN];
 
     /** TODO: create a standalone method to initialize
      * the RedirectStateData for all the helpers.
      */
     RedirectStateData *r = new RedirectStateData(http->uri);
     if (conn != NULL)
         r->client_addr = conn->log_addr;
     else
         r->client_addr.setNoAddr();
     r->client_ident = NULL;
 #if USE_AUTH
     if (http->request->auth_user_request != NULL) {
         r->client_ident = http->request->auth_user_request->username();
         debugs(61, 5, HERE << "auth-user=" << (r->client_ident?r->client_ident:"NULL"));
     }
 #endif
 
     if (!r->client_ident && http->request->extacl_user.size() > 0) {
         r->client_ident = http->request->extacl_user.termedBuf();
         debugs(61, 5, HERE << "acl-user=" << (r->client_ident?r->client_ident:"NULL"));
@@ -272,48 +273,49 @@
 #if USE_SSL
 
     if (!r->client_ident && conn != NULL && Comm::IsConnOpen(conn->clientConnection)) {
         r->client_ident = sslGetUserEmail(fd_table[conn->clientConnection->fd].ssl);
         debugs(61, 5, HERE << "ssl-user=" << (r->client_ident?r->client_ident:"NULL"));
     }
 #endif
 
     if (!r->client_ident)
         r->client_ident = dash_str;
 
     r->method_s = RequestMethodStr(http->request->method);
 
     r->handler = handler;
 
     r->data = cbdataReference(data);
 
     if ((fqdn = fqdncache_gethostbyaddr(r->client_addr, 0)) == NULL)
         fqdn = dash_str;
 
-    sz = snprintf(buf, MAX_REDIRECTOR_REQUEST_STRLEN, "%s %s/%s %s %s myip=%s myport=%d\n",
+    static MemBuf requestExtras;
+    requestExtras.reset();
+    if (requestExtrasFmt)
+        requestExtrasFmt->assemble(requestExtras, http->al, 0);
+
+    sz = snprintf(buf, MAX_REDIRECTOR_REQUEST_STRLEN, "%s%s%s\n",
                   r->orig_url.c_str(),
-                  r->client_addr.toStr(claddr,MAX_IPSTRLEN),
-                  fqdn,
-                  r->client_ident[0] ? rfc1738_escape(r->client_ident) : dash_str,
-                  r->method_s,
-                  http->request->my_addr.toStr(myaddr,MAX_IPSTRLEN),
-                  http->request->my_addr.port());
+                  requestExtras.hasContent() ? " " : "",
+                  requestExtras.hasContent() ? requestExtras.content() : "");
 
     if ((sz<=0) || (sz>=MAX_REDIRECTOR_REQUEST_STRLEN)) {
         if (sz<=0) {
             status = Http::scInternalServerError;
             debugs(61, DBG_CRITICAL, "ERROR: Gateway Failure. Can not build request to be passed to " << name << ". Request ABORTED.");
         } else {
             status = Http::scRequestUriTooLarge;
             debugs(61, DBG_CRITICAL, "ERROR: Gateway Failure. Request passed to " << name << " exceeds MAX_REDIRECTOR_REQUEST_STRLEN (" << MAX_REDIRECTOR_REQUEST_STRLEN << "). Request ABORTED.");
         }
 
         clientStreamNode *node = (clientStreamNode *)http->client_stream.tail->prev->data;
         clientReplyContext *repContext = dynamic_cast<clientReplyContext *>(node->data.getRaw());
         assert (repContext);
         Ip::Address tmpnoaddr;
         tmpnoaddr.setNoAddr();
         repContext->setReplyToError(ERR_GATEWAY_FAILURE, status,
                                     http->request->method, NULL,
                                     http->getConn() != NULL && http->getConn()->clientConnection != NULL ?
                                     http->getConn()->clientConnection->remote : tmpnoaddr,
                                     http->request,
@@ -336,116 +338,131 @@
 
 /**** PUBLIC FUNCTIONS ****/
 
 void
 redirectStart(ClientHttpRequest * http, HLPCB * handler, void *data)
 {
     assert(http);
     assert(handler);
     debugs(61, 5, "redirectStart: '" << http->uri << "'");
 
     if (Config.onoff.redirector_bypass && redirectors->stats.queue_size) {
         /* Skip redirector if there is one request queued */
         ++redirectorBypassed;
         HelperReply bypassReply;
         bypassReply.result = HelperReply::Okay;
         bypassReply.notes.add("message","URL rewrite/redirect queue too long. Bypassed.");
         handler(data, bypassReply);
         return;
     }
 
-    constructHelperQuery("redirector", redirectors, redirectHandleReply, http, handler, data);
+    constructHelperQuery("redirector", redirectors, redirectHandleReply, http, handler, data, redirectorExtrasFmt);
 }
 
 /**
  * Handles the StoreID feature helper starting.
  * For now it cannot be done using the redirectStart method.
  */
 void
 storeIdStart(ClientHttpRequest * http, HLPCB * handler, void *data)
 {
     assert(http);
     assert(handler);
     debugs(61, 5, "storeIdStart: '" << http->uri << "'");
 
     if (Config.onoff.store_id_bypass && storeIds->stats.queue_size) {
         /* Skip StoreID Helper if there is one request queued */
         ++storeIdBypassed;
         HelperReply bypassReply;
 
         bypassReply.result = HelperReply::Okay;
 
         bypassReply.notes.add("message","StoreId helper queue too long. Bypassed.");
         handler(data, bypassReply);
         return;
     }
 
-    constructHelperQuery("storeId helper", storeIds, storeIdHandleReply, http, handler, data);
+    constructHelperQuery("storeId helper", storeIds, storeIdHandleReply, http, handler, data, storeIdExtrasFmt);
 }
 
 void
 redirectInit(void)
 {
     static bool init = false;
 
     if (!init) {
         Mgr::RegisterAction("redirector", "URL Redirector Stats", redirectStats, 0, 1);
         Mgr::RegisterAction("store_id", "StoreId helper Stats", storeIdStats, 0, 1);
     }
 
     if (Config.Program.redirect) {
 
         if (redirectors == NULL)
             redirectors = new helper("redirector");
 
         redirectors->cmdline = Config.Program.redirect;
 
         redirectors->childs.updateLimits(Config.redirectChildren);
 
         redirectors->ipc_type = IPC_STREAM;
 
         helperOpenServers(redirectors);
     }
 
     if (Config.Program.store_id) {
 
         if (storeIds == NULL)
             storeIds = new helper("store_id");
 
         storeIds->cmdline = Config.Program.store_id;
 
         storeIds->childs.updateLimits(Config.storeIdChildren);
 
         storeIds->ipc_type = IPC_STREAM;
 
         helperOpenServers(storeIds);
     }
 
+    if (Config.redirector_extras) {
+        redirectorExtrasFmt = new ::Format::Format("redirecor_extras");
+        (void)redirectorExtrasFmt->parse(Config.redirector_extras);
+    }
+
+    if (Config.storeId_extras) {
+        storeIdExtrasFmt = new ::Format::Format("storeId_extras");
+        (void)storeIdExtrasFmt->parse(Config.storeId_extras);
+    }
+
     init = true;
 }
 
 void
 redirectShutdown(void)
 {
     /** FIXME: Temporary unified helpers Shutdown
      * When and if needed for more helpers a separated shutdown
      * method will be added for each of them.
      */
     if (!storeIds && !redirectors)
         return;
 
     if (redirectors)
         helperShutdown(redirectors);
 
     if (storeIds)
         helperShutdown(storeIds);
 
     if (!shutting_down)
         return;
 
     delete redirectors;
     redirectors = NULL;
 
     delete storeIds;
     storeIds = NULL;
 
+    delete redirectorExtrasFmt;
+    redirectorExtrasFmt = NULL;
+
+    delete storeIdExtrasFmt;
+    storeIdExtrasFmt = NULL;
 }

Reply via email to