Y-lib
Loadrunner libraries
y_browseremulation.c
Go to the documentation of this file.
1 /*
2  * Ylib Loadrunner function library.
3  * Copyright (C) 2012-2014 Floris Kraak <randakar@gmail.com> | <fkraak@ymor.nl>
4  *
5  * This program is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU General Public License
7  * as published by the Free Software Foundation; either version 2
8  * of the License, or (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18  */
19 
68 #ifndef _Y_BROWSER_EMULATION_C_
69 #define _Y_BROWSER_EMULATION_C_
71 
73 #include "y_core.c"
74 #include "y_string.c"
75 
76 
81 {
83  char* name;
85  int chance;
87  void* next;
88 
95 };
96 
100 typedef struct y_struct_browser y_browser;
101 
102 
104 #define MAX_BROWSER_LIST_LENGTH 1000
105 
107 y_browser* y_browser_list_head = NULL;
108 
111 
122 void y_log_browser(const y_browser* browser)
123 {
124  if(browser == NULL)
125  {
126  lr_error_message("y_browser_emulation.c: Attempt to log content of NULL browser. Ignoring.");
127  return;
128  }
129 
130  lr_log_message("y_browseremulation.c: browser: %s, chance %d, max_conn_per_host %d, max_conn %d, user agent string: %s",
131  browser->name,
132  browser->chance,
133  browser->max_connections_per_host,
134  browser->max_connections,
135  browser->user_agent_string);
136 }
137 
154 void y_save_browser_to_parameters(const y_browser* browser)
155 {
156  if(browser == NULL)
157  {
158  lr_error_message("y_browser_emulation.c: Attempt to store the content of NULL browser into parameters. Aborting.");
159  lr_abort();
160  return;
161  }
162 
163  lr_save_string(browser->name, "browser_name");
164  lr_save_int(browser->chance, "browser_chance");
165  lr_save_int(browser->max_connections_per_host, "browser_max_connections_per_host");
166  lr_save_int(browser->max_connections, "browser_max_connections");
167  lr_save_string(browser->user_agent_string, "browser_user_agent_string");
168 }
169 
170 
210 int y_setup_browser_emulation_from_parameters(const char* browser_name_param,
211  const char* browser_chance_param,
212  const char* browser_max_connections_per_host_param,
213  const char* browser_max_connections_param,
214  const char* browser_user_agent_string_param)
215 {
216  int i;
217  y_browser* previous_browser = NULL;
218 
219  for(i=0; i < MAX_BROWSER_LIST_LENGTH; i++ )
220  {
221  y_browser* browser;
222  char* browser_name = y_get_parameter_with_malloc_or_null(browser_name_param);
223 
224  if( browser_name == NULL )
225  {
226  lr_error_message("Browser name parameter %s does not exist. Aborting browser emulation setup.", browser_name_param);
227  return -1;
228  }
229  if(strcmp(browser_name, "END") == 0)
230  {
231  free(browser_name);
232  lr_log_message("y_browseremulation.c: End of browser list initialisation");
233  return 0;
234  }
235 
236  // Allocate a browser object
237  browser = (y_browser*) y_mem_alloc( sizeof browser[0] );
238  browser->name = browser_name;
239  browser->next = NULL;
240 
241  // The next bit is a bit verbose due to all of the checks on missing parameters,
242  // but really we're just filling out the remaining fields of the browser struct.
243  {
244  int error = 0;
245  char* browser_chance = y_get_parameter_or_null(browser_chance_param);
246  char* browser_max_connections_per_host = y_get_parameter_or_null(browser_max_connections_per_host_param);
247  char* browser_max_connections = y_get_parameter_or_null(browser_max_connections_param);
248 
249  if( browser_chance == NULL || browser_max_connections_per_host == NULL || browser_max_connections == NULL )
250  {
251  lr_error_message("Browser parameter missing. Aborting browser emulation setup. chance: %s:%s, max_connections_per_host %s:%s, max_connections %s:%s",
252  browser_chance_param, browser_chance,
253  browser_max_connections_per_host_param, browser_max_connections_per_host,
254  browser_max_connections_param, browser_max_connections);
255  error = 1;
256  }
257  else if( (browser->user_agent_string = y_get_parameter_with_malloc_or_null(browser_user_agent_string_param)) == NULL )
258  {
259  lr_error_message("Browser user agent parameter %s does not exist. Aborting browser emulation setup.", browser_user_agent_string_param);
260  error = 1;
261  }
262 
263  if( error )
264  {
265  free(browser_name);
266  free(browser);
267  return -1;
268  }
269  browser->chance = atoi(browser_chance);
270  browser->max_connections_per_host = atoi(browser_max_connections_per_host);
271  browser->max_connections = atoi(browser_max_connections);
272  }
273 
274  //lr_log_message("y_browseremulation.c: Adding browser");
275  //y_log_browser(browser);
276 
277  // Increment the global count of weights.
278  y_browser_list_chance_total += browser->chance;
279  //lr_log_message("y_browseremulation.c: Adding weight: %d", y_browser_list_chance_total);
280 
281  // Add it to the list.
282  if( y_browser_list_head == NULL )
283  {
284  y_browser_list_head = browser;
285  }
286  else
287  {
288  previous_browser->next = browser;
289  }
290  previous_browser = browser; // Replaces the previous tail element with the new one.
291 
292  // Get the next value for the new iteration.
293  // This parameter should be set to "update each iteration", or this code will play havoc with it ..
294  lr_advance_param((char* )browser_name_param);
295  }
296 
297  if( i == (MAX_BROWSER_LIST_LENGTH) )
298  {
299  lr_log_message("Too many browsers to fit in browser list struct, max list size = %d", MAX_BROWSER_LIST_LENGTH);
300  lr_abort();
301  return -1;
302  }
303  return 0; // No error.
304 }
305 
329 {
330  return y_setup_browser_emulation_from_parameters("browser_name", "browser_chance", "browser_max_connections_per_host", "browser_max_connections", "browser_user_agent_string");
331 }
332 
372 {
373  y_browser* previous_browser = NULL;
374  long fp = fopen(filename, "r");
375  char line[4096];
376 
377  if (fp == NULL)
378  {
379  lr_error_message("Unable to open file %s", filename);
380  lr_abort();
381  return -1;
382  }
383  lr_log_message("Opened file %s", filename);
384 
385  // Overflow protection
386  line[0] = '\0';
387  line[4095] = '\0';
388 
389  // Now read the file line by line.
390  while(fgets(line, sizeof line, fp))
391  {
392  char* remove;
393  lr_log_message("Read line: %s", line);
394 
395  // Remove comments and trailing newlines.
396  while( ((remove = strchr(line, '#')) != NULL) ||
397  //((remove = strstr(line, "//")) != NULL) ||
398  ((remove = strchr(line, '\r')) != NULL) ||
399  ((remove = strchr(line, '\n')) != NULL) )
400  {
401  remove[0] = '\0';
402  //lr_log_message("Stripped line: %s", line);
403  }
404 
405 
406  { // start namespace for various temporary stack buffers
407 
408  char name[4096];
409  size_t user_agent_offset;
410 
411  y_browser* browser = (y_browser*) y_mem_alloc( sizeof browser[0] );
412  int scanresult = sscanf(line, "%4095s %*f%% %d %d %d %n",
413  name, &browser->chance, &browser->max_connections, &browser->max_connections_per_host, &user_agent_offset);
414 
415  // Debug code
416 // lr_log_message("Found %d matches", scanresult);
417 // switch(scanresult)
418 // {
419 // case 5:
420 // lr_log_message("Found at least one too many ..");
421 // case 4:
422 // lr_log_message("user_agent_offset: %d", user_agent_offset); // %n telt niet mee blijkbaar.
423 // lr_log_message("browser->max_connections_per_host: %d", browser->max_connections_per_host);
424 // case 3:
425 // lr_log_message("browser->max_connections: %d", browser->max_connections);
426 // case 2:
427 // lr_log_message("browser->chance: %d", browser->chance);
428 // case 1:
429 // lr_log_message("name: %s", name);
430 // }
431  if(scanresult < 4)
432  {
433  free(browser);
434  lr_log_message("Non-matching line.");
435  continue;
436  }
437 
438  // Copy the char* fields into their own allocated memory and store that.
439  browser->name = y_strdup(name);
440  browser->user_agent_string = y_strdup(line + user_agent_offset);
441 
442  // Report the result
443  //y_log_browser(browser);
444 
445  // Increment the global count of weights.
446  y_browser_list_chance_total += browser->chance;
447  lr_log_message("y_browseremulation.c: Adding weight: %d", y_browser_list_chance_total);
448 
449  // Add it to the list.
450  if( y_browser_list_head == NULL )
451  y_browser_list_head = browser;
452  else
453  previous_browser->next = browser;
454  previous_browser = browser; // Replaces the previous tail element with the new one.
455  } // end namespace
456  } // end while loop
457 
458  return 0;
459 }
460 
461 
484 y_browser* y_choose_browser_from_list(y_browser* browser_list_head, int browser_list_chance_total)
485 {
486  int i, lowerbound, cursor = 0;
487  y_browser* browser = NULL;
488  long roll;
489 
490  if( browser_list_chance_total < 1 )
491  {
492  lr_error_message("y_browseremulation.c: Browser list not initialised before call to y_choose_browser_from_list(). Cannot choose, ignoring.");
493  return NULL;
494  }
495 
496  // The upper bound of the rand() function is determined by the RAND_MAX constant.
497  // RAND_MAX is hardcoded in loadrunner to a value of exactly 32767.
498  // In fact, it doesn't even get #defined by loadrunner as as the C standard mandates.
499  // (y_core.c does that instead, now ..)
500  //
501  // y_rand() depends on rand() for it's output, so that cannot go above 32767.
502  // Update: y_rand() uses Y_RAND_MAX now - slightly over 1 billion.
503  // Unfortunately, the list with weights that we use has numbers bigger than that.
504  //
505  // So we'll need to get a bit creative, and multiply the result of y_rand() with
506  // the maximum (the total of the browser chances) divided by Y_RAND_MAX to scale things up again.
507  // (At the cost of precision ..)
508  //
509  // Update: y_rand() now returns a 31 bit number, giving it an upper bound of 2147483647 (Y_RAND_MAX).
510  // That should reduce the need for this code by a bit ..
511  //
512 
513  roll = y_rand() % browser_list_chance_total;
514  //lr_log_message("browser_list_chance_total = %d, RAND_MAX = %d, roll %d", browser_list_chance_total, RAND_MAX, roll);
515  if( Y_RAND_MAX < browser_list_chance_total)
516  {
517  roll = roll * ((browser_list_chance_total / RAND_MAX));
518  }
519  //lr_log_message("Roll: %d", roll);
520 
521  for( browser = browser_list_head; browser != NULL; browser = browser->next)
522  {
523  cursor += browser->chance;
524  // lr_log_message("Chance cursor: %d for browser: %s", cursor, browser->name);
525 
526  if(roll <= cursor)
527  {
528  //lr_log_message("Chosen browser:");
529  //y_log_browser(browser);
530  return browser;
531  }
532  }
533  // This code should be unreachable.
534  lr_error_message("y_browseremulation.c: Roll result out of bounds: roll: %d, cursor: %d, browser_list_chance_total %d", roll, cursor, browser_list_chance_total);
535  return browser;
536 }
537 
558 y_browser* y_choose_browser()
559 {
561 }
562 
583 int y_emulate_browser(y_browser* new_browser)
584 {
585  char str_max_connections[12];
586  char str_max_connections_per_host[12];
587  int max_connections;
588  static y_browser* previous_browser;
589  y_browser* browser;
590 
591  if( new_browser == NULL )
592  {
593  if( previous_browser == NULL )
594  {
595  lr_error_message("y_browser_emulation.c: Attempt to emulate the NULL browser: Ignored.");
596  return -1;
597  }
598  else
599  {
600  browser = previous_browser;
601  }
602  }
603  else
604  {
605  browser = new_browser;
606  previous_browser = browser;
607  }
608 
609  lr_log_message("Emulating browser:");
610  y_log_browser(browser);
611 
612  // Loadrunner doesn't accept values higher than 50 for this sockets option, so we'll just log it and set it to 50.
613  max_connections = browser->max_connections;
614  if( max_connections > 50 )
615  {
616  lr_log_message("y_browser_emulation.c: Loadrunner does not support using more than 50 browser connections. Using 50 connections instead of %d.", max_connections);
617  max_connections = 50;
618  }
619 
620  snprintf(str_max_connections, sizeof str_max_connections, "%d", max_connections);
621  snprintf(str_max_connections_per_host, sizeof str_max_connections_per_host, "%d", browser->max_connections_per_host);
622 
623  // Now finally set the correct settings for the chosen browser:
624  web_set_sockets_option("MAX_CONNECTIONS_PER_HOST", str_max_connections_per_host);
625  web_set_sockets_option("MAX_TOTAL_CONNECTIONS", str_max_connections);
626  web_add_auto_header("User-Agent", browser->user_agent_string);
627  return 0;
628 }
629 
630 
631 // --------------------------------------------------------------------------------------------------
632 #endif // _Y_BROWSER_EMULATION_C_
y_browser * y_choose_browser()
Choose a browser profile from the browser list at random using the defined weights.
Y-lib string function library.
char * user_agent_string
The browser&#39;s user agent string.
char * name
The name of the browser, as you wish to refer to it.
void y_save_browser_to_parameters(const y_browser *browser)
Save the contents of a browser struct as a series of "browser_*" loadrunner parameters.
int y_browser_list_chance_total
The total of all the browser weights added together.
int chance
The odds that said browser will be chosen. This is actually a weight, meaning you could use anything ...
char * y_mem_alloc(size_t size)
Ylib wrapper for malloc()
Definition: y_core.c:221
long fopen(const char *filename, const char *access_mode)
Documented at http://www.cplusplus.com/reference/cstdio/fopen/.
int max_connections_per_host
The maximum number of connections per host to emulate for this browser.
Contains core ylib support functions needed for the functioning of the library.
void * next
Pointer to the next element in the single-linked list of browsers.
int atoi(const char *string)
Documented at http://www.cplusplus.com/reference/cstdlib/atoi/.
int strcmp(const char *string1, const char *string2)
Documented at http://www.cplusplus.com/reference/cstring/strcmp/.
#define Y_RAND_MAX
Alternate RAND_MAX constant for use with y_rand.
Definition: y_core.c:70
y_browser * y_choose_browser_from_list(y_browser *browser_list_head, int browser_list_chance_total)
Choose a browser profile from a browser list at random using the defined weights in that list...
char * fgets(char *string, int maxchar, long file_pointer)
Documented at http://www.cplusplus.com/reference/cstdio/fgets/.
int y_emulate_browser(y_browser *new_browser)
Emulate a specific browser.
void y_log_browser(const y_browser *browser)
Log the content of a browser object.
int y_setup_browser_emulation_from_parameters(const char *browser_name_param, const char *browser_chance_param, const char *browser_max_connections_per_host_param, const char *browser_max_connections_param, const char *browser_user_agent_string_param)
Initialize the browser list based on a set of prepared parameters.
int sscanf(const char *buffer, const char *format_string,...)
Documented at http://www.cplusplus.com/reference/cstdio/sscanf/.
y_browser * y_browser_list_head
The first element in the browser list.
char * y_strdup(char *source)
Copy a string into a malloc&#39;d piece of memory using strdup(), and lr_abort() if the allocation fails...
Definition: y_core.c:268
#define MAX_BROWSER_LIST_LENGTH
Limit the browser list to 1000 browsers, as a failsafe in case of wrong parameter settings (which may...
char * strchr(const char *string, int c)
Documented at http://www.cplusplus.com/reference/cstring/strchr/.
char * y_get_parameter_with_malloc_or_null(const char *src_param)
Get the content of a parameter and return it as a char * (malloc version)
Definition: y_core.c:406
int snprintf(char *buffer, size_t n, const char *format_string,...)
Documented at http://www.cplusplus.com/reference/cstdio/snprintf/. This function was introduced by t...
Internal browser structure, describing a browser as an entity.
long y_rand(void)
Generate a random (integer) number between 0 and Y_RAND_MAX (30 bit maxint).
Definition: y_core.c:156
int max_connections
The maximum number of connections to emulate for this browser.
int y_setup_browser_emulation()
Initialize the browser list, using the default values for the parameter names:
int y_setup_browser_emulation_from_file(char *filename)
Initialize the browser list based on a tab-seperated CSV file.
char * y_get_parameter_or_null(const char *param_name)
Get the content of a parameter and return it as a char *, or return NULL if it wasn&#39;t set...
Definition: y_core.c:373
#define RAND_MAX
RAND_MAX constant for use with rand() - 15 bits integer.
Definition: y_core.c:57
void free(void *mem_address)
Documented at http://www.cplusplus.com/reference/cstdlib/free/.