.. Licensed to the Apache Software Foundation (ASF) under one
   or more contributor license agreements.  See the NOTICE file
   distributed with this work for additional information
   regarding copyright ownership.  The ASF licenses this file
   to you under the Apache License, Version 2.0 (the
   "License"); you may not use this file except in compliance
   with the License.  You may obtain a copy of the License at

   http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing,
   software distributed under the License is distributed on an
   "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
   KIND, either express or implied.  See the License for the
    specific language governing permissions and limitations
   under the License.

.. include:: ../../common.defs

.. _admin-plugins-cookie_remap:

Cookie Based Routing Inside TrafficServer Using cookie_remap
************************************************************


* `Cookie Based Routing Inside TrafficServer Using cookie_remap <#cookie-based-routing-inside-trafficserver-using-cookie_remap>`_

  * `Features <#features>`_
  * `Limitations <#limitations>`_
  * `Setup <#setup>`_
  * `Operations <#operations>`_

    * `Comments <#comments>`_
    * `cookie: X|X.Y <#cookie-xxy>`_
    * `target: purl <#purl>`_
    * `operation: exists|not exists|string|regex|bucket <#operation-existsnot-existsstringregexbucket>`_
    * `match: str <#match-str>`_
    * `regex: str <#regex-str>`_
    * `bucket|hash: X/Y <#buckethash-xy>`_
    * `sendto|url: url <#sendtourl-url>`_
    * `status: HTTP status-code <#status-http-status-code>`_
    * `else: url [optional] <#else-url-optional>`_
    * `connector: and <#connector-and>`_

  * `Reserved path expressions <#reserved-path-expressions>`_

    * `$cr_req_url <#cr_req_url-v-15>`_
    * `$cr_urlencode() <#cr_urlencode-v-15>`_
    * `$path <#path>`_
    * `$unmatched_path <#unmatched_path>`_

  * `An example configuration file <#an-example-configuration-file>`_
  * `Debugging things <#debugging-things>`_

    * `Initial output <#initial-output>`_

This remap plugin makes decisions about where to send your request based on properties present (or absent) within the HTTP Cookie header.  It can also make decisions based on your uri (url path + query.)

Features
--------

----


* Also supports sub-level cookies

  * K indicates top-level cookie "K"
  * K.l indicates top-level cookie "K", subfield "l"

* Cookie exists / Cookie doesn't exist
* Cookie or uri matches string
* Cookie or uri matches regex (with match replacement in the sendto like `http://foo.com/$1/$2 <http://foo.com/$1/$2>`_\ )
* Cookie falls into a hash/bucket range
* Can url encode dynamic data

Limitations
-----------

----


* Does not support :ref:`remap-config-plugin-chaining`

Setup
-----

----

The plugin is specified in remap.config using a syntax similar to:


.. raw:: html

   <pre>
   map http://foo.com http://bar.com @plugin=/usr/bin/trafficserver/libexec64/cookie_remap.so @pparam=/home/trafficserver/conf/cookie_remap/cookie_remap.txt
   </pre>


Operations
----------

----

All operations are specified in a YAML configuration file you pass as @pparam on the plugin configuration line. YAML is very simple syntax. See the example configuration files below.

Each operation results in a sendto action. That means that, if matched, cookie_remap forwards the request to the given ``sendto`` url. It can be proxied or redirected. An ``else`` sendto can be specified, in which case a failure to match will forward the request also. Once a sendto is invoked:


* cookie_remap will not process any more "operations" from the configuration
* the default destination specified in the remap.config file will not be used; it will be replaced by the ``sendto``

Each ``operation`` can have multiple "sub-operations", connected with an conjunction operator.  Currently, only the ``and`` operator is supported. So, you can say, "if cookie exists, ``and`` uri is ``x``\ , then redirect."

Comments
^^^^^^^^

----

Comments are allowed in the configuration file if they begin with '#'

cookie: X|X.Y
^^^^^^^^^^^^^

----

This sub-operation is testing against the X cookie or X.Y cookie where X.Y denotes the X cookie, sub cookie Y
e.g


.. raw:: html

   <pre>
   A=ACOOKIE;B=data&f=fsub&z=zsub;
   A will operate on ACOOKIE
   B will operate on data&f=fsub&z=zsub
   B.f will operate on fsub
   </pre>

target: puri
^^^^^^^^^^^^

----

When the cookie key is omitted, the operation is applied to the request uri instead.  If this key and value
is specified, the uri for the operation will be the pre-remapped, rather than the remapped, uri.

operation: exists|not exists|string|regex|bucket
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

----

This keyword actually denotes a "suboperation" - it can be specified multiple times, connected with ``and``\ , and send the user to a given destination.


* exists: Test for the existence of a cookie
* not exists: Test for the non-existence of a cookie
* string: string match
* regex: regex matching with $1 - $9 replaced in ``sendto``
* bucket: Hash the cookie and check if it falls in a bucket

The ``cookie`` operator must be specified in the YAML file just before the suboperation that will apply to the cookie.

The ``string``, ``regex`` and ``bucket`` operate on the specified cookie, or if no cookie is specified, they will operate on the uri of the request.

match: str
^^^^^^^^^^

----

Match the cookie data to str.  If no cookie is specified, match to the request uri.

regex: str
^^^^^^^^^^

----

Sets the regex to str. Matching is enabled with $1-$9 (replaced in sendto).  Note that only the final regex match in an operation will be used to populate the substitutions $1-$9.  If no cookie is specified, match to the request uri.

bucket|hash: X/Y
^^^^^^^^^^^^^^^^

----

Hashes (bucketizes) the data in Y buckets. If you fall into the first X of them, this suboperation passes. If no cookie is specified, match to the request uri.


.. raw:: html

   <pre>
   bucket: 1/100
   will effectively bucketize 1% of your users
   </pre>


sendto|url: url
^^^^^^^^^^^^^^^

----

if the sub-operation(s) all match, send to url

status: HTTP status-code
^^^^^^^^^^^^^^^^^^^^^^^^

----

if the sub-operation(s) all match, set the status code (e.g. set it to 302). In the case of a redirect, the sendto URL becomes the redirect URL.

else: url [optional]
^^^^^^^^^^^^^^^^^^^^

----

If one of the sub-operations fails, send to url. This is optional. If there is no 'else', we will continue to process operations until either one succeeds, there is an else in one of the operations or we fall through to the default mapping from remap.config

connector: and
^^^^^^^^^^^^^^

----

'and' is the only supported connector

Reserved path expressions
-------------------------

----

The following expressions can be used in either the sendto **or** ``else`` URLs, and will be expanded.

$cr_req_url
^^^^^^^^^^^^^^^^^^^^^^^^^

----

Replaced with the full original url.

Therefore, a rule like:


.. raw:: html

   <pre>
   op:
     cookie: K
     operation: exists
     sendto: http://foo.com/?.done=$cr_req_url
   </pre>


and a request that matches, e.g.


.. raw:: html

   <pre>
   http://bar.com/hello?fruit=bananas
   </pre>


will become


.. raw:: html

   <pre>
   http://foo.com/?.done=http://bar.com/hello?fruit=bananas
   </pre>


$cr_urlencode()
^^^^^^^^^^^^^^^^^^^^^^^^^

----

Replaced with a urlencoded version of its argument.  The url argument is aggressively encoded such that all non-alphanumeric characters are converted to % hex notation.  The simple algorithm could probably be refined in the future to be less aggressive (encode fewer characters.)

Therefore, a rule like:


.. raw:: html

   <pre>
   op:
     cookie: B
     operation: exists
     sendto: http://foo.com/?.done=$cr_urlencode($cr_req_url)
   </pre>


and a request that matches, e.g.


.. raw:: html

   <pre>
   http://bar.com/hello?fruit=bananas
   </pre>


will become


.. raw:: html

   <pre>
   http://foo.com/?.done=http%3A%2F%2Fbar%2Ecom@2Fhello%3Ffruit%3Dbananas
   </pre>


$path
^^^^^

----

$path can be used to replace in either the sendto **or** else URLs the original request path. Therefore a rule like:


.. raw:: html

   <pre>
   op:
     cookie: K
     operation: exists
     sendto: http://foo.com/$path/x/y/z
   </pre>


and a request like `http://finance.yahoo.com/photos/what/ever/ <http://foo.com/photos/what/ever/>`_ that matches the rule


.. raw:: html

   <pre>
   map http://finance.yahoo.com/photos/ http://newfinance.yahoo.com/1k.html @plugin=cookie_remap.so @pparam=foo.txt
   </pre>


will become `http://foo.com/photos/what/ever/x/y/z <http://foo.com/photos/what/ever/x/y/z>`_

$unmatched_path
^^^^^^^^^^^^^^^

----

$unmatched_path can be used to gather the URL arguments **beyond** what was matched by the remap rule. Therefore a rule like:


.. raw:: html

   <pre>
   op:
     cookie: K
     operation: exists
     sendto: http://foo.com/$unmatched_path/x/y/z
   </pre>


and a request like `http://finance.yahoo.com/photos/what/ever/ <http://foo.com/photos/what/ever/>`_ that matches the rule


.. raw:: html

   <pre>
   map http://finance.yahoo.com/photos/ http://newfinance.yahoo.com/1k.html @plugin=cookie_remap.so @pparam=foo.txt
   </pre>


will become `http://foo.com/what/ever/matches/x/y/z <http://foo.com/what/ever/matches/x/y/z>`_

Alternatives using pre-remapped URL
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

$cr_req_url, $path and $unamatched_path are based on the remapped URL.  To use the pre-remapped
URL, instead use $cr_req_purl, $ppath and $unmatched_ppath, respectively.

An example configuration file
-----------------------------

----



* first match "wins" (top down)


.. raw:: html

   <pre>
   #comments
   #are allowed
   op:
     cookie: K
     operation: exists
     url: http://www.yahoo.com

   op:
     cookie: K
     operation: exists
     connector: and
     cookie: K.l
     operation: not exists
     sendto: http://www.yahoo.com

   op:
     cookie: Y
     operation: bucket
     bucket: 1/1000
     connector: and
     cookie: Y.l
     operation: regex
     regex: (.*)
     connector: and
     cookie: Y.n
     operation: string
     match: foobar
     sendto: http://cnn.com/$1
     else: http://yahoo.com
   </pre>


Debugging things
----------------

----

The easiest way to debug problems with this plugin is to run in the following manner:


.. raw:: html

   <pre>
   bin/traffic_server -T cookie_remap
   </pre>


which will produce output on startup, and for each request. Be aware that this mode of running trafficserver is **extremely** inefficient and should only be used for debugging.

Initial output
^^^^^^^^^^^^^^

----

Initially, you will notice output describing each operation and how trafficserver interpreted the information from your configuration file:


.. raw:: html

   <pre>
   [Jul  8 13:08:34.183] Server {3187168} DIAG: (cookie_remap) loading cookie remap configuration file from /homes/ebalsa/dev/yts_mods/cookie_remap/example_config.txt
   [Jul  8 13:08:34.199] Server {3187168} DIAG: (cookie_remap) ++++operation++++
   [Jul  8 13:08:34.199] Server {3187168} DIAG: (cookie_remap) sending to: http://finance.yahoo.com/2k.html
   [Jul  8 13:08:34.199] Server {3187168} DIAG: (cookie_remap) if these operations match:
   [Jul  8 13:08:34.200] Server {3187168} DIAG: (cookie_remap)     +++subop+++
   [Jul  8 13:08:34.200] Server {3187168} DIAG: (cookie_remap)         cookie: B
   [Jul  8 13:08:34.200] Server {3187168} DIAG: (cookie_remap)         operation: bucket
   [Jul  8 13:08:34.200] Server {3187168} DIAG: (cookie_remap)         bucket: 1/100
   [Jul  8 13:08:34.200] Server {3187168} DIAG: (cookie_remap)         taking: 1
   [Jul  8 13:08:34.200] Server {3187168} DIAG: (cookie_remap)         out of: 100
   [Jul  8 13:08:34.200] Server {3187168} DIAG: (cookie_remap)     +++subop+++
   [Jul  8 13:08:34.201] Server {3187168} DIAG: (cookie_remap)         cookie: Y.l
   [Jul  8 13:08:34.201] Server {3187168} DIAG: (cookie_remap)         operation: exists
   [Jul  8 13:08:34.201] Server {3187168} DIAG: (cookie_remap) ++++operation++++
   [Jul  8 13:08:34.201] Server {3187168} DIAG: (cookie_remap) sending to: http://X.existed.com
   [Jul  8 13:08:34.201] Server {3187168} DIAG: (cookie_remap) if these operations match:
   [Jul  8 13:08:34.201] Server {3187168} DIAG: (cookie_remap)     +++subop+++
   [Jul  8 13:08:34.201] Server {3187168} DIAG: (cookie_remap)         cookie: PH.l
   [Jul  8 13:08:34.202] Server {3187168} DIAG: (cookie_remap)         operation: exists
   [Jul  8 13:08:34.202] Server {3187168} DIAG: (cookie_remap) # of ops: 2
   </pre>


each operation is enumerated and is more-or-less human readable from the top-down.  From now on, each request has information spit out to the console with debugging information on how the cookie_remap is treating this request.

