Share via


PowerShell + REST API: Invoke-RestMethod gotcha

A recent support case saw the customer complaining that one of the PowerShell scripts making multiple REST API calls was failing as below error was encountered:

Invoke-RestMethod : The operation has timed out

We tried checking the server API logs and saw that the connections made from the script were not even reaching the server. We fired up Fiddler to see what was happening behind the scenes but noticed that running the Fiddler in background mitigated the issue but the calls were still failing from the script using the Invoke-RestMethod in PowerShell v4.

A web search found this is a known issue for PUT and DELETE methods in a REST API call.

The customer was facing this issue in the GET method calls to the REST API. Strange!

Found workaround in one of the off-topic forums as this was suspected to be a server side issue. Credits goes to a user named LanceC1. As per the workaround, this is a sort of brute force as this will close all the connections to a hostname.

We have to instantiate an object of ServicePoint Class (this object is used for managing HTTP connections).

Store the ServicePoint Object for the hostname you are connecting to like below:

$servicePoint = [System.Net.ServicePointManager]::FindServicePoint("https://www.microsoft.com")

Now try exploring the object:

PS> $ServicePoint

BindIPEndPointDelegate :

ConnectionLeaseTimeout : -1

Address                : https://www.microsoft.com/

MaxIdleTime            : 100000

UseNagleAlgorithm      : True

ReceiveBufferSize      : -1

Expect100Continue      : True

IdleSince              : 2/8/2015 8:10:46 AM

ProtocolVersion        : 1.1

ConnectionName         : https

ConnectionLimit        : 2 <--------------------------- Simultaneous connections limit

CurrentConnections     : 0

Certificate            :

ClientCertificate      :

SupportsPipelining     : True

Also notice that the connection limit property can be set:

PS> $ServicePoint | Get-Member

   TypeName: System.Net.ServicePoint

Name                   MemberType Definition                                                                           

----                   ---------- ----------                                                                           

CloseConnectionGroup   Method     bool CloseConnectionGroup(string connectionGroupName)                                

Equals                 Method     bool Equals(System.Object obj)                                                       

GetHashCode            Method     int GetHashCode()                                                                    

GetType                Method     type GetType()                                                                       

SetTcpKeepAlive        Method     void SetTcpKeepAlive(bool enabled, int keepAliveTime, int keepAliveInterval)         

ToString               Method     string ToString()                                                                    

Address                Property   uri Address {get;}                                                                   

BindIPEndPointDelegate Property   System.Net.BindIPEndPoint BindIPEndPointDelegate {get;set;}                          

Certificate            Property   System.Security.Cryptography.X509Certificates.X509Certificate Certificate {get;}     

ClientCertificate      Property   System.Security.Cryptography.X509Certificates.X509Certificate ClientCertificate {get;}

ConnectionLeaseTimeout Property   int ConnectionLeaseTimeout {get;set;}                                                

ConnectionLimit        Property   int ConnectionLimit {get;set;} <-------------- This property can be set                                                      

ConnectionName         Property   string ConnectionName {get;}                                                         

CurrentConnections     Property   int CurrentConnections {get;}                                                        

Expect100Continue      Property   bool Expect100Continue {get;set;}                                                    

IdleSince              Property   datetime IdleSince {get;}                                                            

MaxIdleTime            Property   int MaxIdleTime {get;set;}                                                           

ProtocolVersion        Property   version ProtocolVersion {get;}                                                       

ReceiveBufferSize      Property   int ReceiveBufferSize {get;set;}                                                     

SupportsPipelining     Property   bool SupportsPipelining {get;}                                                       

UseNagleAlgorithm      Property   bool UseNagleAlgorithm {get;set;}                                                    

Now changing the Connectionlimit is one of the workaround as already stated in the Connect Bug that Invoke-RestMethod does a poor job of cleaning up the connections it made. We assume that Fiddler increases this limit and also cleans up the stale connections, that's why running Fiddler in background to debug won't be useful.

$ServicePoint = [System.Net.ServicePointManager]::FindServicePoint('<REST API URL>')
$ServicePoint.ConnectionLimit = 4 #Change this value...though not advised (against HTTP 1.1 specs)
#Make the REST API Call using Invoke-RestMethod

Another way is to close the Connection Group. This will close all the connections made to the host.
We went with this way as our REST API Server URL has a different name then the Web Server endpoint.

$ServicePoint = [System.Net.ServicePointManager]::FindServicePoint('<REST API URL>')
#Make the REST API Call using Invoke-RestMethod
$ServicePoint.CloseConnectionGroup("")

Please do update this wiki if you find other gotchas /workarounds and insights into the problem.