1.. index:: session
2.. _sessions-label:
3
4========
5Sessions
6========
7
8Sessions define the content of the scenario itself. They describe
9the requests to execute.
10
11Each session has a given probability. This is used to decide which
12session a new user will execute. The sum of all session's
13probabilities must be 100.
14
15**Since Tsung 1.5.0**, you can use weights instead of
16probabilities. In the following example, there will be twice as many
17sessions of type s1 than s2.
18
19.. code-block:: xml
20
21  <session name="s1" weight="2" type="ts_http">
22  <session name="s2" weight="1" type="ts_http">
23
24
25A transaction is just a way to have customized statistics. Say if you
26want to know the response time of the login page of your website, you
27just have to put all the requests of this page (HTML + embedded
28pictures) within a transaction. In the example above, the transaction
29called ``index_request`` will gives you in the
30statistics/reports the mean response time to get
31``index.en.html + header.gif``. Be warn that If you have a
32thinktime inside the transaction, the thinktime will be part of the
33response time.
34
35.. index:: thinktimes
36
37Thinktimes
38^^^^^^^^^^
39
40You can set static or random thinktimes to separate requests. By
41default, a random thinktime will be a exponential distribution with
42mean equals to ``value``.
43
44.. code-block:: xml
45
46 <thinktime value="20" random="true"></thinktime>
47
48
49In this case, the thinktime will be an exponential distribution with a
50mean equals to 20 seconds.
51
52**Since version 1.3.0**, you can also use a range
53``[min:max]`` instead of a mean for random thinktimes (the
54distribution will be uniform in the interval):
55
56.. code-block:: xml
57
58 <thinktime min="2" max="10" random="true"></thinktime>
59
60
61**Since version 1.4.0**, you can use a dynamic variable to set
62the thinktime value:
63
64.. code-block:: xml
65
66 <thinktime value="%%_rndthink%%" random="true"></thinktime>
67
68You can also synchronize all users using the ``wait_global`` value:
69
70.. code-block:: xml
71
72 <thinktime value='wait_global'>
73
74which means: wait for all (N) users to be connected and waiting for
75the global lock (the value can be set using the option ``<option
76name="global_number" value ="XXX"/>`` and by setting `maxnumber=N` in
77``<arrivalphase>``).
78
79**Since version 1.6.0**, you can wait for a 'bidi' ack. If your protocol is bidirectional (e.g. xmpp, websocket, ...), you can wait until the server sends some data, and the code that handle this data exits the ``think`` state.
80
81.. code-block:: xml
82
83   <thinktime value="wait_bidi"></thinktime> -
84
85HTTP
86^^^^
87
88
89This example shows several features of the HTTP protocol support in
90Tsung: GET and POST request, basic authentication, transaction for
91statistics definition, conditional request (IF MODIFIED SINCE):
92
93
94.. code-block:: xml
95
96 <sessions>
97  <session name="http-example" probability="70" type="ts_http">
98
99    <request> <http url="/" method="GET" version="1.1">
100                    </http> </request>
101    <request> <http url="/images/logo.gif"
102               method="GET" version="1.1"
103               if_modified_since="Fri, 14 Nov 2003 02:43:31 GMT">
104              </http></request>
105
106    <thinktime value="20" random="true"></thinktime>
107
108    <transaction name="index_request">
109     <request><http url="/index.en.html"
110                          method="GET" version="1.1" >
111              </http> </request>
112     <request><http url="/images/header.gif"
113                          method="GET" version="1.1">
114              </http> </request>
115    </transaction>
116
117    <thinktime value="60" random="true"></thinktime>
118    <request>
119      <http url="/" method="POST" version="1.1"
120               contents="bla=blu">
121      </http> </request>
122    <request>
123       <http url="/bla" method="POST" version="1.1"
124             contents="bla=blu&amp;name=glop">
125       <www_authenticate userid="Aladdin"
126                         passwd="open sesame"/></http>
127    </request>
128  </session>
129
130  <session name="backoffice" probability="30" >
131   <!--  -->
132  </session>
133 </sessions>
134
135
136If you use an absolute URL, the server used in the URL will override
137the one specified in the ``<server>`` section. The following relative
138requests in the session will also use this new server value (until a
139new absolute URL is set).
140
141**New in 1.2.2**: You can add any HTTP header now, as in:
142
143.. code-block:: xml
144
145 <request>
146   <http url="/bla" method="POST" contents="bla=blu&amp;name=glop">
147     <www_authenticate userid="Aladdin" passwd="open sesame"/>
148     <http_header name="Cache-Control" value="no-cache"/>
149     <http_header name="Referer" value="http://www.w3.org/"/>
150   </http>
151 </request>
152
153
154**New in 1.3.0**: You can also read the content of a POST or PUT
155request from an external file:
156
157.. code-block:: xml
158
159 <http url="mypage" method="POST" contents_from_file="/tmp/myfile" />
160
161
162Since **1.3.1**, you can also manually set a cookie, though the
163cookie is not persistent: you must add it in every ``<requests>``:
164
165.. code-block:: xml
166
167 <http url="/">
168   <add_cookie key="foo" value="bar"/>
169   <add_cookie key="id"  value="123"/>
170 </http>
171
172
173Authentication
174""""""""""""""
175
176Until Tsung 1.5.0, only Basic authentication was implemented. You can
177now use Digest Authentication and OAuth 1.0.
178
179To use Digest authentication:
180
181.. code-block:: xml
182
183 <!-- 1. First request return 401. We use dynvars to fetch nonce and realm -->
184 <request>
185   <dyn_variable name="nonce" header="www-authenticate/nonce"/>
186   <dyn_variable name="realm" header="www-authenticate/realm"/>
187   <http url="/digest" method="GET" version="1.1"/>
188 </request>
189
190  <!--
191  2. This request will be authenticated. Type="digest" is important.
192  We use the nonce and realm values returned from the previous
193  If the webserver returns the nextnonce we set it to the nonce dynvar
194  for use with the next request.
195  Else it stays set to the old value
196  -->
197  <request subst="true">
198    <dyn_variable name="nonce" header="authentication-info/nextnonce"/>
199    <http url="/digest" method="GET" version="1.1">
200      <www_authenticate userid="user" passwd="passwd" type="digest" realm="%%_realm%%" nonce="%%_nonce%%"/>
201    </http>
202  </request>
203
204
205To use OAuth authentication:
206
207.. code-block:: xml
208
209 <!-- Getting a Request Token -->
210
211   <request>
212     <dyn_variable name="access_token" re="oauth_token=([^&amp;]*)"/>
213       <dyn_variable name="access_token_secret" re="oauth_token_secret=([^&amp;]*)" />
214       <http url="/oauth/example/request_token.php" method="POST" version="1.1" contents="empty">
215         <oauth consumer_key="key" consumer_secret="secret"  method="HMAC-SHA1"/>
216       </http>
217   </request>
218
219   <!-- Getting an Access Token -->
220
221   <request subst='true'>
222        <dyn_variable name="access_token" re="oauth_token=([^&amp;]*)"/>
223        <dyn_variable name="access_token_secret" re="oauth_token_secret=([^&amp;]*)"/>
224          <http url="/oauth/example/access_token.php" method="POST" version="1.1" contents="empty">
225          <oauth consumer_key="key" consumer_secret="secret"  method="HMAC-SHA1" access_token="%%_access_token%%" access_token_secret="%%_access_token_secret%%"/>
226        </http>
227      </request>
228
229      <!-- Making Authenticated Calls -->
230
231      <request subst="true">
232        <http url="/oauth/example/echo_api.php" method="GET" version="1.1">
233         <oauth consumer_key="key" consumer_secret="secret" access_token="%%_access_token%%" access_token_secret="%%_access_token_secret%%"/>
234        </http>
235      </request>
236
237
238.. _sec-session-jabber-label:
239
240Jabber/XMPP
241^^^^^^^^^^^
242
243.. index:: jabber
244
245Here is an example of a session definition for the Jabber/XMPP protocol:
246
247.. code-block:: xml
248
249   <sessions>
250     <session probability="70" name="jabber-example" type="ts_jabber">
251
252       <request> <jabber type="connect" ack="local" /> </request>
253
254       <thinktime value="2"></thinktime>
255
256       <transaction name="authenticate">
257         <request> <jabber type="auth_get" ack="local"></jabber> </request>
258         <request> <jabber type="auth_set_plain" ack="local"></jabber> </request>
259       </transaction>
260
261       <request> <jabber type="presence:initial" ack="no_ack"/> </request>
262
263       <thinktime value="30"></thinktime>
264
265       <transaction name="online">
266         <request> <jabber type="chat" ack="no_ack" size="16" destination="online"/></request>
267       </transaction>
268
269       <thinktime value="30"></thinktime>
270
271       <transaction name="offline">
272         <request> <jabber type="chat" ack="no_ack" size="56" destination="offline"/><request>
273       </transaction>
274
275       <thinktime value="30"></thinktime>
276
277       <transaction name="close">
278         <request> <jabber type="close" ack="local"> </jabber></request>
279       </transaction>
280     </session>
281   </sessions>
282
283Message stamping
284""""""""""""""""
285
286It is possible to stamp chat message by setting ``stamped`` attribute of
287``<jabber>`` element inside request to ``true``. The stamp will include current
288timestamp and ID of the sender node. If the recipient will recognize the node ID,
289it will compare the timestamp inside message with the current one. The difference
290will be reported as ``xmpp_msg_latency`` metric (in milliseconds).
291The aim of node ID comparison is to avoid slight inconsistencies
292of timestamps on different Tsung nodes.
293
294Only a fraction of requests will hit the same node they originated from,
295but with request rate high enough this fraction should be sufficient.
296
297``stamped`` is allowed only with ``size`` attribute. ``data`` will cause
298``stamped`` to be ignored. There is a minimal length of the stamp,
299roughly 30 bytes. When ``size`` is greater than stamp length, random
300padding will be added to the stamp. If the stamp length is higher than
301``size``, then only stamp will be used as messagecontent, effectively
302exceeding specified length.
303
304StartTLS
305""""""""
306
307To secure a stream with STARTTLS, use:
308
309.. code-block:: xml
310
311 <jabber type="starttls" ack="bidi_ack" />
312
313Client certificate is implemented since **1.5.1**, for example, you can
314use dynamic variables like this:
315
316.. code-block:: xml
317
318 <jabber type="starttls" ack="bidi_ack"
319            cacertfile="%%_cacert%%"
320            certfile="%%_certfile%%"
321            keyfile="%%_keyfile%%" />
322
323Roster
324""""""
325
326What you can do with rosters using Tsung:
327
328You can
329
330#. Add a new contact to their roster
331   - The new contact is added to the ``Tsung Group`` group, and their name matches their JID
332
333#. Send a ``subscribe`` presence notification to the new contact's JID
334   - This results in a *pending* subscription
335
336#. Rename a roster contact
337   This changes the previously added contact's name from the default JID, to ``Tsung Testuser``
338
339#. Delete the previously added contact.
340
341
342Note that when you add a new contact, the contact JID is stored and
343used for the operations that follow. It is recommended that for each
344session which is configured to perform these operations, only do so
345once. In other words, you would NOT want to ADD more than one new
346contact per session. If you want to alter the rate that these roster
347functions are used during your test, it is best to use the session
348'probability' factor to shape this.
349
350
351The nice thing about this is that when you test run is complete, your
352roster tables should look the same as before you started the test. So,
353if you set it up properly, you can have pre-loaded roster entries
354before the test, and then use these methods to dynamically add,
355modify, and remove roster entries during the test as well.
356
357
358Example roster modification setup:
359
360.. code-block:: xml
361
362 <session probability="100" name="jabber-rostermod" type="ts_jabber">
363
364    <!-- connect, authenticate, roster 'get', etc... -->
365
366    <transaction name="rosteradd">
367      <request>
368        <jabber type="iq:roster:add" ack="no_ack" destination="online"></jabber>
369      </request>
370      <request>
371        <jabber type="presence:subscribe" ack="no_ack"/>
372      </request>
373    </transaction>
374
375    <!-- ... -->
376
377    <transaction name="rosterrename">
378      <request> <jabber type="iq:roster:rename" ack="no_ack"></jabber> </request>
379    </transaction>
380
381    <!-- ... -->
382
383    <transaction name="rosterdelete">
384      <request> <jabber type="iq:roster:remove" ack="no_ack"></jabber> </request>
385    </transaction>
386
387    <!-- remainder of session... -->
388
389  </session>
390
391
392See also :ref:`bidi-presence-label` for automatic handling of subscribing requests.
393
394.. index:: sasl plain
395
396SASL Plain
397""""""""""
398
399SASL Plain authentication example:
400
401.. code-block:: xml
402
403 <session probability="100" name="sasl" type="ts_jabber">
404
405    <request> <jabber type="connect" ack="local"></jabber> </request>
406
407    <thinktime value="10"></thinktime>
408
409    <transaction name="authenticate">
410     <request>
411       <jabber type="auth_sasl" ack="local"></jabber></request>
412
413     <request>
414       <jabber type="connect" ack="local"></jabber> </request>
415
416    <request>
417       <jabber type="auth_sasl_bind" ack="local" ></jabber></request>
418    <request>
419       <jabber type="auth_sasl_session" ack="local" ></jabber></request>
420
421    </transaction>
422
423
424SASL Anonymous
425""""""""""""""
426
427SASL Anonymous authentication example:
428
429.. code-block:: xml
430
431  <session probability="100" name="sasl" type="ts_jabber">
432
433    <request> <jabber type="connect" ack="local"></jabber> </request>
434
435    <thinktime value="10"></thinktime>
436
437    <transaction name="authenticate">
438     <request>
439       <jabber type="auth_sasl_anonymous" ack="local"></jabber></request>
440
441     <request>
442       <jabber type="connect" ack="local"></jabber> </request>
443
444    <request>
445       <jabber type="auth_sasl_bind" ack="local" ></jabber></request>
446    <request>
447       <jabber type="auth_sasl_session" ack="local" ></jabber></request>
448
449    </transaction>
450
451
452
453Presence
454""""""""
455
456
457* **type** can be either ``presence:broadcast`` or ``presence:directed``.
458* **show** value must be either ``away``, ``chat``, ``dnd``, or ``xa``.
459* **status** value can be any text.
460
461
462For more info, see section 2.2 of :RFC:`3921`.
463
464If you omit the **show** or **status** attributes, they default to **chat** and **Available** respectively.
465
466Example of broadcast presence (broadcast to members of your roster):
467
468.. code-block:: xml
469
470    <request>
471      <jabber type="presence:broadcast" show="away" status="Be right back..." ack="no_ack"/>
472    </request>
473
474    <thinktime value="5"></thinktime>
475
476    <request>
477      <jabber type="presence:broadcast" show="chat" status="Available
478      to chat" ack="no_ack"/>
479    </request>
480
481    <thinktime value="5"></thinktime>
482
483    <request>
484      <jabber type="presence:broadcast" show="dnd" status="Don't bother me!" ack="no_ack"/>
485    </request>
486    <thinktime value="5"></thinktime>
487
488    <request>
489     <jabber type="presence:broadcast" show="xa" status="I may never come back..."
490      ack="no_ack"/>
491    </request>
492    <thinktime value="5"></thinktime>
493
494    <request> <jabber type="presence:broadcast" ack="no_ack"/> </request>
495    <thinktime value="5"></thinktime>
496
497
498Example of directed presence (sent to random ``online`` users):
499
500.. code-block:: xml
501
502    <request>
503      <jabber type="presence:directed" show="away" status="Be right back..." ack="no_ack"/>
504    </request>
505    <thinktime value="5"></thinktime>
506
507    <request>
508      <jabber type="presence:directed" show="chat" status="Available to chat" ack="no_ack"/>
509    </request>
510    <thinktime value="5"></thinktime>
511
512    <request>
513      <jabber type="presence:directed" show="dnd" status="Don't bother me!" ack="no_ack"/>
514    </request>
515    <thinktime value="5"></thinktime>
516
517    <request>
518      <jabber type="presence:directed" show="xa" status="I may never come back..."
519        ack="no_ack"/>
520     </request>
521    <thinktime value="5"></thinktime>
522
523    <request>
524      <jabber type="presence:directed" ack="no_ack"/>
525    </request>
526    <thinktime value="5"></thinktime>
527
528
529MUC
530"""
531
532
533Tsung supports three MUC operations:
534
535* Join a room (attribute ``type='muc:join'``)
536* Send a message to a room (attribute ``type='muc:chat'``)
537* Change nickname (attribute ``type='muc:nick'``)
538* Exit a room (attribute ``type='muc:exit'``)
539
540
541Here's an example:
542
543.. code-block:: xml
544
545 <!-- First, choose an random room and random nickname: -->
546 <setdynvars sourcetype="random_number" start="1" end="100">
547   <var name="room"/>
548 </setdynvars>
549 <setdynvars sourcetype="random_string" length="10">
550   <var name="nick1"/>
551 </setdynvars>
552
553 <request subst="true">
554   <jabber type='muc:join' ack="local" room="room%%_room%%" nick="%%_nick1%%"/>
555 </request>
556
557 <!-- use a for loop to send several messages to the room -->
558 <for from="1" to="6" var="i">
559  <thinktime value="30"/>
560  <request subst="true">
561    <jabber type="muc:chat" ack="no_ack" size="16" room="room%%_room%%"/>
562  </request>
563 </for>
564
565 <!-- change nickname-->
566 <thinktime value="2"/>
567 <setdynvars sourcetype="random_string" length="10">
568  <var name="nick2"/>
569 </setdynvars>
570
571 <request subst="true">
572  <jabber type="muc:nick" room="room%%_room%%" nick="%%_nick2%%"
573          ack="no_ack"/>
574 </request>
575
576
577MUC support is available since version **1.3.1**.
578
579PubSub
580""""""
581
582Experimental support for PubSub is available in version **1.3.1**
583
584You can read the following entry: https://support.process-one.net/browse/TSUN-115
585
586VHost
587"""""
588
589VHost support is available since version **1.3.2**
590
591Tsung is able to bench multiple vhost instances by choosing a
592vhost XMPP name from a list at connection time in the scenario.
593
594The vhost list is read from a file:
595
596.. code-block:: xml
597
598 <options>
599 ...
600 <option name="file_server" value="domains.csv" id="vhostfileId"></option>
601 ...
602 <option type="ts_jabber" name="vhost_file" value="vhostfileId"></option>
603 ...
604 </options>
605
606
607When each client starts a session, it chooses randomly a domain (each domain has the
608same probability).
609
610.. _sec-read-user-jabber-csv-label:
611
612Reading usernames and password from a CSV file
613""""""""""""""""""""""""""""""""""""""""""""""
614
615Since version 1.4.0, you can now use a CSV file to store the usernames
616and password.
617
618Configure the CSV file:
619
620.. code-block:: xml
621
622 <options>
623  <option name="file_server" id='userdb' value="/home/foo/.tsung/users.csv"/>
624 </options>
625
626
627And then you have to defined two variables of type ``file``,
628and the first jabber request (``connect``) must include a
629``xmpp_authenticate`` tag:
630
631.. code-block:: xml
632
633 <session probability="100" name="jabber-example" type="ts_jabber">
634
635  <setdynvars sourcetype="file" fileid="userdb" delimiter=";" order="iter">
636    <var name="username" />
637    <var name="password" />
638  </setdynvars>
639
640    <request subst='true'>
641      <jabber type="connect" ack="no_ack">
642         <xmpp_authenticate username="%%_username%%" passwd="%%_password%%"/>
643      </jabber>
644    </request>
645
646   <thinktime value="2"></thinktime>
647
648   <transaction name="authenticate">
649
650   <request>
651     <jabber type="auth_get" ack="local"> </jabber>
652   </request>
653   <request>
654     <jabber type="auth_set_plain" ack="local"></jabber>
655   </request>
656
657  </transaction>
658  ...
659 </session>
660
661
662Moreover (since **1.5.0**), when using chat messages to random or offline users, you
663should disable the default users (not from CSV) by setting
664``userid_max`` to ``0`` and by setting the fileid for
665offline and random users (also used for pubsub):
666
667.. code-block:: xml
668
669 <options>
670  <option type="ts_jabber" name="userid_max" value="0" />
671  <option type="ts_jabber" name="random_from_fileid" value='userdb'/>
672  <option type="ts_jabber" name="offline_from_fileid" value='userdb'/>
673  <option type="ts_jabber" name="fileid_delimiter" value=";"/>
674 </options>
675
676
677The username (resp. passwd) should be the first (resp. second) entry in the each CSV line (the
678delimiter is by default ``";"`` and can be overriden).
679
680raw XML
681"""""""
682
683You can send raw XML data to the server using the ``raw`` type:
684
685.. code-block:: xml
686
687 <jabber type="raw" ack="no_ack" data="&lt;stream&gt;foo&lt;/stream&gt;"></jabber>
688
689
690Beware: you must encode XML characters like ``<`` , ``>``, ``&``, etc.
691
692resource
693""""""""
694
695By default, the XMPP resource is set to ``tsung``. Since
696version 1.5.0, you can override this (in all ``auth_*`` and
697``register`` requests) using the ``resource`` attribute.
698
699PostgreSQL
700^^^^^^^^^^
701
702For PostgreSQL, 4 types of requests are available:
703
704* connect (to a given database with a given username)
705* authenticate (with password or not)
706* sql (basic protocol)
707* close
708
709
710In addition, the following parts of the extended protocol is supported:
711
712* copy, copydone and copyfail
713* parse, bind, execute, describe
714* sync, flush
715
716
717This example shows most of the features of a PostgreSQL session:
718
719.. code-block:: xml
720
721  <session probability="100" name="pgsql-example" type="ts_pgsql">
722    <transaction name="connection">
723      <request>
724        <pgsql type="connect" database="bench" username="bench" />
725      </request>
726    </transaction>
727
728    <request><pgsql type="authenticate" password="sesame"/></request>
729
730    <thinktime value="12"/>
731
732    <request><pgsql type="sql">SELECT * from accounts;</pgsql></request>
733
734    <thinktime value="20"/>
735
736    <request><pgsql type="sql">SELECT * from users;</pgsql></request>
737
738    <request><pgsql type='sql'><![CDATA[SELECT n.nspname as "Schema",
739  c.relname as "Name",
740  CASE c.relkind WHEN 'r' THEN 'table' WHEN 'v' THEN 'view' WHEN 'i'
741  THEN 'index' WHEN 'S' THEN 'sequence' WHEN 's' THEN '%_toto_% END as "Type",
742  u.usename as "Owner"
743 FROM pg_catalog.pg_class c
744     LEFT JOIN pg_catalog.pg_user u ON u.usesysid = c.relowner
745     LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
746 WHERE c.relkind IN ('r','v','S','')
747      AND n.nspname NOT IN ('pg_catalog', 'pg_toast')
748      AND pg_catalog.pg_table_is_visible(c.oid)
749 ORDER BY 1,2;]]></pgsql></request>
750
751    <request><pgsql type="close"></pgsql></request>
752
753  </session>
754
755
756Example with the extended protocol:
757
758.. code-block:: xml
759
760 <request><pgsql type='parse' name_prepared='P0_7'><![CDATA[BEGIN;]]></pgsql></request>
761 <request><pgsql type='sync'/></request>
762 <request><pgsql type='parse' name_prepared='P0_8'><![CDATA[UPDATE pgbench_accounts
763  SET abalance = abalance + $1 WHERE aid = $2;]]></pgsql></request>
764 <request><pgsql type='sync'/></request>
765 <request><pgsql type='parse' name_prepared='P0_9'><![CDATA[SELECT
766  abalance FROM pgbench_accounts
767  WHERE aid = $1;]]></pgsql></request>
768 <request><pgsql type='sync'/></request>
769 <request><pgsql type='parse' name_prepared='P0_10'><![CDATA[UPDATE pgbench_tellers
770   SET tbalance = tbalance + $1 WHERE tid = $2;]]></pgsql></request>
771 <request><pgsql type='sync'/></request>
772 <request><pgsql type='parse' name_prepared='P0_11'><![CDATA[UPDATE pgbench_branches
773   SET bbalance = bbalance + $1 WHERE bid = $2;]]></pgsql></request>
774 <request><pgsql type='sync'/></request>
775 <request><pgsql type='parse' name_prepared='P0_12'><![CDATA[INSERT
776   INTO pgbench_history (tid, bid, aid, delta, mtime)
777   VALUES ($1, $2, $3, $4, CURRENT_TIMESTAMP);]]></pgsql></request>
778 <request><pgsql type='sync'/></request>
779 <request><pgsql type='parse' name_prepared='P0_13'><![CDATA[END;]]></pgsql></request>
780 <request><pgsql type='sync'/></request>
781 <request><pgsql type='bind' name_prepared='P0_7' formats='none' formats_results='text' /></request>
782 <request><pgsql type='describe' name_portal=''/></request>
783 <request><pgsql type='execute'/></request>
784 <request><pgsql type='sync'/></request>
785 <request><pgsql type='bind' name_portal='' name_prepared='P0_8'
786   formats='none' formats_results='text'
787   parameters='2924,37801'/></request>
788
789
790.. _session-mysql-label:
791
792MySQL
793^^^^^
794
795
796For MySQL, 4 types of requests are available (same as PostgreSQL):
797
798* connect (to a given database with a given username)
799* authenticate (with password or not)
800* sql
801* close
802
803
804This example shows most of the features of a MySQL session:
805
806.. code-block:: xml
807
808 <session probability="100" name="mysql-example" type="ts_mysql">
809 <request>
810  <mysql type="connect" />
811 </request>
812 <request>
813  <mysql type="authenticate" database="test" username="test" password="test" />
814 </request>
815 <request>
816  <mysql type="sql">SHOW TABLES</mysql>
817 </request>
818 <request>
819  <mysql type="sql">SELECT * FROM mytable</mysql>
820 </request>
821 <request>
822  <mysql type="close" />
823 </request>
824 </session>
825
826
827Websocket
828^^^^^^^^^
829.. _sec-session-websocket-label:
830
831For Websocket, 3 types of requests are available:
832
833* connect (to a given path)
834* message (send message to server, add a attribute 'ack' to specify whether
835  Tsung should wait for a response)
836* close
837
838
839Example with Websocket as a session type:
840
841.. code-block:: xml
842
843 <session probability="100" name="websocket-example" type="ts_websocket">
844   <request subst="true">
845     <websocket type="connect" path="/path/to/ws"></websocket>
846   </request>
847   <request>
848     <dyn_variable name="uid" jsonpath="uid"/>
849     <!-- send data with text frame, default binary-->
850     <websocket type="message" frame="text">{"user":"user", "password":"password"}</websocket>
851   </request>
852
853   <request subst="true">
854     <match do="log" when="nomatch">ok</match>
855     <websocket type="message">{"uid":"%%_uid%%", "data":"data"}</websocket>
856   </request>
857
858   <request>
859     <websocket type="message" ack="no_ack">{"key":"value"}</websocket>
860   </request>
861
862   <request>
863     <websocket type="close"></websocket>
864   </request>
865 </session>
866
867You can do substitution on attribute 'path' and message content, match a
868response or define dynamic variables based on the response message.
869
870You can also set the subprotocols in a connect message:
871
872.. code-block:: xml
873
874  <websocket type="connect" path="/path/to/ws" subprotocols="chat"></websocket>
875
876If you use ``change_type`` to start a websocket, don't forget to set
877``bidi="true"``, like this:
878
879.. code-block:: xml
880
881 <change_type new_type="ts_websocket" host="127.0.0.1" port="8080" server_type="tcp" restore="true" store="true" bidi="true"/>i
882
883When connecting to a websocket server you can set the ``origin``, which can be
884checked by a websocket as a security measure, see
885https://tools.ietf.org/html/rfc6455#section-10.2 for more details.
886If not set this defaults to the ``host`` value.
887
888.. code-block:: xml
889
890  <websocket type="connect" origin="https://example.com"></websocket>
891
892AMQP
893^^^^^^^^^
894.. _sec-session-amqp-label:
895
896For AMQP, it supports publish and consume messages on multiple channel,
897Available request types:
898
899* connection.open (to a given vhost)
900* connection.close
901* channel.open (with specified and valid channel id)
902* channel.close (with specified and valid channel id)
903* confirm.select (on specified and already opened channel)
904* basic.qos (on specified and already opened channel, only supports
905  attribute 'prefetch_count')
906* basic.publish (with channel id, exchange name, routing_key and the payload
907* basic.consume (with channel id, queue name)
908* waitForConfirms (with timeout for confirmations from the server)
909* waitForMessages (with timeout for messages delivered to the client)
910
911Example with AMQP as a session type:
912
913.. code-block:: xml
914
915    <session probability="100" name="amqp-example" type="ts_amqp" bidi="true">
916        <request>
917            <amqp type="connection.open" vhost="/"></amqp>
918        </request>
919
920        <!--  open channel, channel id is from 1 to 10 -->
921        <for from="1" to="10" incr="1" var="loops">
922            <request>
923                <amqp type="channel.open"></amqp>
924            </request>
925        </for>
926
927        <!-- ignore this request if you don't need publisher confirm -->
928        <for from="1" to="10" incr="1" var="loops">
929            <request subst="true">
930                <amqp type="confirm.select" channel="%%_loops%%"></amqp>
931            </request>
932        </for>
933
934        <for from="1" to="10" incr="1" var="loops">
935            <for from="1" to="100" incr="1" var="counter">
936                <transaction name="publish">
937                    <!-- specify payload_size to have tsung generate a payload for you -->
938                    <request subst="true">
939                        <amqp type="basic.publish" channel="%%_loops%%" exchange="test_exchange"
940                        routing_key="test_queue" persistent="true" payload_size="100"></amqp>
941                    </request>
942            <!-- substitutions are supported on the payload. Payload will override payload_size. -->
943                    <request subst="true">
944                        <amqp type="basic.publish" channel="%%_loops%%" exchange="test_exchange"
945                        routing_key="test_queue" persistent="true" payload="Test Payload"></amqp>
946                    </request>
947                </transaction>
948            </for>
949
950            <!-- if publish with confirm, add a waitForConfirms request as you need: timeout=1s -->
951            <request>
952                <amqp type="waitForConfirms" timeout="1"></amqp>
953            </request>
954        </for>
955
956        <for from="1" to="10" incr="1" var="loops">
957            <request subst="true">
958                <amqp type="basic.consume" channel="%%_loops%%" queue="test_queue" ack="true"></amqp>
959            </request>
960        </for>
961
962        <!-- wait to receive messages from the server: timeout=180s -->
963        <request>
964            <amqp type="waitForMessages" timeout="180"></amqp>
965        </request>
966
967        <for from="1" to="10" incr="1" var="loops">
968            <request subst="true">
969                <amqp type="channel.close" channel="%%_loops%%"></amqp>
970            </request>
971        </for>
972
973        <request>
974            <amqp type="connection.close"></amqp>
975        </request>
976    </session>
977
978MQTT
979^^^^^^^^^
980.. _sec-session-mqtt-label:
981
982It supports publish messages, subscribe and unsubscribe topics,
983Available request types:
984
985* connect (with options like clean_start, will_topic, username, password, etc.)
986* disconnect
987* publish (with topic name, qos level and retain flag)
988* subscribe (with topic name and qos level)
989* unsubscribe (with topic name)
990* waitForMessages (with timeout for messages published from server to the
991  client)
992
993Example with MQTT as a session type:
994
995.. code-block:: xml
996
997    <session name="mqtt-example" probability="100" type="ts_mqtt">
998        <request>
999            <mqtt type="connect" clean_start="true" keepalive="10" will_topic="will_topic" will_qos="0" will_msg="will_msg" will_retain="false"></mqtt>
1000        </request>
1001
1002        <for from="1" to="10" incr="1" var="loops">
1003            <request subst="true">
1004                <mqtt type="publish" topic="test_topic" qos="1" retained="true">test_message</mqtt>
1005            </request>
1006        </for>
1007
1008        <request subst="true">
1009            <mqtt type="subscribe" topic="test_topic" qos="1"></mqtt>
1010        </request>
1011
1012        <request>
1013            <!-- wait for 60s -->
1014            <mqtt type="waitForMessages" timeout="60"></mqtt>
1015        </request>
1016
1017        <request subst="true">
1018            <mqtt type="unsubscribe" topic="test_topic"></mqtt>
1019        </request>
1020
1021        <request>
1022            <mqtt type="disconnect"></mqtt>
1023        </request>
1024    </session>
1025
1026LDAP
1027^^^^
1028
1029.. _sec-session-ldap-label:
1030
1031Authentication
1032""""""""""""""
1033
1034The recommended mechanism used to authenticate users against a LDAP
1035repository requires two steps to follow. Given an username and
1036password, we:
1037
1038* Search the user in the repository tree, using the username (so users can reside in different subtrees of the organization)
1039* Try to bind as the user, with the distinguished name found in the first step and the user's password
1040
1041If the bind is successful, the user is authenticated (this is the
1042scheme used, among others, by the LDAP authentication module for
1043Apache http://httpd.apache.org/docs/2.0/mod/mod_auth_ldap.html)
1044
1045LDAP Setup
1046""""""""""
1047
1048For this example we are going to use a simple repository with the following hierarchy:
1049
1050.. figure:: ./images/ldap-hierarchy.png
1051
1052  LDAP hierarchy
1053
1054The repository has users in two organizational units
1055
1056#. users (with four members)
1057#. users2 (with tree members)
1058
1059
1060For simplicity we set the password of each user to be  the same as its common name (cn).
1061Tsung Setup
1062We will use a CSV file as input, containing the user:password pairs
1063for our test. So we start by writing it, in this case we name the file ``users.csv``::
1064
1065  user1;user1
1066  user2;user2
1067  user3;user3
1068  user4;user4
1069  jane;jane
1070  mary;mary
1071  paul;pablo
1072  paul;paul
1073
1074The pair ``paul:pablo`` should fail to authenticate, we will note that in the Tsung report.
1075Then, in our Tsung scenario, we let Tsung know about this file:
1076
1077.. code-block:: xml
1078
1079   <options>
1080     <option name="file_server" id="users" value="users.csv"/>
1081   </options>
1082   <!-- We use two dynamic variables to hold the username and password -->
1083   <setdynvars sourcetype="file" fileid="users" delimiter=";" order="iter">
1084		<var name="username" />
1085		<var name="password" />
1086   </setdynvars>
1087
1088
1089To start the authentication process we instruct Tsung to perform a search, to find the distinguished name of the user we are trying to authenticate
1090
1091.. code-block:: xml
1092
1093  <ldap type="search" base="dc=pablo-desktop" filter="(cn=%%_username%%)"
1094        result_var="search_result" scope="wholeSubtree"></ldap>
1095
1096
1097As we need to access the search result, we specify it using the ``result_var`` attribute. This attribute tells Tsung in which dynamic variable we want to store the result (if the ``result_var`` attribute isn't set, Tsung doesn't store the search result in any place).
1098Finally,  we try to bind as that user.
1099
1100.. code-block:: xml
1101
1102 <request subst="true">
1103   <ldap type="bind" user="%%ldap_auth:user_dn%%"
1104         password="%%_password%%"></ldap>
1105 </request>
1106
1107
1108The only thing that remains to do is to implement the ``ldap_auth:user_dn`` function, that extract the distinguished name from the search result.
1109
1110.. code-block:: erlang
1111
1112 -module(ldap_auth).
1113 -export([user_dn/1]).
1114 user_dn({_Pid,DynVars}) ->
1115      [SearchResultEntry] = proplists:get_value(search_result,DynVars),
1116      {_,DN,_} = SearchResultEntry,
1117      DN.
1118
1119
1120We aren't covering errors here. supposing that there is always one (and only one) user found, that we extract from the ``search_result`` variable (as defined in the previous search operation).
1121Each entry in the result set is a SearchResultEntry record. The record definition can be found in ``<TSUNG_DIR>/include/ELDAPv3.hrl``.
1122
1123As we only need to access the distinguished name of the object, we index into the result tuple directly. But if you need to access other attributes you probably will want to include the appropriate .hrl and use the record syntax instead. One of the eight user:password pairs in our users file was wrong, so we expect 1/8 of the authentication attempts to fail.
1124
1125Indeed, after running the scenario we can confirm this in the Tsung
1126report (see figure :ref:`fig-ldap-results-label`). The bind operation maintains two
1127counters: ``ldap_bind_ok`` and ``ldap_bind_error``,
1128that counts successful and unsuccessful bind attempts.
1129
1130.. _fig-ldap-results-label:
1131.. figure:: ./images/ldap-results.png
1132
1133  LDAP Results
1134
1135Other examples
1136""""""""""""""
1137
1138.. code-block:: xml
1139
1140 <session probability="100" name="ldap-example" type="ts_ldap">
1141   <request>
1142     <ldap type="bind" user="uid=foo" password="bar"/>
1143   </request>
1144
1145   <request>
1146     <ldap type="search" base="dc=pablo-desktop" filter="(cn=user2)"
1147     scope="wholeSubtree"></ldap>
1148   </request>
1149
1150   <!-- Add. Adds a new entry to the directory* -->
1151   <request subst="true">
1152     <ldap type="add" dn="%%_new_user_dn%%" >
1153       <attr type="objectClass">
1154         <value>organizationalPerson</value>
1155         <value>inetOrgPerson</value>
1156         <value>person</value>
1157       </attr>
1158       <attr type="cn"><value>%%_new_user_cn%%</value></attr>
1159       <attr type="sn"><value>fffs</value></attr>
1160     </ldap>
1161   </request>
1162
1163   <!-- Modify. Modifies an existing entry; type=add|delete|modify-->
1164   <request subst="false">
1165     <ldap type="modify" dn="cn=u119843,dc=pablo-desktop" >
1166       <modification type="replace">
1167         <attr type="sn"><value>SomeSN</value></attr>
1168         <attr type="mail"><value>some@mail.com</value></attr>
1169       </modification>
1170     </ldap>
1171   </request>
1172 </session>
1173
1174.. index:: change_type
1175
1176Mixing session type
1177^^^^^^^^^^^^^^^^^^^
1178
1179Since version **1.3.2**, a new tag **change_type** can be
1180used in a session to change it's type.
1181
1182
1183.. code-block:: xml
1184
1185 <request>
1186   <jabber type="chat" ack="no_ack" size="16"
1187           destination="offline"/>
1188 </request>
1189
1190 <thinktime value="3"/>
1191
1192 <change_type new_type="ts_http" host="foo.bar" port="80"
1193 server_type="tcp" store="true"/>
1194
1195 <request> <http url="http://foo.bar/"/> </request>
1196 <request> <http url="/favicon"/> </request>
1197
1198 <change_type new_type="ts_jabber" host="localhost" port="5222"
1199 server_type="tcp" restore="true"/>
1200
1201 <request> <jabber type="chat" ack="no_ack" size="16"
1202 destination="previous"/> </request>
1203
1204
1205``store="true"`` can be used to save the current state of the session (socket,
1206cookies for http, …) and ``restore="true"`` to reuse the previous state when
1207you switch back to the old protocol.
1208
1209You can use ``bidi="true"`` to indicate that the new protocol is bidirectional or
1210``bidi="false"`` for a non-bidirectional protocol (only available in version
1211**1.5.1** and newer).
1212
1213A dynamic variable set in the first part of the session will be
1214available after a **<change_type>**. There is currently one caveat: you have
1215to use a full URL in the first http request after a **<change_type>** (a
1216relative URL will fail).
1217
1218
1219Raw
1220^^^^^^^^^
1221.. _sec-session-raw-label:
1222
1223The **ts_raw** plugin allows you to send traffic to any kind of
1224TCP/UDP server without any knowledge of the underlying protocol. You can set the data
1225by attribute ``data``, or just set a data size by attribute
1226``datasize`` (in this situation, Tsung send ``datasize`` bits of
1227zeros). ``data`` and ``datasize`` can be a dynamic values.
1228
1229The only way to control the response from the server is to use the
1230``ack`` attribute (also used by the **jabber** plugin):
1231
1232* ``ack="local"`` as soon as a packet is received from the server, the
1233  request is considered as completed. Hence if you use a local ack with a request
1234  that does not require a response from the server, it
1235  will wait forever (or until a timeout is reached).
1236
1237* ``ack="no_ack"`` as soon as the request is sent, it is considered as completed (do
1238  not wait for incoming data).
1239
1240* ``ack="global"`` synchronized users. its main use is for waiting for all
1241  users to connect before sending messages. To do that, set a request
1242  with global ack  (the value can be set using the option ``<option
1243  name="global_number" value ="XXX"/>`` and by setting `maxnumber=N` in
1244  ``<arrivalphase>``).
1245
1246.. code-block:: xml
1247
1248 <session probability="100" name="raw" type="ts_raw">
1249    <transaction name="open">
1250      <request> <raw data="HELLO" ack="local"></raw> </request>
1251    </transaction>
1252
1253    <thinktime value="4"/>
1254    <request> <raw datasize="2048" ack="local"></raw> </request>
1255
1256    <transaction name="bye">
1257      <request> <raw data="BYEBYE" ack="local"></raw> </request>
1258    </transaction>
1259 </session>
1260