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&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&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=([^&]*)"/> 213 <dyn_variable name="access_token_secret" re="oauth_token_secret=([^&]*)" /> 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=([^&]*)"/> 223 <dyn_variable name="access_token_secret" re="oauth_token_secret=([^&]*)"/> 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="<stream>foo</stream>"></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