1Advanced Features
2-----------------
3
4Dynamic substitutions
5^^^^^^^^^^^^^^^^^^^^^
6
7Dynamic substitution are mark-up placed in element of the scenario.
8For HTTP, this mark-up can be placed in basic authentication (www\_authenticate
9tag: userid and passwd attributes), URL (to change GET parameter)
10and POST content.
11
12Those mark-up are of the form ``%%Module:Function%%``.
13Substitutions are executed on a request-by-request basis, only if the
14request tag has the attribute ``subst="true"``.
15
16When a substitution is requested, the substitution mark-up is replaced by
17the result of the call to the Erlang function:
18``Module:Function({Pid, DynData})`` where ``Pid`` is the Erlang process
19id of the current virtual user and DynData the list of all Dynamic
20variables (**Warn: before version 1.1.0, the argument was just the
21Pid!**).
22
23Here is an example of use of substitution in a Tsung scenario:
24
25.. code-block:: xml
26
27   <session name="rec20040316-08:47" probability="100" type="ts_http">
28     <request subst="true">
29       <http url="/echo?symbol=%%symbol:new%%" method="GET"></http>
30     </request>
31   </session>
32
33For the http plugin, and since version 1.5.1, you can use the special value
34``subst='all_except_body'`` instead of ``'true'`` to skip the substitutions in
35the body part of the HTTP response.
36
37Here is the Erlang code of the module used for dynamic substitution:
38
39.. code-block:: erlang
40
41 -module(symbol).
42 -export([new/1]).
43
44 new({Pid, DynData}) ->
45    case random:uniform(3) of
46        1 -> "IBM";
47        2 -> "MSFT";
48        3 -> "RHAT"
49    end.
50
51
52Use :command:`erlc` to compiled the code, and put the resulting .beam
53file in :file:`\$PREFIX/lib/erlang/lib/tsung-X.X.X/ebin/` on all client
54machines.
55
56As you can see, writing scenario with dynamic substitution is
57simple. It can be even simpler using dynamic variables (see later).
58
59.. index:: ts_user_server
60
61If you want to set unique id, you can use the built-in function
62**ts_user_server:get_unique_id**.
63
64.. code-block:: xml
65
66 <session name="rec20040316-08:47" probability="100" type="ts_http">
67   <request subst="true">
68     <http url="/echo?id=%%ts_user_server:get_unique_id%%" method="GET" />
69   </request>
70 </session>
71
72
73Reading external file
74^^^^^^^^^^^^^^^^^^^^^
75
76**New in 1.0.3**: A new module ``ts_file_server`` is available. You
77can use it to read external files. For example, if you need to read user
78names and passwd from a CSV file, you can do it with it (currently,
79you can read only a single file).
80
81
82You have to add this in the XML configuration file:
83
84.. code-block:: xml
85
86 <option name="file_server"  value="/tmp/userlist.csv"></option>
87
88
89**New in 1.2.2**: You can read several files, using the **id**
90attribute to identify each file:
91
92.. code-block:: xml
93
94 <option name="file_server" value="/tmp/userlist.csv"></option>
95 <option name="file_server" id='random' value="/tmp/randomnumbers.csv"></option>
96
97
98Now you can build your own function to use it, for example, create a
99file called :file:`readcsv.erl`:
100
101.. code-block:: erlang
102
103 -module(readcsv).
104 -export([user/1]).
105
106 user({Pid,DynVar})->
107    {ok,Line} = ts_file_server:get_next_line(),
108    [Username, Passwd] = string:tokens(Line,";"),
109    "username=" ++ Username ++"&password=" ++ Passwd.
110
111
112The output of the function will be a string ``username=USER&password=PASSWORD``
113
114Then compile it with :command:`erlc readcsv.erl` and put
115:file:`readcsv.beam` in :file:`$prefix/lib/erlang/lib/tsung-VERSION/ebin` directory (if the
116file has an id set to ``random``, change the call to ``ts_file_server:get_next_line(random)``).
117
118Then use something like this in your session:
119
120.. code-block:: xml
121
122  <request subst="true">
123    </http>
124  </request>
125
126
127Two functions are available: ``ts_file_server:get_next_line``
128and ``ts_file_server:get_random_line``. For the
129``get_next_line`` function, when the end of file is reached, the
130first line of the file will be the next line.
131
132**New in 1.3.0**: you no longer have to create an external
133function to parse a simple csv file: you can use ``setdynvars``
134(see next section for detailed documentation):
135
136.. code-block:: xml
137
138 <setdynvars sourcetype="file" fileid="userlist.csv" delimiter=";" order="iter">
139  <var name="username" />
140  <var name="user_password" />
141 </setdynvars>
142
143
144This defines two dynamic variables **username** and
145**user_password** filled with the next entry from the csv
146file. Using the previous example, the request is now:
147
148.. code-block:: xml
149
150  <request subst="true">
151    <http url='/login.cgi' version='1.0'
152      contents='username=%%_username%%&amp;password=%%_user_password%%&amp;op=login'
153    content_type='application/x-www-form-urlencoded' method='POST'>
154    </http>
155  </request>
156
157
158Much simpler than the old method!
159
160In case you have several arrival phases programmed and if you use file with
161``order="iter"`` the position in the file will not be reset between different
162arrival phase. You will not be returned to the first line when changing phase.
163
164.. code-block:: xml
165
166  <arrivalphase phase="1" duration="10" unit="minute">
167    <users maxnumber="10" arrivalrate="100" unit="second" />
168  </arrivalphase>
169  <arrivalphase phase="2" duration="10" unit="minute">
170    <users maxnumber="20" arrivalrate="100" unit="second"></users>
171  </arrivalphase>
172
173
174In this example phase 1 will read about 10 lines and phase 2 will read the next
17520 lines.
176
177.. TODO explain, that file servers are synchronized between tsung nodes in a distributed setup.
178
179.. index:: dyn_variable
180.. _sec-dynamic-variables-label:
181
182Dynamic variables
183^^^^^^^^^^^^^^^^^
184
185In some cases, you may want to use a value given by the server in a
186response later in the session, and this value is **dynamically
187generated** by the server for each user. For this, you can use
188``<dyn_variable>`` in the scenario
189
190Let's take an example with HTTP. You can easily grab a value in a HTML
191form like:
192
193.. code-block:: html
194
195 <form action="go.cgi" method="POST">
196   <hidden name="random_num" value="42"></form>
197 </form>
198
199with:
200
201.. code-block:: xml
202
203 <request>
204   <dyn_variable name="random_num"></dyn_variable>
205   <http url="/testtsung.html" method="GET" version="1.0"></http>
206 </request>
207
208
209Now ``random_num`` will be set to 42 during the users session. Its
210value will be replace in all mark-up of the form
211``%%_random_num%%`` if and only if the ``request`` tag has the
212attribute ``subst="true"``, like:
213
214.. code-block:: xml
215
216  <request subst="true">
217    <http url="/go.cgi" version="1.0"
218      contents="username=nic&amp;random_num=%%_random_num%%&amp;op=login"
219      content_type="application/x-www-form-urlencoded" method="POST">
220    </http>
221  </request>
222
223
224Regexp
225""""""
226
227If the dynamic value is not a form variable, you can set a regexp by
228hand, for example to get the title of a HTML page: the regexp engine
229uses the ``re`` module, a Perl like regular expressions module
230for Erlang.
231
232.. code-block:: xml
233
234    <request>
235      <dyn_variable name="mytitlevar"
236                    re="&lt;title&gt;(.*)&lt;/title&gt;"/>
237      <http url="/testtsung.html" method="GET" version="1.0"></http>
238    </request>
239
240
241Previously (before 1.4.0), Tsung uses the old ``regexp`` module
242from Erlang. This is now deprecated. The syntax was:
243
244.. code-block:: xml
245
246    <request>
247      <dyn_variable name="mytitlevar"
248                    regexp="&lt;title&gt;\(.*\)&lt;/title&gt;"/>
249      <http url="/testtsung.html" method="GET" version="1.0"></http>
250    </request>
251
252.. index:: xpath
253
254XPath
255"""""
256
257A new way to analyze the server response has been introduced in the
258release **1.3.0**. It is available only for the HTTP and XMPP plugin since it is
259based on XML/HTML parsing. This feature uses the mochiweb library
260and **only works with Erlang R12B and newer version**.
261
262This give us some benefices:
263
264* XPath is simple to write and to read, and match very well with
265  HTML/XML pages
266
267* The parser works on ``binaries()``, and doesn't create any
268  ``string()``.
269
270* The cost of parsing the HTML/XML and build the tree is amortized
271  between all the dyn_variables defined for a given request
272
273
274To utilize XPath expression, use a ``xpath`` attribute when
275defining the ``dyn_variable``, instead of ``re``, like:
276
277.. code-block:: xml
278
279 <dyn_variable name="field1_value" xpath="//input[@name='field1']/@value"/>
280 <dyn_variable name="title" xpath="/html/head/title/text()"/>
281
282
283There is a bug in the XPath engine, result nodes from
284"descendant-or-self" aren't returned in document order. This isn't a
285problem for the most common cases.
286
287However, queries like ``//img[1]/@src`` are not recommended,
288as the order of the ``<img>`` elements returned from ``//img`` is
289not the expected.
290
291The order is respected for paths without "descendant-or-self" axis, so
292this: ``/html/body/div[2]/img[3]/@src`` is interpreted as
293expected and can be safely used.
294
295It is possible to use XPath to get a list of elements from an html page,
296allowing dynamic retrieval of objects. You can either create embedded
297Erlang code to parse the list produced, or use foreach that was introduced
298in release **1.4.0**.
299
300For XMPP, you can get all the contacts in a dynamic variable:
301
302.. code-block:: xml
303
304 <request subst="true">
305    <dyn_variable name="contactJids"
306      xpath="//iq[@type='result']/query[@xmlns='jabber:iq:roster']//item[string-length(@wr:type)=0]/@jid" />
307    <jabber type="iq:roster:get" ack="local"/>
308 </request>
309
310
311.. index:: jsonpath
312
313.. _sec-jsonpath-label:
314
315JSONPath
316""""""""
317
318Another way to analyze the server response has been introduced in the
319release **1.3.2** when the server is sending JSON data. It is
320only for the HTTP plugin. This feature uses the mochiweb library and
321**only works with Erlang R13B and newer version**.
322
323Tsung implements a (very) limited subset of JSONPath as defined here
324http://goessner.net/articles/JsonPath/
325
326To utilize jsonpath expression, use a **jsonpath** attribute when
327defining the ``<dyn_variable>>``, instead of ``re``, like:
328
329.. code-block:: xml
330
331   <dyn_variable name="array3_value" jsonpath="field.array[3].value"/>
332
333
334You can also use expressions ``Key=Val``, e.g.:
335
336.. code-block:: xml
337
338   <dyn_variable name="myvar" jsonpath="field.array[?name=bar].value"/>
339
340
341PostgreSQL
342""""""""""
343
344.. versionadded:: 1.3.2
345
346Since the  PostgreSQL protocol is binary, regexp are not useful to
347parse the output of the server. Instead, a specific parsing can be
348done to extract content from the server's response; to do this, use the
349``pgsql_expr`` attribute. Use ``data_row[L][C]`` to
350extract the  column C of the  line L of the data output. You can also use
351the literal name of the column (ie. the field name of the
352table). This example extract 3 dynamic variables from the server's
353response:
354
355First one, extract the 3rd column of the fourth row, then the ``mtime``
356field from the second row, and then it extract some data of the
357``row_description``.
358
359.. code-block:: xml
360
361 <request>
362   <dyn_variable name="myvar" pgsql_expr="data_row[4][3]"/>
363   <dyn_variable name="mtime" pgsql_expr="data_row[2].mtime"/>
364   <dyn_variable name="row" pgsql_expr="row_description[1][3][1]"/>
365   <pgsql type="sql">SELECT * from pgbench_history LIMIT 20;</pgsql>
366 </request>
367
368
369A row description looks like this::
370
371  | =INFO REPORT==== 14-Apr-2010::11:03:22 ===
372  |            ts_pgsql:(7:<0.102.0>) PGSQL: Pair={row_description,
373  |                                                [{"tid",text,1,23,4,-1,16395},
374  |                                                 {"bid",text,2,23,4,-1,16395},
375  |                                                 {"aid",text,3,23,4,-1,16395},
376  |                                                 {"delta",text,4,23,4,-1,16395},
377  |                                                 {"mtime",text,5,1114,8,-1,16395},
378  |                                                 {"filler",text,6,1042,-1,26,16395}]}
379
380
381So in the example, the **row** variable equals "aid".
382
383Decoding variables
384""""""""""""""""""
385
386It's possible to decode variable that contains html entities encoded,
387this is done with **decode** attribute set to **html_entities**.
388
389.. code-block:: xml
390
391 <request>
392   <dyn_variable name="mytitlevar"
393                 re="&lt;title&gt;(.*)&lt;/title&gt;"
394                 decode="html_entities"/>
395   <http url="/testtsung.html" method="GET" version="1.0"></http>
396 </request>
397
398.. index:: setdynvars
399
400set_dynvars
401"""""""""""
402
403**Since version 1.3.0**, more powerful dynamic variables are implemented.
404
405You can set dynamic variables not only while parsing server data, but
406you can build them using external files or generate them with a function
407or generate random numbers/strings:
408
409Several types of dynamic variables are implemented (``sourcetype`` attribute):
410
411.. index:: callback
412
413* Dynamic variables defined by calling an Erlang function:
414
415  .. code-block:: xml
416
417     <setdynvars sourcetype="erlang" callback="ts_user_server:get_unique_id">
418        <var name="id1" />
419
420.. index:: delimiter
421.. index:: fileid
422.. index:: iter
423
424* Dynamic variables defined by parsing an external file:
425
426  .. code-block:: xml
427
428     <setdynvars sourcetype="file" fileid="userdb" delimiter=";" order="iter">
429       <var name="user" />
430       <var name="user_password" />
431     </setdynvars>
432
433  *delimiter* can be any string, and *order* can be
434  **iter** or **random**
435
436*  A dynamic variable can be a random number (uniform distribution)
437
438   .. code-block:: xml
439
440      <setdynvars sourcetype="random_number" start="3" end="32">
441        <var name="rndint" />
442      </setdynvars>
443
444* A dynamic variable can be a random string
445
446  .. code-block:: xml
447
448     <setdynvars sourcetype="random_string" length="13">
449        <var name="rndstring1" />
450     </setdynvars>
451
452* A dynamic variable can be a urandom string: this is much faster than
453  the random string, but the string is not really random: the same set
454  of characters is always used.
455
456* A dynamic variable can be generated by dynamic evaluation of erlang code:
457
458  .. code-block:: xml
459
460     <setdynvars sourcetype="eval"
461                 code="fun({Pid,DynVars})->
462                           {ok,Val}=ts_dynvars:lookup(md5data,DynVars),
463                           ts_digest:md5hex(Val) end.">
464       <var name="md5sum" />
465     </setdynvars>
466
467
468  In this case, we use tsung function ``ts_dynvars:lookup`` to retrieve the
469  dynamic variable named ``md5data``. This dyn\_variable ``md5data``
470  can be set in any of the ways described in the Dynamic variables
471  section :ref:`sec-dynamic-variables-label`.
472
473* A dynamic variable can be generated by applying a JSONPath
474  specification (see :ref:`sec-jsonpath-label`) to an existing dynamic
475  variable:
476
477  .. code-block:: xml
478
479     <setdynvars sourcetype="jsonpath" from="notification" jsonpath="result[?state=OK].node">
480       <var name="deployed" />
481     </setdynvars>
482
483* You can create dynamic variables to get the hostname and port of the current server
484
485  .. code-block:: xml
486
487    <setdynvars sourcetype="server">
488      <var name="host" />
489      <var name="port" />
490    </setdynvars>
491
492
493* You can define a dynamic variable as constant value to use it in
494  a plugin (since version **1.5.0**)
495
496  .. code-block:: xml
497
498     <setdynvars sourcetype="value" value="foobar">
499       <var name="constant" />
500     </setdynvars>
501
502
503
504
505A **setdynvars** can be defined anywhere in a session.
506
507
508.. index:: match
509
510Checking the server's response
511^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
512
513With the tag ``match`` in a ``<request>`` tag, you can check
514the server's response against a given string, and do some actions
515depending on the result. In any case, if it matches, this will
516increment the ``match`` counter, if it does not match, the
517``nomatch`` counter will be incremented.
518
519For example, let's say you want to test a login page. If the login is
520ok, the server will respond with ``Welcome !`` in the
521HTML body, otherwise not. To check that:
522
523.. code-block:: xml
524
525 <request>
526    <match do="continue" when="match">Welcome !</match>
527    <http url="/login.php" version="1.0" method="POST"
528          contents="username=nic&amp;user_password=sesame"
529          content_type="application/x-www-form-urlencoded" >
530 </request>
531
532
533You can use a regexp instead of a simple string.
534
535The list of available actions to do is:
536
537* **continue**: do nothing, continue (only update match or nomatch counters)
538
539* **log**: log the request id, userid, sessionid, name in a file (in :file:`match.log`)
540
541* **abort**: abort the session
542
543* **abort_test**: abort the whole test
544
545* **restart**: restart the session. The maximum number of
546  restarts is 3 by default.
547
548* **loop**: repeat the request, after 5 seconds. The maximum number of
549  loops is 20 by default.
550
551* **dump**: dump the content of the response in a file. The filename
552  is :file:`match-<userid>-<sessionid>-<requestid>-<dumpid>.dump`
553
554
555You can mixed several match tag in a single request:
556
557.. code-block:: xml
558
559   <request>
560     <match do="loop" sleep_loop="5" max_loop="10" when="match">Retry</match>
561     <match do="abort" when="match">Error</match>
562     <http url='/index.php' method=GET'>
563   </request>
564
565
566You can also do the action on **nomatch** instead of **match**.
567
568.. index:: skip_headers
569.. index:: apply_to_content
570
571If you want to skip the HTTP headers, and match only on the body, you
572can use **skip_headers='http'**. Also, you can apply a
573function to the content before matching; for example the following
574example use both features to compute the md5sum on the body of a HTTP
575response, and compares it to a given value:
576
577.. code-block:: xml
578
579   <match do='log' when='nomatch' skip_headers='http' apply_to_content='ts_digest:md5hex'>01441debe3d7cc65ba843eee1acff89d</match>
580   <http url="/" method="GET" version="1.1"/>
581
582
583You can also use dynamic variables, using the **subst** attribute:
584
585.. code-block:: xml
586
587   <match do='log' when='nomatch' subst='true' >%%_myvar%%</match>
588   <http url="/" method="GET"/>
589
590
591**Since 1.5.0**, it's now possible to add **name** attribute in **match** tag to name a record printed in match.log as follow:
592
593.. code-block:: xml
594
595   <match do='log' when='match' name='http_match_200ok'>200OK</match>
596   <http url="/" method="GET" version="1.1"/>
597
598
599Loops, If, Foreach
600^^^^^^^^^^^^^^^^^^
601
602**Since 1.3.0**, it's now possible to add conditional/unconditional loops in a session.
603
604**Since 1.4.0**, it is possible to loop through a list of dynamic variables thanks to foreach.
605
606.. index:: for
607
608<for>
609"""""
610
611Repeat the enclosing actions a fixed number of times. A dynamic
612variable is used as counter, so the current iteration could be used in
613requests. List of attributes:
614
615``from``
616  Initial value
617``to``
618  Last value
619``incr``
620  Amount to increment in each iteration
621``var``
622  Name of the variable to hold the counter
623
624
625.. code-block:: xml
626
627 <for from="1" to="10" incr="1" var="counter">
628   ...
629   <request> <http url="/page?id=%%_counter%%"></http> </request>
630   ...
631 </for>
632
633.. index:: repeat
634.. index:: while
635.. index:: until
636
637<repeat>
638""""""""
639
640Repeat the enclosing action (while or until) some condition. This is
641intended to be used together with ``<dyn_variable>`` declarations. List of
642attributes:
643
644``name``
645  Name of the repeat
646
647``max_repeat``
648  Max number of loops (default value is 20)
649
650
651The last element of repeat must be either ``<while>`` or ``<until>`` example:
652
653.. code-block:: xml
654
655 <repeat name="myloop" max_repeat="40">
656   ...
657   <request>
658     <dyn_variable name="result" re="Result: (.*)"/>
659     <http url="/random" method="GET" version="1.1"></http>
660   </request>
661   ...
662   <until var="result" eq="5"/>
663 </repeat>
664
665
666**Since 1.3.1**, it's also possible to add if statements based on
667dynamic variables:
668
669.. index:: if
670
671<if>
672""""
673
674.. code-block:: xml
675
676 <if var="tsung_userid" eq="3">
677   <request> <http url="/foo"/> </request>
678   <request> <http url="/bar"/> </request>
679 </if>
680
681
682You can use ``eq`` or ``neq`` to check the variable.
683
684**Since 1.5.1** you can also use the comparison operators ``gt``,
685``gte``, ``lt`` and ``lte`` to do respectively ``greater than``,
686``greater than or equal to``, ``less than`` and ``less than or equal to``.
687
688If the dynamic variable is a list (output from XPath for example), you
689can access to the n-th element of a list like this:
690
691.. code-block:: xml
692
693 <if var="myvar[1]" eq="3">
694
695Here we compare the first element of the list to 3.
696
697.. index:: abort
698
699<abort>
700""""""""
701**Since 1.7.0** you can abort the session or the whole test by using an ``<abort/>`` element in a session (can be used inside an <if> statement for example). By default it will abort the current user session, but you can abort the whole test by setting the `type` attribute to `all`  ``<abort type='all'/>``
702
703.. index:: foreach
704
705<foreach>
706"""""""""
707
708Repeat the enclosing actions for all the elements contained in the list specified. The basic syntax is as follows:
709
710.. code-block:: xml
711
712 <foreach name="element" in="list">
713   <request subst="true">
714    <http url="%%_element%%" method="GET" version="1.1"/>
715   </request>
716 </foreach>
717
718
719It is possible to limit the list of elements you're looping through, thanks to the use of the ``include`` or ``exclude`` attributes inside the foreach statement.
720
721As an example, if you want to include only elements with a local path you can write:
722
723.. code-block:: xml
724
725 <foreach name="element" in="list" include="^/.*$">
726
727
728If you want to exclude all the elements from a specific URI, you would write:
729
730.. code-block:: xml
731
732 <foreach name="element" in="list" exclude="http:\/\/.*\.tld\.com\/.*$">
733
734
735You can combine this with a XPath query. For instance the following scenario will retrieve all the images specified on a web page:
736
737
738.. code-block:: xml
739
740 <request subst="true">
741   <dyn_variable name="img_list" xpath="//img/@src"/>
742   <http url="/mypage.html" method="GET" version="1.1"/>
743 </request>
744 <foreach name="img" in="img_list">
745   <request subst="true">
746     <http url="%%_img%%" method="GET" version="1.1"/>
747   </request>
748 </foreach>
749
750Rate limiting
751^^^^^^^^^^^^^
752
753Since version **1.4.0**, rate limiting can be enabled, either globally
754(see :ref:`sec-options-label`), or for each session separately.
755
756For example, to limit the rate to 64KB/sec for a given session:
757
758.. code-block:: xml
759
760  <session name="http-example" probability="70" type="ts_http">
761    <set_option name="rate_limit" value="64" />
762    ...
763  </session>
764
765
766Only the incoming traffic is rate limited currently.
767
768.. index:: tag
769
770Requests exclusion
771^^^^^^^^^^^^^^^^^^
772
773.. versionadded:: 1.5.1
774
775It is possible to exclude some request for a special run. To do this
776you have to tag them and use the option ``-x`` when launching the run.
777
778For example, to exclude the GET of foo.png, add a ``tag`` to the
779respective request:
780
781.. code-block:: xml
782
783   <request>
784     <http url="/" method="GET"></http>
785   </request>
786   <request tag="image">
787     <http url="/foo.png" method="GET"></http>
788   </request>
789
790Then launch the run with::
791
792   tsung -f SCENARIO.xml -x image start
793
794Only the GET to ``/`` will be performed.
795
796Note that request tags also get logged on **dumptraffic="protocol"** (see :ref:`sec-file-structure-label`)
797
798Client certificate
799^^^^^^^^^^^^^^^^^^
800.. versionadded:: 1.5.1
801
802It is possible to use a client certificate for ssl authentication. You
803can use dynamic variables to set some parameters of the certificate
804(and the key password is optional).
805
806.. code-block:: xml
807
808  <session name="https-with-cert" probability="70" type="ts_http">
809
810    <set_option name="certificate">
811      <certificate cacertfile="/etc/ssl/ca.pem"
812                   keyfile="%%_keyfile%%" keypass="%%_keypass%%" certfile="/home/nobody/.tsung/client.pem"/>
813    </set_option>
814