VuGen correlation for SAP Web Dynpro
If you are trying to create a LoadRunner script for a SAP Web Dynpro application, and you are having problems correlating the SAPEVENTQUEUE in your POST request, then this Tech Tip is for you…
Here is what a typical request might look like:
1 2 3 4 5 6 7 8 9 10 11 12 13 | web_submit_data("sap-ext-sid_2", "Action=http://www.example.com:8000/sap/bc/webdynpro/SAP/ERC_A_WORKCENTER/;sap-ext-sid={SapExtSid2_120}", "Method=POST", "TargetFrame=", "RecContentType=text/html", "Referer=http://www.example.com:8000/sap/bc/webdynpro/SAP/ERC_A_WORKCENTER/;sap-ext-sid={SapExtSid2_98}", "Snapshot=t18.inf", "Mode=HTML", ITEMDATA, "Name=SAPEVENTQUEUE", "Value=Custom_ClientInfos~E002Id~E004WD01~E005WindowOpenerExists~E004false~E005ClientURL~E004http~003A~002F~002Fwww.example.com~003A8000~002Fsap~002Fbc~002Fwebdynpro~002FSAP~002FERC_A_WORKCENTER~002F~003Bsap-ext-sid~003DzuUt57Mx_3JozG7pOff~002AEg--U_0j6OHCaCQurUN1Pimp1Q--~E003~E002ClientAction~E004enqueue~E005ResponseData~E004delta~E003~E002~E003~E001TimeTrigger_Trigger~E002Id~E004WDE4~E003~E002ResponseData~E004delta~E005ClientAction~E004submit~E003~E002~E003", ENDITEM, "Name=sap-charset", "Value=utf-8", ENDITEM, "Name=_client_url_", "Value=", ENDITEM, LAST); |
Obviously the sap-ext-sid has already been correlated (this is easy to do with a Correlation Rule), but the SAPEVENTQUEUE also needs to be correlated. This is difficult, as it is constructed dynamically using JavaScript, so the value does not appear directly in any HTML response, and therefore cannot be correlated using a simple web_reg_save_param.
Examining the SAPEVENTQUEUE string, there are two repeated patterns; a series of 5 characters like “~E005″, and a series of 5 characters like “~003A” (without the “E”). Taking an educated guess, we can see that the string…
1 | http~003A~002F~002Fwww.example.com~003A8000~002Fsap~002Fbc~002Fwebdynpro~002FSAP~002FERC_A_WORKCENTER~002F~003Bsap-ext-sid~003DzuUt57Mx_3J |
…is an encoding of…
1 | http://www.example.com:8000/sap/bc/webdynpro/SAP/ERC_A_WORKCENTER/;sap-ext-sid=zuUt57Mx_3J |
…which means that…
- ~003A is :
- ~002F is /
- ~002F is /
- ~003D is =
- ~003B is ;
So it looks like SAP has invented their own way of URL Encoding values to be POSTed to a Web Dynpro server.
But what about the encoded values with an “E” at the start? Searching through the source code, we find that these are special “event separators”…
- ~E001 is EVENT
- ~E002 is SECTION_BEGIN
- ~E003 is SECTION_END
- ~E004 is KEYVALUE
- ~E005 is KEYVALUE_PAIR
- ~E006 is COLLECTION_ENTRY
As I am unlikely to want to change the separators, here is a simple function that will encode a string using SAP’s special version of URL encoding.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 | // This function replaces unreserved characters in a string with their encoded values. // Encoding is in the style of SAP Web Dynpro. E.g. "abd*def" becomes "abc~002Adef". // Reserved/unreserved characters are according to RFC3986 (http://tools.ietf.org/html/rfc3986) // This function returns a pointer to the start of the encoded string (buf). // Note that buf must be big enough to hold original string plus all converted entities. char* dynpro_encode(char* plain_string, char* buf) { int len = strlen(plain_string); int i,j; char hex_value[3]; if (plain_string == NULL) { lr_error_message("Input string is empty."); return NULL; } for (i=0, j=0; i<len; i++) { // Check if character is in list of allowed characters. // A B C D E F G H I J K L M N O P Q R S T U V W X Y Z // a b c d e f g h i j k l m n o p q r s t u v w x y z // 0 1 2 3 4 5 6 7 8 9 - _ . ~ if ( (plain_string[i] >= 'A' && plain_string[i] <= 'Z') || (plain_string[i] >= 'a' && plain_string[i] <= 'z') || (plain_string[i] >= '0' && plain_string[i] <= '9') || (plain_string[i] == '-') || (plain_string[i] == '_') || (plain_string[i] == '.') || (plain_string[i] == '~') ) { buf[j++] = plain_string[i]; } else if ( (plain_string[i] < 32 ) || (plain_string[i] > 126) ) { lr_error_message("Input string contains non-printable or non-ASCII character %c at position: %d", plain_string[i], i); return NULL; } else { // The unicode value for use in url encoding is the same as the hex value for the ASCII character itoa(plain_string[i], hex_value, 16); buf[j++] = '~'; buf[j++] = '0'; buf[j++] = '0'; buf[j++] = toupper(hex_value[0]); buf[j++] = toupper(hex_value[1]); } } buf[j] = NULL; // terminate the string return buf; } |
And, just for completeness, here is a function that will decode a string.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 | char *strncpy ( char *dest, const char *source, size_t n ); // explicit declaration required // This function replaces encoded characters from with their non-encoded value. // Decoding is in the style of SAP Web Dynpro. E.g. "abc~002Adef" becomes "abd*def". // Reserved characters are according to RFC3986 (http://tools.ietf.org/html/rfc3986) // This function returns a pointer to the start of the decoded string (buf). // Note that buf must be big enough to hold the decoded string (always equal to or shorter than the encoded string). char* dynpro_decode(char* enc_string, char* buf) { int len = strlen(enc_string); int i, j; char code[3]; // holds url encoded value e.g. "2F" (/) int hex; // decimal value of hex code e.g. 47 (0x2F) int rc; // return code if (enc_string == NULL) { lr_error_message("Input string is empty."); return NULL; } for (i=0, j=0; i<len; i++, j++) { // Only convert entities that do not start with "~E". Do not run off the end of the string. if ( (enc_string[i] == '~') && (enc_string[i+1] != 'E') && ((i+4) < len) && (enc_string[i+1] == '0') && (enc_string[i+2] == '0') && (isalpha(enc_string[i+3]) || isdigit(enc_string[i+3])) && (isalpha(enc_string[i+4]) || isdigit(enc_string[i+4])) ) { // Get the hex value from the input string code[0] = enc_string[i+3]; code[1] = enc_string[i+4]; code[3] = NULL; // Convert the hex value to the appropriate character, and add it to buf rc = sscanf(code, "%2x", &hex); if (rc != 1) { lr_error_message("Invalid hex value: %s", code); } buf[j] = hex; i+=4; // skip the rest of this encoded value in the input string } else { buf[j] = enc_string[i]; } } return buf; } |
Please share any more tips you have for VuGen scripting of Web Dynpro in the comments section.
Related posts:
- Testing Web Services With a Standard Web Vuser It is possible to test web services using the standard...
- What’s New in LoadRunner 9.50? LoadRunner 9.5 was released today and, as mentioned by the...
- DNS-based load balancing for virtual users In DNS-based load balancing, a website visitor will request a...
- LoadRunner in Windows 7 Windows 7 has finally been released, and I’ve had the...
- Changing LoadRunner/VuGen log options at runtime LoadRunner has a whole bunch of logging options. These can...
You can follow any responses to this entry through the RSS 2.0 feed. You can leave a response, or trackback from your own site.
April 28th, 2009 at 12:40 pm
Web Dynpro is a big pile of crap. Why does SAP always build applications “their own way” i.e. in a non-standard way?
Have you tried automating a Dynpro application using QuickTest Pro? It’s fricken painful. Here’s an example from the HP Support Site:
Web dynpro | 06/18/06 22:49:09
Problem 1:
When recording a combo box / drop down list, QTP records it as a button and then sets the info into an edit field Will not see it as a combo box and will not run on replay. It will not select and attempts to write into an uneditable field.
Problem 2:
When recording a CheckBox, QTP records it as a
What is Web Dynpro application ABAP Web Dynpro communicates to SAP ABAP code directly, and displays it at the presentation layer in a web browser When this opens you get: SAP NetWeaver Web Dynpro ABAP Final Release 7.00 SP 0006 Starting Application…….
Source code does show the objects as ComboBox and CheckBox The HTML ID shows correctly in QTP as found in the source code eg ComboBox WD84 displays in QTP as WebEdit WD84 QTP is using Web objects and not SAP objects. Anyone keen to help with this, I can supply screen shots and source code files.
I have even seen a Dynpro application where the html id (the only unique object property apart from the x/y coordinates) changes every time a new build is deployed (every day). This totally sucks!
October 6th, 2009 at 3:03 pm
Good Morning ,
i have question to ask you about “Web Dynpro”
I have problem in LR “Load Runner”
When I do record I don’t see the pages !! I don’t know why ?!
I’m used “Web Dynpro” if you have any solution tell me I need that to important
Thank you
Regards,
Talib AL-Hamood
October 6th, 2009 at 3:28 pm
Talib, LoadRunner has problems rendering some web pages in the Tree View.
If your application does not successfully display a recording snapshot in the Tree View, then you will just have to get your script working using the Script view (without snapshots).
Cheers,
Stuart.
December 15th, 2009 at 3:16 am
For a non-programmer, I am pretty skilled at recording and testing SAP-BSP applications, but am now working on my first recording of an SAP Abap Web Dynpro application. Immediately after login the replay produces an HTTP 500 error, and fails to execute. The very next statement has the SAPEVENTQUEUE, so I suspect your information here is relevant to my problem.
I have not yet even reached the point to possibly reach the combo box or check box issues!
Can you please elaborate on where the above coding is to be inserted? As mentioned, I am not a programmer, and blindly tried adding at the beginning of the script. I encountered several syntax errors. Anything you can offer to help me is very appreciated! I have a very short timeline!
Thanks,
Pat Gallaher