Apache–jMeter – Test Plan – ASP.Net – Forms Authentication–Troubleshooting

Background

In a previous post we spoke about the steps we undertook to design a test plan for authenticating users connecting to an ASP.Net web site.

In this post, we will shield light on the headwinds that batted us along the way.

 

Headwinds

Outline

  1. Workflow
  2. Thread Group
    • Thread Group Configuration
      • More threads than necessary
  3. HTML Page
    • Hidden fields
    • Entry fields
    • Action or Push button
  4. Component – Cookies Manager
  5. Component – View Results Tree
  6. Component – View Results Table
  7. Web Server
    • HTTP Logs
    • Failed Request Tracing

 

Workflow

This is a very crude drawing …

Workflow_20171026_0533PM

But, it hopefully shows workflow  ..

  1. First HTTP Request Default
  2. Second HTTP Request
    • Use Get Method to request session page
    • Returns to us the session date ( viewstate, eventValidation, viewStateGenerator )
  3. Parse returned Page
    • Using CSS /JQuery parse data and retrieve session data mentioned above
  4. Third HTTP Request
      • Use Post Method to submit user credentials
      • Make sure that session data we parsed earlier is packaged, as well

 

Thread Group

Thread Group Configuration

More threads than Necessary

Images
Thread Group – Configuration @ 7:11 PM

At 7:11 PM, we were hopeful and set up for fifty users, a ramp time of 10 seconds.

And, 2 repetitions.

ThreadGroup_20171024_0714PM

 

Thread Group – Configuration @ 1:03 AM

At 1 AM of the next day, we were humbled to 1 user and a single iteration.

ThreadGroup_20171025_1158AM

 

Explanation

Once we could not successfully authenticate and started adding ViewResultsTree and viewResultsInTable, we started seeing double and some of it was due to the fact that we had more workers than was necessary.

 

HTML Page

Hidden Fields

Images

Explanation

  1. Make a note of all hidden fields
  2. Determine how they are populated
    • Static versus Dynamic
    • Vetted against replay
  3. Encoded ( Yes or No )

 

Cookies Manager

For state management, you will need cookies, server and client side, so please save yourself the headache by enabling them.

Image

Image – Before

HTTPCookieManager_20171025_1152AM

Image – After

HTTPCookieManager_20171026_0459PM

Explanation

  1. Once things are good
    • Clear cookies each iteration
      • Please mark “Clear cookies each iteration” once you are comfortable with your design

 

View Results Tree

Get

View Results Tree – Request

Image

ViewResultsTree_Request_20171025_1204PM

 

Explanation

Take a good look at the Post data

  1. Post data
    • Do you have that the user field populate
    • What about the hidden fields
      • Are the hidden fields supplied by the system and are they varied as a counter measure against replay

 

View Results Tree – Response data

Image

ViewResultsTree_ResponseData_20171025_1204PM

 

Explanation

Our response data looks perfect.

Post

View Results Tree – Request

Image

ViewResultsTree_Request_20171025_0127PM

 

Explanation
  1. Get data
    • Because our request type is not a Get, but a Post, the Get data is left vacant
  2. Cookies Data
    • We are authenticated and we have our cookies

 

Web Server

Please check IIS Logs and enable Failed Request Tracing

  • HTTP Logs
  • Failed Request Tracing

Failed Request Tracing

IIS Failed Request Tracking module offers superlative debugging tooling.

Failed Request Tracing – 001

Failed Request Tracing – 001 – Image

 

Failed Request Tracing – 001 – Textual

Validation of viewstate MAC failed. 
If this application is hosted by a Web Farm or cluster, ensure that machineKey configuration specifies the same validationKey and validation algorithm. 
AutoGenerate cannot be used in a cluster.
See http://go.microsoft.com/fwlink/?LinkID=314055 for more information.

 

 

Failed Request Tracing – 002

Failed Request Tracing – 002 – Image

 

Failed Request Tracing – 002 – Textual

The state information is invalid for this page and might be corrupted.

 

Dedication

Dedicated to Michael Stover.

Main » jmeter-user » 2003-07 » RE: using the regular expression extractor to obtain a form value
Link

MichaelStoverWorkflow_20171026_0545PM


					

Apache – jMeter – Microsoft ASP.Net – Forms Authentication Page

Background

In this our sophomoric jMeter post opted to go with what is for us a hard choice.

It would have been far easier to go with a web site that does not require authentication, but thankfully we do not trust people enough to have one of those.

And, so here we are ruefully going with a site that requires authentication.

 

Design

Initiate JMeter

Let us start jMeter in design mode.

To start jMeter in design mode we simply issue jmeter.bat from the MS Windows Console

 

Outline

Here are the components that we will be using.

List

  1. Thread Group
    • Session Manager
      • HTTP Cookie Manager
    • HTTP Request
      • HTTP Request Defaults
        • HTTP Request ( initial request of page )
          • CSS / JQuery Extractor
            • CSS / JQuery Extractor ( View State )
            • CSS / JQuery Extractor ( Event Validation )
            • CSS / JQuery Extractor ( View State Generator )
      • HTTP Request ( posting of data to page )
    • Debugging
      • View Results Tree
      • View Results in Table

Image

TestPlan_Outline_20171025_0807AM

 

Designer

Thread Group

Image

HTTP Cookie Manager

Image

Explanation

  1. Initial State
    • Number of Thread :- 1
    • Ramp up speed ( in seconds ) :- 1
    • Loop Count :- 1

HTTP Cookie Manager

Image

Explanation

For session management, we need to keep state and so an HTTP Cookie Manager is required.

HTTP Request Defaults

Image

Explanation

Just a basic HTTP test.

Does not accomplish anything other than to validate connection to website.

 

HTTP Request – Initiate Page Get

Image

HTTPRequestLoginPageGet-20171025-0816AM

Tabulate

  1. Protocol [http] :- http
  2. Server Name or IP :- [servername]
  3. Method :- GET
  4. Path :- /apps/login.aspx
  5. Options :-
    • Redirect Automatically ( not set )
    • Follow Redirects ( set )
    • Use KeepAlive ( set )
    • Use multipart/form-data for POST ( disabled )
    • Browser-compatible headers ( not set )

 

HTTP Request – Initiate Page Get – CSS/JQuery Extractor

Outline

Once we initiate a Page Get Request to our Login Page the web server responds and sends back to us a Login Page.

Using a web browser we access the login page and review the Source HTML

HTML Source

Image

HTTPRequestLoginPageGet-CSSJQueryExtractor-hiddenElements-20171025-1031AM

 

Explanation
  1. The hidden elements includes
    • __VIEWSTATE
    • __VIEWGENERATOR
    • __EVENTVALIDATION

 

CSS / JQuery Expression

Image

HTTPRequestLoginPageGet-CSSJQueryExtractor-viewState-20171025-1049AM

Tabulate

  1. Sample JQuery Expression
    • Expression :- input[id=__VIEWSTATE]
    • Attribute :- value

 

HTTP Request – Initiate Page Get – CSS/JQuery Extractor – “VIEW STATE

Image

HTTPRequestLoginPageGet-CSSJQueryExtractor-viewState-20171025-0913AM

 

Tabulate

  1. Name :- CSS/JQuery Extractor – viewState
  2. Reference Name :- viewState
  3. CSS/JQuery Expression :- input[id=__VIEWSTATE]
  4. Attribute :- value
  5. Match No. ( 0 for random ) :- 1

HTTP Request – Initiate Page Get – CSS/JQuery Extractor – “EVENT VALIDATION

Image

HTTPRequestLoginPageGet-CSSJQueryExtractor-eventValidation-20171025-0945AM

 

Tabulate

  1. Name :- CSS/JQuery Extractor – eventValidation
  2. Reference Name :- viewState
  3. CSS/JQuery Expression :- input[id=__EVENTVALIDATION]
  4. Attribute :- value
  5. Match No. ( 0 for random ) :- 1

HTTP Request – Initiate Page Get – CSS/JQuery Extractor – “ VIEW STATE GENERATOR

Image

HTTPRequestLoginPageGet-CSSJQueryExtractor-viewStateGenerator-20171025-0948AM

 

Tabulate

  1. Name :- CSS/JQuery Extractor – viewStateGenerator
  2. Reference Name :- viewState
  3. CSS/JQuery Expression :- input[id=__VIEWSTATEGENERATOR]
  4. Attribute :- value
  5. Match No. ( 0 for random ) :- 1

HTTP Request – Page Post

Image
HTTPRequestLoginPagePost-20171025-1108AM

Tabulate

  1. Protocol [http] :- http
  2. Server Name or IP :- [servername]
  3. Method :- POST
  4. Path :- /apps/login.aspx
  5. Options :-
    • Redirect Automatically (  unchecked )
    • Follow Redirects (  checked )
    • Use KeepAlive (  checked )
    • Use multipart/form-data for POST ( unchecked )
    • Browser-compatible headers (  unchecked )

HTTP Request – Page Post – Parameters

Image

Tabulated

Name Value Encode Include Equals
txtMemberName dadeniji Checked Checked
txtMemberPassword [password] Checked Checked
btnValidateMember Validate Checked Checked
__VIEWSTATE ${viewState} Checked Checked
__VIEWSTATEGENERATOR ${viewStateGenerator} Checked Checked
__EVENTVALIDATION ${eventValidation} Checked Checked

 

View Results Table

View Results Table – Get

View Results Table – Get – Sampler Result
Image

View Results Table – Get – Request
Image

View Results Table – Get – Response Data
Image

View Results Table – Post

View Results Table – Post – Request
Image

View Results Table – Post – Request
Image
 
View Results Table – Post – Response Data
Image

View Results in Table

Image

Tabulated

Metric Get Post
Start Time 12:01:42.708 12:01:42.789
label Get Post
Sample Time (ms) 74 1833
Bytes 7995 287595
Send Bytes 148 1160
Latency 148 1160
Connect Time (ms) 15 0

 

References

  1. Apache – JMeter
    • Building a web test plan
      Link
    • Apache JMeter HTTP(S) Test Script Recorder
      Link
  2. Mark Schabacker
    • Using JMeter with ASP.NET WebForms Authentication
      Link
  3. BlazeMeter
    • ASP.NET Login Testing with JMeter
      Link
    • How to Debug your Apache JMeter Script
      Link
  4. execute automation
    • ASP.Net WebForms Authentication using JMeter (Series)
      Link
  5. QA
    • Stack Overflow
      • View State value doesn’t get extracted from Request
        Link
      • jmeter Problems getting the _ViewState and _EventValidation parameters
        Link

.Net – ASPX – Avoid Parser Error when commenting out code

Background

While trying out different code snippets as I worked on tweaking an ASPX app, I ran into a parser error.

BTW, the enhancement is detailed here.

Let us quickly go over the parser error.

 

Parser Error

 

Code


<asp:TemplateColumn HeaderText="Link" >

	<ItemTemplate>		

	<!--
		
		 <asp:HyperLinkColumn 
				HeaderText="Link" 		
				Text="Link" 
				NavigateUrl='<%# DataBinder.Eval(Container.DataItem, "eventLink") %>'
				Visible="True"
				Target="_blank"
				runat="server"												
				> 	
				
		</asp:HyperLinkColumn>   		    

	-->
	
	 <asp:HyperLink
			HeaderText="Link" 		
			Text="Link" 
			NavigateUrl='<%# DataBinder.Eval(Container.DataItem, "eventLink") %>'
			Visible="True"
			Target="_blank"
			runat="server"
			> 	
	 </asp:HyperLink>   		    		                         																				
	
	</ItemTemplate>
	
</asp:TemplateColumn>


 

Parser Error

Image

ParserError-20160730-0731AM

Textual


Parser Error

Description: An error occurred during the parsing of a resource required to service this request. Please review the following specific parse error details and modify your source file appropriately. 

Parser Error Message: Databinding expressions are only supported on objects that have a DataBinding event. System.Web.UI.WebControls.HyperLinkColumn does not have a DataBinding event.

Source Error: 

Correct

Here is the change.

Basically, it is to replace “<!–” and “–>” with “<%–” and “–%>“.


<asp:TemplateColumn HeaderText="Link" >

	<ItemTemplate>		

	<%--
		
		 <asp:HyperLinkColumn 
				HeaderText="Link" 		
				Text="Link" 
				NavigateUrl='<%# DataBinder.Eval(Container.DataItem, "eventLink") %>'
				Visible="True"
				Target="_blank"
				runat="server"												
				> 	
				
		</asp:HyperLinkColumn>   		    

	--%>
	
	 <asp:HyperLink
			HeaderText="Link" 		
			Text="Link" 
			NavigateUrl='<%# DataBinder.Eval(Container.DataItem, "eventLink") %>'
			Visible="True"
			Target="_blank"
			runat="server"
			> 	
	 </asp:HyperLink>   		    		                         																				
	
	</ItemTemplate>
	
</asp:TemplateColumn>


 

Acknowledge

Crediting Scott Guthrie

Tip/Trick: Using Server Side Comments with ASP.NET 2.0
Link

ASP.Net – DataGrid – Adding Hyperlink Column

 

Background

There is a dated Black-book web site that I wrote to keep up with people and appointments years ago.

Here is what the Appointments page looks like:

List Appointments

BeforeGrid

Edit Appointment

BeforeEdit

 

Enhancement

Lately, I have found more and more the need to isolate the corresponding web link into its own entry field and not just mushroom it into the comments multi-edit box.

Edit Appointment

AfterEdit

List Appointments

AfterGrid

 

Code

I had a little problem and had to have my brother help me with .Net code to add an hyperlink to a Data Grid.

And, so wanted to place it online for anyone else rusty with ASP.Net

Code line


<asp:DataGrid id="gridContactEventInfo" BorderColor="black" BorderWidth="1" CellPadding="3" AutoGenerateColumns="false" runat="server" font-size="8" width='100%'>

 <HeaderStyle BackColor="#9FBCE3">
 </HeaderStyle>

 <ItemStyle BackColor="#EEF2F7">
 </ItemStyle>
			 
 <AlternatingItemStyle BackColor="#EEEEEE">                                 
 </AlternatingItemStyle>                                 
								  
 <columns>
 
	 <asp:BoundColumn HeaderText="Event" DataField="eventName" /> 


		<asp:TemplateColumn HeaderText="Link" >

			<ItemTemplate>		

			 <asp:HyperLink
					HeaderText="Link" 		
					Text="Link" 
					NavigateUrl='<%# DataBinder.Eval(Container.DataItem, "eventLink") %>'
					Visible="True"
					Target="_blank"
					runat="server"
					> 	
			 </asp:HyperLink>   		    		                         																				
			
			</ItemTemplate>
			
		</asp:TemplateColumn>

	</columns>		

</asp:DataGrid>	

 

Quick Explanation

  1. Added asp:TemplateColumn
  2. Added ItemTemplate
  3. Added asp:HyperLink
    • Added NavigateUrl='<%# DataBinder.Eval(Container.DataItem, “eventLink”) %>’

 

ASP.Net – DataGrid – Sorting

Background

Yearns ago I developed an electronic’s black-book. It is very basic and rudimentary.  It allows me to jot down names and contact details.

Here is what the screen looks like:

defaultSorting

A couple of days ago, I wanted to find someone I met a few months ago. Unfortunately, I could not remember the person’s name, but I remembered how I met the person and noted that it is a very recent acquaintance.

And, so I know sorting by name is the way to go, but I also know that the sorted by column never quite worked.

So this is a good opportunity to clear that up.

Forward

When adding the ability to sort a DataGrid via exposing individual Column sorting, there are a few things to keep in mind.

  1. Datagrid
    • Set AllowSorting to true i.e. AllowSorting=”True”
    • Set OnSortCommand to an event function i.e. OnSortCommand = “gridSortEvent”
    • On each sort-able column, be sure to set
      • HeaderText
      • DataField
      • SortExpression
  2. In the code behind
    • From Database, feed in SQL Query and get unsorted RecordSet
      • Persist Query Result in DataSet
      • Capture DataSet’s default view in a variable ( objDefaultView )
      • Save default view in a Session Variable
    • Have a helper function that we called getSortDirection. It receives the sortColumn and compares that sortColumn with the previous callback’s sortColumn.
      • Determine Sort Direction
        • If previous sortColumn is empty, then set to default of ASC
        • If previous sortColumn is the same as current, then toggles sortDirection
        • If previous sortColumn is different than current, then assumes ASC
      • Preserve Sort Column and Sort Direction
        • We used viewState to preserve the passed-in sortColumn and derived sortDirection
      • Return sortDirection
    • Have the event handling function ( gridContactSortEvent )
      • It gets passed the DataGridSortCommandEventArgs argument
      • We extract the sortExpression from the argument
      • We call the helper function mentioned above ( getSortDirection )
      • We concatenate our two sort arguments (sortColumn and sortDirection) and create a new variable sortColumnAndDir
      • Retrieve the session variable gridContact and casts it as a DataView; saved as dv
      • Set the Sort property of the dataview (dv) to the variable sortColumnAndDir
      • Set the grid DataSource to the saved variable dv
      • Invoke grid’s DataBind method

 

Code

ASPX Code


   <form id='frmCover'
         method='post'
         runat='server'
         action='Cover.aspx'
         defaultFocus='txtContactSearchTag'
         defaultButton='idAnchorContactSearch'
    >

   <asp:dataGrid
      id='gridContact'
      BorderColor='black'
      BorderWidth='1'
      CellPadding='3'
      AutoGenerateColumns='false'
      runat='server'
      width='100%'
      AllowSorting='True'
      onSortCommand='gridContactSortEvent'
>

   <HeaderStyle BackColor='#9FBCE3'>
   </HeaderStyle>

   <ItemStyle BackColor='#EEF2F7'>
   </ItemStyle>

   <AlternatingItemStyle BackColor='#F5F9FD'>
   </AlternatingItemStyle>;

   <Columns>

     <asp:HyperLinkColumn>

         HeaderText='Contact'
         Text='Contact'
         DataTextField='Contact'
         DataNavigateUrlField='ContactIdentifier'
         DataNavigateUrlFormatString='ContactBrowse.aspx?ContactID={0}'
	 SortExpression='Contact'
      >       

      </asp:HyperLinkColumn>   		                        

      <asp:BoundColumn
          HeaderText='Affiliate'
          DataField='affiliate'
          SortExpression='affiliate'
       > 		

       </asp:BoundColumn>

       <asp:BoundColumn
	  HeaderText='Profession'
	  DataField='Profession'
	  SortExpression='Profession'>; 	

      </asp:BoundColumn>		                            

      <asp:BoundColumn
          HeaderText='Created On'
          DataField='dateCreatedAsDay'
          SortExpression='dateCreated'>

      </asp:BoundColumn>

      <asp:TemplateColumn
	  HeaderText='Created On'
	  SortExpression='dateCreated'
	  Visible='False'
      >

        <ItemTemplate>

         <asp:label 

             id='dateCreatedFormattedAsDay'
             runat='server'
             text='<%# Eval('dateCreated', '{0:d}') %>' 

          >

        </ItemTemplate>

      </asp:TemplateColumn>									

   </Columns>                                                   		

  </asp:DataGrid>

 </form>

Code Behind – C#


namespace addressBook
{

    using System;
    using System.Collections;
    using System.Web;
    using System.Web.UI;
    using System.Web.UI.HtmlControls;
    using System.Web.UI.WebControls;
    using System.ComponentModel;
    using System.Data;
    using System.Data.OleDb;
    using System.Web.Security;
    using System.Text;    //StringBuilder
    using System.Configuration;    

    public class frmCover : System.Web.UI.Page
    {

       private DataView getRecordSetNames
       (
	      String strContactFilter
       )
       {

	   if (
                    (strMemberID == null)
                 || (strMemberID == string.Empty)
              )
	    {
		return null;
	    }

            OleDbDataAdapter objDbAdapter = null;
	    DataSet objDataSet;
	    DataView objDataView;
	    OleDbCommand objOleDBCommand;

	    strSQLQuery = &quot;dbo.usp_GetContactsForMember&quot;;

	    objOleDBCommand = new OleDbCommand();
	    objOleDBCommand.Connection = objConnection;
	    objOleDBCommand.CommandType
                 = CommandType.StoredProcedure;
	    objOleDBCommand.CommandText = strSQLQuery;

            OleDbParameter objDBParametemItemMemberID =
                             new OleDbParameter();
            objDBParametemItemMemberID.OleDbType
                             = OleDbType.VarChar;
            objDBParametemItemMemberID.Size = 88;
            objDBParametemItemMemberID.Value =
                 strMemberID;
            objOleDBCommand.Parameters.Add(
                 objDBParametemItemMemberID);			

            OleDbParameter objDBParametemItemContactFP =
                            new OleDbParameter();
            objDBParametemItemContactFP.OleDbType
                           = OleDbType.VarChar;
            objDBParametemItemContactFP.Size = 88;
            objDBParametemItemContactFP.Value =
                   strContactFilter;

            objOleDBCommand.Parameters.Add(
                   objDBParametemItemContactFP);			            

            objDbAdapter = new OleDbDataAdapter();
            objDbAdapter.SelectCommand = objOleDBCommand;

            objDataSet = new DataSet(&quot;namedetails&quot;);

            objDbAdapter.Fill(objDataSet);						

	    objDataView =
               objDataSet.Tables[0].DefaultView;

	    objDataView.RowFilter = String.Empty;

	    return objDataView;

	}              

	private void loadNamesGrid(
                                      String strContactFillter
                                  )
	   {

		//ICollection objNames;
		DataView    objNames;

		//get Names
		objNames = getRecordSetNames
                       (
                          strContactFilter
                       );

		//set data source
		gridContact.DataSource = objNames;

		//data Bind
		gridContact.DataBind();

                //save Grid's default view as a
                //session var
		Session[&quot;gridContact&quot;] = objNames;

	   }       

	private string getSortDirection(string column)
	{

	   // By default, set the sort direction to asc
	   string sortDirection = &quot;ASC&quot;;

	   // Retrieve the last column that was sorted.
	   string sortExpression = (string)
               ViewState[&quot;gridContactSortExpression&quot;];

           string lastDirection = null;

	   if (sortExpression != null)
	   {
	      // Check if the same column is being sorted
	      // Otherwise, the default value can be
	      if (sortExpression == column)
	      {
		lastDirection = (string)
                   ViewState[&quot;gridContactSortDirection&quot;];

		if ((lastDirection != null) &amp;&amp; (lastDirection == &quot;ASC&quot;))
		{
		   sortDirection = &quot;DESC&quot;;
		}

	      } // if sortExpression == column

	  } // if sortExpression is not null

	  // Save new values in ViewState.
	  ViewState[&quot;gridContactSortDirection&quot;] = sortDirection;
	  ViewState[&quot;gridContactSortExpression&quot;] = column;

	  return sortDirection;

	} // getSortDirection

       protected void gridContactSortEvent(
                                             Object sender
					   , DataGridSortCommandEventArgs e
					   )
       { 

 	   DataView dv = (DataView)Session[&quot;gridContact&quot;];
	   String strSortColumn = e.SortExpression;

	   String strSortDir = getSortDirection(strSortColumn);

	   String strSortColumnANDDir = strSortColumn + &quot; &quot; + strSortDir;

	   // The DataView provides an easy way to sort.
	   // Simply set the Sort property with
	   // the name of the field to sort by.
	   // dv.Sort = e.SortExpression;
	   dv.Sort = strSortColumnANDDir;

	   // Re-bind the data source and specify that it should be sorted
	   // by the field specified in the SortExpression property.
	   gridContact.DataSource = dv;
	   gridContact.DataBind();

 	} // gridContactSortEvent

    } // public class frmCover : System.Web.UI.Page

} //namespace

 

Post Code Changes

Our code changes bore dividend, as here is the result:

Grid Sorted by Name – Ascending

SortByNameAscending

Grid Sorted by Name – Descending

SortByNameDescending

 

Summary

Couple of things.  We increased session state usage by maintaining the results of the original query in a session variable.

As that data was saved in a session variable, we did not have to pass along the user’s intended sort column and direction to the database.

We needed a bit of code to determine sort direction as the DataGrid does not appear to keep that as part of it’s session state.

Please be sure to maintain database column’s datatype fidelity as much as possible. That is, keep data and formatting different.  This is achievable by exposing two data columns, or using the core column in the DB and using TemplateColumn and ItemTemplate in the ASPX code to perform formatting.

The big realization for me is that I actually need to handle the event for sorting, as it is not handled intrinsically by setting “AllowSorting“.

References

DataGrid

Microsoft Reference

 

GridView

Microsoft Reference

Q/A

 

IIS – ASP & ASP.Net – Session State Management – In Process

Background

I have a few ASP.Net applications running on a MS Windows 2003 box.

Occasionally, the applications error out.

 

Error

Textual:


Procedure or function 'listDatesWithEventsForMonth' expects parameter '@memberIdentifier', which was not supplied.

Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code. 

Exception Details: System.Data.OleDb.OleDbException: Procedure or function 'listDatesWithEventsForMonth' expects parameter '@memberIdentifier', which was not supplied.

Source Error: 

Line 613:
Line 614:
Line 615:            objDBReader = objDBCommand.ExecuteReader();
Line 616:
Line 617:            while (objDBReader.Read())

 

Thoughts …

There is nothing more disconcerting than when an application wrote yearns ago, crash out so ungracefully on the original developer.

Yes, there are problems, but I am too lazy to fix them.

The problems include:

  1. Architectural – In Process Management
    • Should not be using in-process state management, but more so out of process.
      • In using out of process State Management, worker process recycling will not null out session states
      • Same with errant “Worker Process” that occasionally die
  2. Architectural – Web Garden
    • Insufficient consideration of multi worker processes
  3. Coding Issue
    • Why am I not sanitizing my variables; that is, when I read state data via session[x], I should make sure it is not null, before passing them it off to the DB
      • When null, display an error message and return to the most relevant screen depending on what is missing

Code

Here is the code snippet:


        protected void getUserInfo()
        {

            System.Security.Principal.IIdentity objIdentity;
            Boolean bAuthenticated;

            //get currently logged on user
            objIdentity = User.Identity;

            bAuthenticated = objIdentity.IsAuthenticated;

            strMemberName = objIdentity.Name;

            //get currently selected member
            if (Session["memberID"] != null)
            {
            	strMemberID = Session["memberID"].ToString();
            }	            

        }     

To sanitize the read in session variable, please change it a bit to something like this:


  protected void getUserInfo()
  {

     System.Security.Principal.IIdentity objIdentity;
     Boolean bAuthenticated;          

     objIdentity = User.Identity;

     bAuthenticated = objIdentity.IsAuthenticated;

     strMemberName = objIdentity.Name;

     if (Session["memberID"] != null)
     {
      	strMemberID = Session["memberID"].ToString();
     }	            

     //On 2015-08-10, dadeniji
     //sanitize
    if (     ( strMemberID == null)
          || ( strMemberID == String.Empty) )
    {

	String strURLHome = "login.aspx";
	Response.Redirect(strURLHome,true);

    }		

 }     

 

 

Bandage

Let us take to Google and see what type of bandage we can wool over this self induced wound.

Google Hits

 

Apply

Web.Config

  1. ASP.Net Configuration Settings – State Management
    • Session timeout ( in minutes ):
      • Change from 20 minutes to 300 minutes

 

 

ASP.Net Configuration Settings – State Management ( Before )

sessionStateSettings (before)

After

sessionStateSettings (after)

 

Application Pool

  1. Recycling
    • Recycle working process ( in minutes ):
      • Keep at 1740
  2. Performance
    • Idle Timeout
      • Shutdown Worker processes after being idle for ( time in minutes )
        • Changed from 20 minutes to 300 minutes
    • Web Garden
      • Maximum Number of Worker Processes
        • Left as 1

 

Recycling ( Before )

Application – Performance [ Before ]

Performance (Before)

 

Application – Performance [ After ]

Performance (After)

 

Summary

In summary, we are modifying the session state timeout on the web site, via the web.config file.

The change is to increase the idle time out from 20 minutes to 5 hours.  2 to 3 hours will likely have been sufficient, as this is not a heavily used web server and so we are likely not competing with other web sites nor users.

On the worker processes, which is fronted through the Application Pool configuration, we modified the idle timeout setting in similar fashion; changed from 20 minutes to 5 hours.

It goes without saying that the Application itself needs to be significantly cleaned-up. We need to possibly capture timeout notifications, and also sanitize data read in and not simply assume they are always valid.

 

References

ASP

 

ASP.Net

Mining Application Logs – 2015/Feb

Background

A few days a 3rd party web Application that I am working on started getting this error message:

PleaseSelectACity

 

Diagnostic Steps

Vendor

Spoke to the onsite Vendor Support person and he blamed it on customization we are doing on our side.

And, so again, we are on our own.

As the Application is Microsoft; .Net, Classic ASP, C#, and Classic ASP, I am on safe grounds.

Launched Command Shell

Launched Command Shell and changed folder to application’s Inetpub folder.

Find Text

Tool

While one can use GUI tools to search, I personally find the built-in Windows Search tool hard to navigate.  And, so we will use findstr for our exercise.

For our current needs, here are the relevant parameters:

Argument Description  Usage
 /m Prints only the file name if a file contains a match. This allows us to print out matches and not worry about their contextual usage in corresponding file
 /p Skips files with non-printable characters. In our case, we have exes and dlls.  These are binary files and their contents is not available via normal textual parsing means nor editors.And, so for now we will skip them.
 /s Searches for matching files in the current directory and all sub-directories. Delve into sub-folders \ sub-directories, as well.
 /c Uses specified text as a literal search string. Findstr supports both literal and regular expression (RE) searches.In our case, we will edge towards literal as it is a bit simpler and we will avoid the relative complexity of RE; especially as we do not need it for our current simple need.

 

 

 

Let us use findstr to look for the text.

   findstr /p /s /c:"Please select a city"

 

Output

Textual:


   _translate\english.inc:t5078 = "Please select a country"
   _translate\englishus.inc:t5078 = "Please select a country"

 

pleaseSelectACountry

 

Match ID

From the matches above, we appear to be dealing with an App that has a bit of Internationalization.  That is, it supports various languages through IDs and “culture” specific resource files.

This time, I do not want to print out the specific matching lines, just the actual file names.

   findstr /M /p /s /c:"t5078" *.* | more

 

Output

Textual:


   GridCache\js\jQuery.VC.js
   GridCache\js\jQuery.VC.min.js
   FuncLinks\metaAdmin.asp
   _translate\dutch.inc
   _translate\english.inc
   _translate\englishus.inc
   _translate\french.inc
   _translate\german.inc
   _translate\hungarian.inc
   _translate\indonesian.inc
   _translate\italian.inc
   _translate\norwegian.inc
   _translate\portuguese.inc
   _translate\portuguesebr.inc
   _translate\spanish.inc
   _translate\swedish.inc

 

Review HTML\jQuery File

In the GridCache\js\jQuery.VC.js file, we find matches for t5078 and t5079.

 


     var cz = 0;
	e.find("#selCountry option:selected").each(function() {
		if (cz == 0) {
			cz = b(this).attr("value")
		}

	if (cz <= 0) {
		alert(getTextValue("t5078"));
		e.find("#selCountry").focus();
		return
	}

	var y = 0;
	e.find("#selRegion option:selected").each(function() {
		if (y == 0) {
			y = b(this).attr("value")
		}
	});
	if (y <= 0) {
		alert(getTextValue("t5079"));
		e.find("#selRegion").focus();
		return
	}

 

 

Review ASP File

Next was to review some of the Server-side ASP files:

                     

  <strong><%= t1 %>:</strong>
  <span id="summary_location">
    <% if isArray(arr_locationDetails) and int_serviceOnly = 0 then response.write arr_locationDetails(1,0) end if %>
  </span>

 

Invoke Stored Procedure

Let us invoked our Stored Procedure and pass in the arguments we are most interested in.

  

  set oRS_getForm = server.createObject("ADODB.recordset")
  oRS_getForm.CursorLocation = 3


  on error resume next

  oRS_getForm.open "{call dbo.sp_displayForm ("&userDefinedLocation&", "&int_roomID&","&Session("LanguageID")&", "&Session("UserID")&", "&userDefinedBusinessUnit&", "&Session("countryID")&", "&int_bookingID&", '"&userDefinedFloor&"', "&session("filterGrid")&", "&int_managedBooking&", "& int_editSeries&", '"& dt_currentDateAndTime &"', "&session("outlook")&", "&int_serviceOnly&", "&int_deliveryPoint&")}", oConn

  set colErrs = oConn.Errors

  if oConn.Errors.Count <> 0 then 

    response.Write t1499
    For Each objError In colErrs 
        call logErrorData(objError.Number, objError.Description, "Form", "sp_displayForm")
    Next
    oConn.Errors.Clear
    oConn.close
    set oConn = nothing
    response.end

 end if

 

Retrieve Database Record Set

In the code below, we can see that on the third getRows we bring back the recordset that we are most interested in (arr_locationDetails).

                     

 if oRS_getForm.recordcount <> 0 then arr_userDetails = oRS_getForm.getRows() end if
 set oRS_getForm = oRS_getForm.nextRecordset()

 if oRS_getForm.recordcount <> 0 then arr_siteSetup = oRS_getForm.getRows() end if
 set oRS_getForm = oRS_getForm.nextRecordset()

 if oRS_getForm.recordcount <> 0 then arr_locationDetails = oRS_getForm.getRows() end if
 set oRS_getForm = oRS_getForm.nextRecordset()

 if oRS_getForm.recordcount <> 0 then arr_resourceNamingDetails = oRS_getForm.getRows() end if
 set oRS_getForm = oRS_getForm.nextRecordset()

 if oRS_getForm.recordcount <> 0 then arr_colours = oRS_getForm.getRows() end if
 set oRS_getForm = oRS_getForm.nextRecordset()

 if oRS_getForm.recordcount <> 0 then arr_messages = oRS_getForm.getRows() end if

 

Stored Procedure Code

Get Location & Business Unit


   SELECT 
           l.pkLocationID, l.LocationName, l.fkCountryID, isNull(l.att, 0), isNull(CostCentreMandatory, 1) AS CostCentreMandatory    
         , l.fkRegionID as regionID ,l.CCPaymentCostCodeEnabled CCPaymentCostCodeEnabled 
  FROM dbo.tblLocationBusinessUnit lbu     
        INNER JOIN dbo.tblLocation l 
         ON l.pkLocationID = lbu.fkLocationID    
  WHERE fkBusinessUnitID = @businessUnit    
  

 

Database Error Log

We went through some more files and finally reviewed the Application’s Error Log Tables.

 


set nocount on
go

set dateformat dmy
go

declare @dateCutoff datetime
declare @VENDOR_TAG varchar(60)

set @VENDOR_TAG = 'BR'
set @dateCutoff = '19/3/2015'

declare @errorListIgnore TABLE
(
    [errorDescription] varchar(60)
)

declare @errorListInclude TABLE
(
    [errorDescription] varchar(60)
)

insert into @errorListIgnore
([errorDescription])
values
      ('Invalid')
    , ('GridCache:InsertCache')
    , ('Cache removed reason: Removed')
    , ('found file')
    , ('Host')

insert into @errorListInclude
([errorDescription])
values
      ('Sequence contains no elements')

select top 1500
          'using ignore list' as 
        , replace(tblCE.errorNumber, @VENDOR_TAG, '') as [errorNumber]
        , errorDescription
        , replace(tblCE.[filename], @VENDOR_TAG, '') as [filename]
        , errorDescription
        , storedProcedure
        , replace(tblCE.datetime, @VENDOR_TAG, '') as [datetime]
from   dbo.tblBRError tblCE
where  errorDescription not in ( select tblL.[errorDescription] from @errorListIgnore tblL)
and    [datetime] < @dateCutoff
order by pkErrorID desc

select top 1500
          'using include list' as 
        , replace(tblCE.errorNumber, @VENDOR_TAG, '') as [errorNumber]
        , errorDescription
        , replace(tblCE.[filename], @VENDOR_TAG, '') as [filename]
        , errorDescription
        , storedProcedure
        , replace(tblCE.datetime, @VENDOR_TAG, '') as [datetime]
from   dbo.tblBRError tblCE
where  errorDescription in ( select tblL.[errorDescription] from @errorListInclude tblL)
and    [datetime] < @dateCutoff
order by pkErrorID desc

 

Thank goodness for Application’s Logging, we found entries such as:

 

Textual:

Error Entries
   at RNM..DAL.User.UserDAL.GetUserdefaultSettingsDB(Int32 userID)
at RNM..DAL.User.UserDAL.GetUserDefaultSettingsByID(Int32 userID)
at RNM..BLL.User.UserBLL.GetUserDefaultSettingsByID(Int32 userID)
  at RNM..Utilities.ExceptionManager.HandleException(Exception hExp, String dataToLog, Boolean throwException)
at RNM..BLL.User.UserBLL.GetUserDefaultSettingsByID(Int32 userID)
at RNM..BLL.User.UserBLL.GetUserDefaultSettingsByResourceID(Int32 userID, EnumResource resource)
 Timezone name not found

 

 

 Image:

errorTable

 

Fixes

There are a couple of Database support tables that are pertinent to us.

  • dbo.tblBusinessUnit
  • dbo.tblLocationBusinessUnit
  • dbo.tblLocation
  • dbo.tblUserdefaultView

Summary

I have intentionally obfuscated error messages and simplified our problem solving passageway for several reasons:

  • The Application is only used by a few companies
  • The Vendor does not have a broad support web site
  • I am too old to have the Vendor close some of our tool-sets and have to rely on them ever more; or worse still resort to more rudimentary techniques

 

Listening

It has been a taxing week.  And, so I have to listen and share

Patrick Anthony – No Pretense (Official Music Video)
https://www.youtube.com/watch?v=7g4_6Y_gCqw

Link