PowerShell Script Gets Stuck in Error State?

I wrote a PowerShell script to track the response times of a few web sites.  I know there are commercial products that crawl a site at given times and report timing details, missing links, etc., etc., etc.  But, I’m just trying to demonstrate to my hosting provider that one of my sites is consistently slower than other sites.  Ironically, the site which is consistently slower is a higher-grade hosting plan.  Yep, I pay more for the slower site than I do the other sites.  I need this site to be faster than the others, so I don’t want to just lower the plan.

Ok, enough background.  The script simply walks a small array of URLs, creates a WebRequest with caching turned off (NoCacheNoStore), executes the request and calculate the amount of time for the response to return (a LastByte meaurement).  After timing the response for each URL in the array, the script sleeps for a random amount of time (between 2 minutes and 4 hours).

The problem is that eventually one of the sites will timeout.  (Of course this is one of the primary reasons I pay a company to host for me – my customers and visitors shouldn’t experience timeouts, slow responses, etc.  But I digress.)  Once a WebException with the message “The operation has timed-out” occurs, every future WebRequest experiences the same exception.  At first I thought the script must not be clearing $error appropriately, but it is clearing $error between requests (at least twice actually).  I don’t think this exception should occur with each request.  Some of my rationale includes:

  1. $error.Clear() is called before each WebRequest is created (see the first line in the foreach loop of the Main function)
  2. The script does not attempt to re-use WebRequest; it creates a new instance each time (see the first few lines of the GetResponse function)
  3. Neither does it attempt to reuse WebResponse; it sets the variable to $null prior to executing WebRequest.GetResponse(). (see line about mid-way into the GetResponse function)

Another important point is that the PowerShell environment becomes corrupted.  If I stop the script (ctrl-c) and restart it, the time-out errors will continue.  If I close the PowerShell environment (shell or ISE), restart the environment and then restart the script, the errors will not occur (at least not until a “real” timeout occurs).

If you’re interested to educate me on the finer points of PowerShell, using WebRequest in PoSh, etc., the full script is below.  Post a comment if you have ideas or a solution.

1 ################################################## 2 # Script: Track-WebResponse.ps1 3 # Created by: J Burnett 4 # History: Feb, 2011 - Created 5 ################################################## 6 7 function GetResponse($uri) 8 { 9 # Create WebRequest; force it to bypass cache 10 $request = $null # TODO: does this help eliminate the repetitious "Operation timed out" errors 11 $request = [net.WebRequest]::Create($uri) 12 try { 13 $request.CachePolicy = new-object -TypeName System.Net.Cache.HttpRequestCachePolicy 14 $request.CachePolicy = [net.cache.HttpRequestCacheLevel].NoCacheNoStore 15 } 16 catch { 17 $response = "ERROR: Unable to set CachePolicy: $error[0]" 18 } 19 20 [net.WebResponse] $response = $null 21 22 $requestStartTime = get-date 23 try { 24 $response = $request.GetResponse() 25 } 26 # Don't catch anything, just get the end time. Exceptions will be handled by the caller via $error 27 finally { 28 $endTime = get-date 29 } 30 31 return $response, $requestStartTime, $endTime 32 } 33 34 35 #################### 36 function DeclareCustomTypes() 37 { 38 $classDef = @" 39 public class ResponseResults 40 { 41 public string RequestUrl; 42 // Response timing data 43 public System.DateTime RequestStartTime; 44 public System.DateTime RequestEndTime; 45 public System.TimeSpan ResponseTime; 46 // Data from HttpWebResponse 47 public int ContentLength; 48 public string ContentType; 49 public bool IsFromCache; 50 // Error and other messages 51 public string Message; 52 // public System.Net.HttpWebResponse Response; 53 } 54 "@ 55 Add-Type -TypeDefinition $classDef 56 } 57 58 59 #################### 60 Function BuildObject { 61 param ($urlReqT, $respT, $requestStartTimeT, $endTimeT, $contentLength, $msg) 62 63 $respResults = new-object ResponseResults 64 $respResults.RequestUrl = $urlReqT 65 # Capture request / response timing data 66 $respResults.RequestStartTime = $requestStartTime 67 $respResults.RequestEndTime = $endTimeT 68 $respResults.ResponseTime = (([DateTime]$endTimeT) - $requestStartTime) 69 70 # Capture data from HttpWebResponse 71 $respResults.ContentLength = $respT.ContentLength 72 $respResults.ContentType = $respT.ContentType 73 $respResults.IsFromCache = $respT.IsFromCache 74 75 $respResults.Message = $msg 76 77 return $respResults 78 } 79 80 81 #################### 82 Function TimeRequest($uri) 83 { 84 $error.Clear() 85 $resp, $requestStartTime, $endTime = GetResponse($uri) 86 $msg = "Response from $uri took " + ($endTime - $requestStartTime) 87 88 $contentLength = 0 89 if ($null -ne $resp) { 90 $contentLength = $resp.ContentLength 91 $msg += " [ContentLength: $contentLength]" 92 } 93 else { 94 $msg = "ERROR: $error $msg" 95 } 96 97 $forXml = BuildObject $uri $resp $requestStartTime $endTime $contentLength $msg 98 $timeForFileName = $requestStartTime.ToString("yyyyMMdd-HHmm.ss") 99 ($forXml | ConvertTo-Xml).Save("./ResponseResults-$timeForFileName.xml") 100 101 return $msg 102 } 103 104 105 #################### 106 Function Main() 107 { 108 DeclareCustomTypes 109 110 $rgUri = @("http://UtilIntel.com/", "https://altamodatech.com/blogs/", "http://godaddy.com/about/") # "https://altamodatech.com/blogs/wp-admin/index.php", "http://utilintel.com/wp-admin/index.php") 111 while ($true) { 112 foreach ($uri in $rgUri) { 113 $error.clear() 114 $resultMsg = TimeRequest($uri) 115 write-host $resultMsg 116 } 117 118 # Sleep for 2 minute to 4 hours 119 $sleepSecs = get-random -min (2*60) -max (4*60*60) 120 write-host "Sleeping for $sleepSecs seconds... `n" 121 start-sleep -seconds $sleepSecs 122 } 123 } 124 125 #################### 126 # Globals 127 $requestStartTime = get-date 128 129 #################### 130 # Main Entry point 131 Main 132 133

2 thoughts on “PowerShell Script Gets Stuck in Error State?”

  1. I have noticed similar odd issues with using ctrl +c to exit a script…specifically with using the -pssession and invoke-command cmdlets.

    I haven’t had time to research the issue, but I have observed that running get-variable in either the local powershell shell or remote powershell shell on the targeted machine shows the following values
    $
    ? True
    ^

  2. Lines 13 and 14 for setting the cache policy are wrong. Cache policy will be null after executing them.

    Try this:
    $request.CachePolicy = new-object System.Net.Cache.RequestCachePolicy(“NoCacheNoStore”)

Leave a Reply