ASP.NET HttpWebRequest stop sending requests, high CPU usage - asp.net

I have an old ASP.NET application. We send out httpWebRequest to a remote REST server and retrieve XML back, most of the time the application works fine. Recently, we got some high CPU usage issue several times a day.
During the high CPU usage time, we monitored those httpWebRequest connections (by checking netstat for the w3wp process). At the very beginning, the connections change to "CLOSE_WAIT" status from "ESTABLISHED", then after those connections timeout, those connections disappeared one by one, and then there is no connection any more.
After resetting the IIS, when the w3wp.exe process start again, we still could not find any connections to httpWebRequest target server. So the CPU usage keep staying at high level. Even after several round of reset, it won't solve the issue, until we saw some connections start to connect to httpWebRequest target server, the CPU usage went down.
I actually thought it could be the issue of my code not handling the httpWebRequest properly, I posted another question here: How to close underlying connections after catch httpwebrequest timeout?.
As mentioned in that question, I also found lots of timeout exceptions for System.Net.HttpWebRequest.GetResponse(). We found 3500 of the same exception within 5 minutes when CPU usage is really high.
What could cuase this type of issue and what could be the medicine? Why won't the application send out request any more (since there is no connection in netstat)?
Here is the source code just in case:
System.Net.HttpWebResponse httpWebResponse = null;
System.IO.Stream stream = null;
XmlTextReader xmlTextReader = null;
try
{
System.Net.HttpWebRequest httpWebRequest = (System.Net.HttpWebRequest)System.Net.WebRequest.Create(request);
httpWebRequest.ReadWriteTimeout = 10000;
httpWebRequest.Timeout = 10000;
httpWebRequest.KeepAlive = false;
httpWebRequest.Method = "GET";
httpWebResponse = (System.Net.HttpWebResponse)httpWebRequest.GetResponse();
stream = httpWebResponse.GetResponseStream();
xmlTextReader = new XmlTextReader(stream);
xmlTextReader.Read();
xmlDocument.Load(xmlTextReader);
//Document processing code.
//...
}
catch
{
//Catch blcok with error handle
}
finally
{
if (xmlTextReader != null)
xmlTextReader.Close();
if (httpWebResponse != null)
httpWebResponse.Close();
if (stream != null)
stream.Close();
}

From your description, it is not clear to me that your high CPU utilization is related to your outgoing HTTP requests. High CPU utilization could be due to a bug in your code, a bug in CLR, IIS, or something else. Without knowing which component is consuming the CPU, you wont be able to do anything further.
If I were you, I would first try to attach a sampling profiler to the W3WP process, and see which component is consuming the CPU. That should point you to the next steps in resolving this issue.

I would suggest you to try sending requests asynchronously to avoid blocking the main thread:
using (var client = new WebClient())
{
client.OpenReadCompleted += (sender, e) =>
{
using (var reader = XmlReader.Create(e.Result))
{
// Process the XML document here
}
};
client.OpenReadAsync(new Uri("http://www.example.com"));
}

Finding the reason for the high CPU utilization can take some time since you will have to locate the code that is causing the problem. I am working through this right now on a vb.net app that I recently developed. In the meantime, I have developed a page that has a button which an Administrative user can click to stop the W3WP.exe process. It is a great stop gap measure until the problem code can be identified and updated. Here is the code I used. Just create a .aspx page with a button that call the following code on the corresponding .aspx.vb page. The code uses the command prompt to get the Tasklist and writes this to a file. I then parse the text file for the PID of the W3WP.exe worker process. Then, I access the command prompt programmatically again to terminate the W3WP.exe process using the PID.
Imports System.Web
Partial Class TAP
Inherits System.Web.UI.Page
Protected Sub btnStop_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles btnStop.Click
Call thStop_IIS()
End Sub
Function thStop_IIS()
Dim varRetval As String
Dim varPID As String
dim x as long
Dim savePath As String = Request.PhysicalApplicationPath + "exports\"
If Dir(savePath + "filename.txt") = "filename.txt" Then
Kill(savePath + "filename.txt")
End If
varRetval = Shell("cmd /c tasklist > " + savePath + "filename.txt")
For x = 1 To 90000000
Next x
varPID = thParse_File_Return_PID(savePath + "filename.txt")
varRetval = Shell("cmd /c taskkill /pid " & varPID & " /F")
Return True
End Function
Function thParse_File_Return_PID(ByVal varFileToParse As String) As Integer
On Error GoTo handler_err
'Dim FILE_NAME As String = "C:\Users\Owner\Documents\test.txt"
Dim FILE_NAME As String = varFileToParse
Dim TextLine As String
Dim varPID As Integer
Dim x As Long
If System.IO.File.Exists(FILE_NAME) = True Then
Dim objReader As New System.IO.StreamReader(FILE_NAME)
Do While objReader.Peek() <> -1
'TextLine = TextLine & objReader.ReadLine() & vbNewLine
TextLine = objReader.ReadLine() & vbNewLine
If InStr(TextLine, "w3wp.exe") Then
varPID = Mid(TextLine, 31, 4)
End If
Loop
thParse_File_Return_PID = varPID
Else
thParse_File_Return_PID = 0
End If
handler_exit:
Exit Function
handler_err:
'MsgBox(Err.Number & " " & Err.Description & ":" & "thParse_File_Return_Pages")
Resume
End Function
End Class

Related

vb.net multiple webproxy in httpwebrequest

I am currently working on a VB.net project where I need to get http responses from a certain URI but the requests needs to go through http proxy which I am perfectly fine with. The problem occurred when I realised sometimes our proxy servers are not working and then the application throws an error. I want my app to check whether the proxy is working or not, if not then I want it to take another proxy from the proxy list/array. And also, please feel free to share if you have any alternative ideas.
Currently I am using this (which is static and when it throws an error I need to manually change the proxy):
Dim proxyObject As WebProxy = New WebProxy("192.168.0.10:80")
request.Proxy = proxyObject
What I want is something like this:
If WebProxy("192.168.0.10:80") is working fine Then
Execute the response
Else
Take the next proxy address from the list/array and go back to the starting
of "If"
End If
FYI: my proxies doesn't require authentication.
I apologise if I couldn't explain it properly and to be honest I'm fairly new in VB.net.
Thanks a lot for your time and patience. Appreciate your help.
Borrowing from this question
Dim urlList As New List(Of String) 'Urls stored here
For each urlString as string in urlList
If CheckProxy(urlString) Then
'Execute the response
else
Continue For 'or something else here, mark it as bad?
End If
next
Public Shared Function CheckProxy(ByVal Proxy As String) As Boolean
Dim prx As Uri = Nothing
If Uri.TryCreate(Proxy, UriKind.Absolute, prx) Then
Return CheckProxy(prx)
ElseIf Uri.TryCreate("http://" & Proxy, UriKind.Absolute, prx) Then
Return CheckProxy(prx)
Else
Return False
End If
End Function
Public Shared Function CheckProxy(ByVal Proxy As Uri) As Boolean
Dim iProxy As Socket = New Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp)
iProxy.ReceiveTimeout = 500 : iProxy.SendTimeout = 500
Try
'' Connect using a timeout (1/2 second)
Dim result As IAsyncResult = iProxy.BeginConnect(Proxy.Host, Proxy.Port, Nothing, Nothing)
Dim success As Boolean = result.AsyncWaitHandle.WaitOne(500, True)
If (Not success) Then
iProxy.Close() : Return False
End If
Catch ex As Exception
Return False
End Try
Dim bytData() As Byte, strData As String
Dim iDataLen As Integer = 1024
strData = String.Format("CONNECT {0}:{1} HTTP/1.0{2}{2}", "www.google.com", 80, vbNewLine)
bytData = System.Text.ASCIIEncoding.ASCII.GetBytes(strData)
If iProxy.Connected Then
iProxy.Send(bytData, bytData.Length, SocketFlags.None)
ReDim bytData(1024)
Do
Try
iDataLen = iProxy.Receive(bytData, bytData.Length, SocketFlags.None)
Catch ex As Exception
iProxy.Close() : Return False
End Try
If iDataLen > 0 Then
strData = System.Text.ASCIIEncoding.ASCII.GetString(bytData)
Exit Do
End If
Loop
Else
Return False
End If
iProxy.Close()
Dim strAttribs() As String
strAttribs = strData.Split(" "c)
If strAttribs(1).Equals("200") Then
Return True
Else
Return False
End If
End Function

Can't share isolated storage file between applications in different app pools

I've got various web apps (containing WCF services) in IIS under the default website. As long as they are all running in the same app pool they can access a shared isolated storage file no problem.
However, once I move them to different app pools I get "System.IO.IsolatedStorage.IsolatedStorageException: Unable to create mutex" when one tries to access a file created by another. They are all running under NetworkService user. I tried GetUserStoreForAssembly and GetMachineStoreForAssembly all with the same result. Any ideas why they couldn't use a shared file?
I made sure to close the stream and even dispose it in case one was holding onto it, but I am running a simple test where one service writes it, then another tries to read from it later, and it always fails.
Also, I am accessing the isolated store from a signed assembly.
Does anybody have any ideas?
Here is the code:
Private Sub LoadData()
Dim filename = FullFilePath(_fileName)
Dim isoStorage As IsolatedStorageFile = IsolatedStorageFile.GetUserStoreForAssembly()
' Tried GetMachineStoreForAssembly, same failure
isoStorage.CreateDirectory(ROOT_DIRECTORY)
If (isoStorage.GetFileNames(filename).Length = 0) Then
Return
End If
Dim stream As Stream = New IsolatedStorageFileStream(filename, FileMode.OpenOrCreate, isoStorage)
If stream IsNot Nothing Then
Try
Dim formatter As IFormatter = New BinaryFormatter()
Dim appData As Hashtable = DirectCast(formatter.Deserialize(stream), Hashtable)
Dim enumerator As IDictionaryEnumerator = appData.GetEnumerator()
While enumerator.MoveNext()
Me(enumerator.Key) = enumerator.Value
End While
Finally
stream.Close()
stream.Dispose()
stream = Nothing
End Try
End If
End Sub
Public Sub Save()
Dim filename = FullFilePath(_fileName)
' Open the stream from the IsolatedStorage.
Dim isoFile As IsolatedStorageFile = IsolatedStorageFile.GetUserStoreForAssembly()
' Tried GetMachineStoreForAssembly, same failure
Dim stream As Stream = New IsolatedStorageFileStream(filename, FileMode.Create, isoFile)
If stream IsNot Nothing Then
Try
Dim formatter As IFormatter = New BinaryFormatter()
formatter.Serialize(stream, DirectCast(Me, Hashtable))
Finally
stream.Close()
stream.Dispose()
stream = Nothing
End Try
End If
End Sub
Looks like it was a trust issue.
After adding the assembly accessing the isolated storage file to the gac it magically worked as everything in the gac has full trust set automatically.
This works for me, but it might not always be an option to do this for other solutions. Check out the .NET Framework caspol utility if this is the case.
Hope this helps somebody! It was a huge pitafor me.

Enterprise Library 5.0 Connection Pool Timeout

I have an old web application written in ASP.Net 2.0 Web Forms. I use the Data Access Block in Enterprise Library and have recently updated to version 5.0. The application is tiered, ie, UI layer, Service Layer, Data Layer. It also uses SQL Server 2008 for the data storage.
I have recently noticed that the following error is appearing when I run the application and browse to particular pages.
Timeout expired. The timeout period elapsed prior to obtaining a connection from the pool. This may have occurred because all pooled connections were in use and max pool size was reached.
This tends to happen on pages that do a lot of separate reads from the database, maybe up to as many as 20 on one page.
Below shows snippets of my Data Access Class.
Public Class DataAccess
' create a private instance of the database factory
Private db As Database = DatabaseFactory.CreateDatabase()
Public Function ExecuteNonQuery(ByVal params() As SqlParameter, ByVal strSproc As String) As Integer
Dim intReturnValue As Integer = 0
Dim i As Integer
Dim cmd As DbCommand
cmd = db.GetStoredProcCommand(strSproc)
cmd.CommandTimeout = 120
For i = 0 To params.Length - 1
db.AddInParameter(cmd, params(i).ParameterName.ToString, params(i).DbType, params(i).Value)
Next
db.AddParameter(cmd, "return_value", DbType.Int32, ParameterDirection.ReturnValue, "", DataRowVersion.Default, 0)
db.ExecuteNonQuery(cmd)
intReturnValue = Int32.Parse(db.GetParameterValue(cmd, "#return_value"))
Return intReturnValue
End Function
Public Function ExecuteDataReader(ByVal params() As SqlParameter, ByVal SProc As String) As SqlDataReader
Dim i As Integer
Dim dr As SqlDataReader = Nothing
Dim cmd As DbCommand
cmd = db.GetStoredProcCommand(SProc)
cmd.CommandTimeout = 120
For i = 0 To params.Length - 1
db.AddInParameter(cmd, params(i).ParameterName.ToString, params(i).DbType, params(i).Value)
Next
dr = TryCast(DirectCast(db.ExecuteReader(cmd), RefCountingDataReader).InnerReader, SqlDataReader)
Return dr
End Function
Throughout my code, once I have finished with an SqlDataReader I always do something like this
If Not (drSource Is Nothing) Then
drSource.Close()
End If
Is there anything you folk can see that I am missing? Does it look like my code could be leaking connections or not closing properly?
I always thought the Garbage collector got rid of any open connections.
Any feedback or help would be greatly appreciated.
Thanks.
Your code is closing the data reader, but not the data connection associated with it.
Since your data reader is a SqlDataReader, it has a Connection property. You should be able to use that to close and dispose of the connection.

Load page then process rows in Asp.net

I have a webpage that is a site monitoring tool for my company. Basically, it pulls a list of 150 IP addresses from a database, and checks if the webpage loads for them. This takes about 15 minutes to load, I'd like it to load the list and go 1 by 1 and update the status with text or an icon.
Here is my Function block, any way to thread this or help me get to what I need to get to?
Function SiteMonitorResults(ByVal WebAddress As String)
Try
'Code Example
Dim httpReq As HttpWebRequest = DirectCast(WebRequest.Create(WebAddress), HttpWebRequest)
httpReq.AllowAutoRedirect = False
Dim httpRes As HttpWebResponse = DirectCast(httpReq.GetResponse(), HttpWebResponse)
' Close the response.
httpRes.Close()
' Code for NotFound resources goes here.
If httpRes.StatusCode = HttpStatusCode.OK Then
Return "Online"
Else
Return "Offline"
End If
Catch ex As Exception
Return "Unknown"
End Try
End Function
Basically, I would go for something like this, using System.Threading.Tasks and System.Net.Http
( sorry for C# code )
I left out try catch for readability, but they are required, or the code will crash on the first DNS problem (for example)
public string CheckAddresses(List<string> addresses)
{
List<string> result = new List<string>();
List<Task> tasks = new List<Task>();
addresses.ForEach(address =>
{
var task = new HttpClient().GetAsync(address).ContinueWith(
res => result.Add(String.Format("{0} : {1}", address, res.Result.IsSuccessStatusCode)));
tasks.Add(task);
});
Task.WaitAll(tasks.ToArray());
return string.Join(", ", result.ToArray());
}
Hope this will help

Sharepoint not getting current user NetworkCredential from active directory

we have strange problem, we have single signon and we are trying to fetch unread email count from Exchange ews webservice, the problem is it always gets same count for all user which is actually for server user.
'it should now get for current user who requested the page
'but its always for server user where sharepoint is installed
Public Sub GetUnreadEmailCount()
Dim errormsg As String = String.Empty
Dim UnreadCount As Integer = 0
Dim esb As New ExchangeServiceBinding
Try
esb.RequestServerVersionValue = New RequestServerVersion
esb.RequestServerVersionValue.Version = ExchangeVersionType.Exchange2007_SP1
esb.UseDefaultCredentials = True
esb.Url = Domain + "/EWS/Exchange.asmx"
ServicePointManager.ServerCertificateValidationCallback = New RemoteCertificateValidationCallback(AddressOf CertificateValidationCallBack)
Dim biArray(1) As BaseFolderIdType
Dim dfFolder As New DistinguishedFolderIdType
dfFolder.Id = DistinguishedFolderIdNameType.inbox
biArray(0) = dfFolder
Dim geGetFolder As New GetFolderType
geGetFolder.FolderIds = biArray
geGetFolder.FolderShape = New FolderResponseShapeType
geGetFolder.FolderShape.BaseShape = DefaultShapeNamesType.AllProperties
Dim gfResponse As GetFolderResponseType = esb.GetFolder(geGetFolder)
Dim rmta As ResponseMessageType() = gfResponse.ResponseMessages.Items
Dim rmt As FolderInfoResponseMessageType = DirectCast(rmta(0), FolderInfoResponseMessageType)
If rmt.ResponseClass = ResponseClassType.Success Then
Dim folder As FolderType = DirectCast(rmt.Folders(0), FolderType)
UnreadCount = folder.UnreadCount
End If
Label1.Text = vbCrLf + "Unread email count : " + UnreadCount.ToString
' Return UnreadCount
Catch ex As Exception
If Not ex.Message Is Nothing Then errormsg = ex.Message
Try
If Not ex.InnerException.Message Is Nothing Then errormsg = errormsg + " : " + ex.InnerException.Message
Catch e As Exception
End Try
Finally
If esb IsNot Nothing Then esb.Dispose() : esb = Nothing
If Not errormsg = String.Empty Then
Label1.Text = vbCrLf + "Error : " + errormsg
End If
End Try
End Sub
We were actually having the same problem, although we were not using single sign on. So I'm not sure this is exactly what you are experiencing.
The problem is that you can not have a user on Machine A give their credentials to Machine B (SharePoint?) and then have Machine B send those credentials on to Machine C
It's referred to as the "Double Hop" problem and is a security feature, however I'm not really into the technical side of it. Our solution was to use Kerberos.
I hope this helps you, if not, that it helps you rule out this specific issue :)
Your server side code is running as the AppPool's identity, which is your sharepoint service account. I'm assuming that's what you mean by 'the server user.'
esb.UseDefaultCredentials = true;
will use the creds of the context. I'm not sure of what's available in the EWS services, so if you can use a higher-privileged account and get based on the user coming in, i.e., HttpContext.Current.User.Identity as a parameter, then that may be the best way.
You could authenticate via javascript directly to the EWS services, skipping server-side code altogether, and write something that consumes & displays the server stuff as you need it.
You'd need to find a way to auth the user directly to the EWS services. Double-hop is an issue with NTLM, as your NTLM ticket is only valid for the first hop. Kerberos fixes that, but you still need to impersonate.

Resources