Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions ci/tsqa/files/header-rewrite.config
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
cond %{READ_REQUEST_PRE_REMAP_HOOK}
cond %{PATH} /^.*addcookie$/ [AND]
add-cookie testkey testaddvalue

cond %{READ_REQUEST_PRE_REMAP_HOOK}
cond %{PATH} /^.*rmcookie$/ [AND]
rm-cookie testkey


cond %{READ_REQUEST_PRE_REMAP_HOOK}
cond %{PATH} /^.*setcookie$/ [AND]
set-cookie testkey testsetvalue

115 changes: 115 additions & 0 deletions ci/tsqa/tests/test_header_rewrite.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
'''
Test cookie rewrite
'''
import os
import requests
import time
import logging
import random
import tsqa.test_cases
import helpers
import shutil
import SocketServer
import urllib2

log = logging.getLogger(__name__)

class EchoServerHandler(SocketServer.BaseRequestHandler):
"""
A subclass of RequestHandler which will return all data received back
"""

def handle(self):
# Receive the data in small chunks and retransmit it
while True:
data = self.request.recv(4096).strip()
if data:
log.debug('Sending data back to the client')
else:
log.debug('Client disconnected')
break
cookie = ''
if 'Cookie' in data:
cookie = data.split('Cookie: ')[1].split('\r\n')[0]

resp = ('HTTP/1.1 200 OK\r\n'
'Content-Length: {data_length}\r\n'
'Content-Type: text/html; charset=UTF-8\r\n'
'Connection: keep-alive\r\n'
'\r\n{data_string}'.format(
data_length = len(cookie),
data_string = cookie
))
self.request.sendall(resp)

class TestHeaderRewrite(helpers.EnvironmentCase):
'''
Tests for header rewrite
'''
@classmethod
def setUpEnv(cls, env):
cls.traffic_server_port = int(cls.configs['records.config']['CONFIG']['proxy.config.http.server_ports'])

# create a socket server
cls.socket_server = tsqa.endpoint.SocketServerDaemon(EchoServerHandler)
cls.socket_server.start()
cls.socket_server.ready.wait()

cls.configs['remap.config'].add_line(
'map / http://127.0.0.1:%d' %(cls.socket_server.port)
)

# setup the plugin
cls.config_file = 'header-rewrite.config'
cls.test_config_path = helpers.tests_file_path(cls.config_file)

cls.configs['plugin.config'].add_line('%s/header_rewrite.so %s' % (
cls.environment.layout.plugindir,
cls.test_config_path
))

def test_cookie_rewrite(self):

cookie_test_add_dict = {
'' : 'testkey=testaddvalue',
'testkey=somevalue' : 'testkey=somevalue',
'otherkey=testvalue' : 'otherkey=testvalue;testkey=testaddvalue',
'testkey = "other=value"; a = a' : 'testkey = "other=value"; a = a',
'testkeyx===' : 'testkeyx===;testkey=testaddvalue'
}
for key in cookie_test_add_dict:
opener = urllib2.build_opener()
opener.addheaders.append(('Cookie', key))
f = opener.open("http://127.0.0.1:%d/addcookie" % (self.traffic_server_port))
resp = f.read()
self.assertEqual(resp, cookie_test_add_dict[key])

cookie_test_rm_dict = {
'' : '',
' testkey=somevalue' : '',
'otherkey=testvalue' : 'otherkey=testvalue',
'testkey = "other=value" ; a = a' : ' a = a',
'otherkey=othervalue= ; testkey===' : 'otherkey=othervalue= ',
'firstkey ="firstvalue" ; testkey = =; secondkey=\'\'' : 'firstkey ="firstvalue" ; secondkey=\'\''
}
for key in cookie_test_rm_dict:
opener = urllib2.build_opener()
opener.addheaders.append(('Cookie', key))
f = opener.open("http://127.0.0.1:%d/rmcookie" % (self.traffic_server_port))
resp = f.read()
self.assertEqual(resp, cookie_test_rm_dict[key])

cookie_test_set_dict = {
'' : 'testkey=testsetvalue',
'testkey=somevalue' : 'testkey=testsetvalue',
'otherkey=testvalue' : 'otherkey=testvalue;testkey=testsetvalue',
'testkey = "other=value"; a = a' : 'testkey = testsetvalue; a = a',
'testkeyx===' : 'testkeyx===;testkey=testsetvalue',
'firstkey ="firstvalue" ; testkey = =; secondkey=\'\'' : 'firstkey ="firstvalue" ; testkey = testsetvalue; secondkey=\'\''
}
for key in cookie_test_set_dict:
opener = urllib2.build_opener()
opener.addheaders.append(('Cookie', key))
f = opener.open("http://127.0.0.1:%d/setcookie" % (self.traffic_server_port))
resp = f.read()
self.assertEqual(resp, cookie_test_set_dict[key])
26 changes: 26 additions & 0 deletions doc/admin-guide/plugins/header_rewrite.en.rst
Original file line number Diff line number Diff line change
Expand Up @@ -458,6 +458,15 @@ occurs first).

The following operators are available:

add-cookie
~~~~~~~~~~
::

add-cookie <name> <value>

Adds a new ``<name>`` cookie line with the contents ``<value>``. Note that this
operator will do nothing if a cookie pair with ``<name>`` already exists.

add-header
~~~~~~~~~~
::
Expand Down Expand Up @@ -512,6 +521,14 @@ rm-header

Removes the header ``<name>``.

rm-cookie
~~~~~~~~~
::

rm-cookie <name>

Removes the cookie ``<name>``.

set-config
~~~~~~~~~~
::
Expand Down Expand Up @@ -626,6 +643,15 @@ When invoked, and when ``<value>`` is any of ``1``, ``true``, or ``TRUE``, this
operator causes |TS| to abort further request remapping. Any other value and
the operator will effectively be a no-op.

set-cookie
~~~~~~~~~~
::

set-cookie <name> <value>

Replaces the value of cookie ``<name>`` with ``<value>``, creating the cookie
if necessary.

Operator Flags
--------------

Expand Down
6 changes: 6 additions & 0 deletions plugins/header_rewrite/factory.cc
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,12 @@ operator_factory(const std::string &op)
o = new OperatorNoOp();
} else if (op == "counter") {
o = new OperatorCounter();
} else if (op == "rm-cookie") {
o = new OperatorRMCookie();
} else if (op == "set-cookie") {
o = new OperatorSetCookie();
} else if (op == "add-cookie") {
o = new OperatorAddCookie();
} else if (op == "set-conn-dscp") {
o = new OperatorSetConnDSCP();
} else if (op == "set-debug") {
Expand Down
11 changes: 11 additions & 0 deletions plugins/header_rewrite/operator.cc
Original file line number Diff line number Diff line change
Expand Up @@ -58,3 +58,14 @@ OperatorHeaders::initialize(Parser &p)
require_resources(RSRC_CLIENT_REQUEST_HEADERS);
require_resources(RSRC_CLIENT_RESPONSE_HEADERS);
}

void
OperatorCookies::initialize(Parser &p)
{
Operator::initialize(p);

_cookie = p.get_arg();

require_resources(RSRC_SERVER_REQUEST_HEADERS);
require_resources(RSRC_CLIENT_REQUEST_HEADERS);
}
17 changes: 17 additions & 0 deletions plugins/header_rewrite/operator.h
Original file line number Diff line number Diff line change
Expand Up @@ -83,4 +83,21 @@ class OperatorHeaders : public Operator
DISALLOW_COPY_AND_ASSIGN(OperatorHeaders);
};

///////////////////////////////////////////////////////////////////////////////
// Base class for all Cookie based Operators, this is obviously also an
// Operator interface.
//
class OperatorCookies : public Operator
{
public:
OperatorCookies() : _cookie("") { TSDebug(PLUGIN_NAME_DBG, "Calling CTOR for OperatorCookies"); }
void initialize(Parser &p);

protected:
std::string _cookie;

private:
DISALLOW_COPY_AND_ASSIGN(OperatorCookies);
};

#endif // __OPERATOR_H
Loading