Failed HttpWebRequest to an HTTPS server takes 100% of CPU

J

jgraumann

We have a CE 5.0 application (.NET CF 2.0) that we use to download files from
both secure and non-secure servers over HTTP and HTTPS. When making a request
to a misconfigured server over HTTPS, we noticed that the CPU on our device
would jump to 100% and stay there indefinately. We traced this to a thread
that appears to be created when calling HttpWebRequest.GetResponse().

It appears that when we make a request to this specific server, the TCP
connection gets established and the client sends its "Client Hello" message
to the server. The server immediately closes the connection (you can see the
FIN-ACK from the server over Wireshark). Here is where the problem occurs:

If you are making this request from a client PC using the full .NET
framework, the client completes the handshake by sending its own FIN and the
connection is closed. HttpWebRequest.GetResponse() returns immediately with
an error stating that the server closed the connection.

However, if you are making this request from a CE 5.0 client running the
compact framework, the connection never gets closed from the client side.
Instead, HttpWebRequest.GetResponse() does not return until the specified
timeout expires. In addition to this, even after calling Abort() on the
request object, a thread is leftover that seems to spin forever and takes up
100% of the CPU.

This does not appear to happen when making non-HTTPS requests, nor when
making requests to a properly configured server or a server that is not
listening on port 443 at all.

I was able to recreate this easily by writing a simple test server that runs
on Windows XP (in C++) and test client for CE in C#. The test server listens
for a TCP connection on a port, accepts a connection, receives one packet and
then closes the socket. The test client simply creates an HttpWebRequest
object for the specified server and port and calls GetResponse(). Every time
(repeatable on more than one device and more than one computer hosting the
server) the call to GetResponse() will result in a spawned thread that eats
up the CPU. Again, calling Abort() on the request object does not seem to
clean things up.

Does anyone have any ideas? I have pasted the code below, but can send the
solution files if required.

================================================
SimpleTCPServer (C++ Win32)
================================================

#include <winsock2.h>
#include <windows.h>
#include <stdio.h>

#define LISTEN_PORT 9091

int _tmain (int argc, TCHAR* argv[])
{
WSADATA wsaData;
SOCKET sock = INVALID_SOCKET;
sockaddr_in local;
SOCKET client;
sockaddr_in from;
int fromlen=sizeof(from);
char buf[512];

// initialize WinSock
if(WSAStartup(MAKEWORD(2,2), &wsaData))
{
printf("WSAStartup() failed\r\n");
return 0;
}

while(true)
{
// create socket
local.sin_family=AF_INET;
local.sin_addr.s_addr=INADDR_ANY;
local.sin_port=htons((u_short)LISTEN_PORT);
sock = socket(AF_INET, SOCK_STREAM, 0);
if(sock == INVALID_SOCKET)
{
printf("socket() failed\r\n");
WSACleanup();
return 0;
}

// bind
if(bind(sock, (sockaddr*)&local, sizeof(local))!=0)
{
printf("bind() failed\r\n");
closesocket(sock);
WSACleanup();
return 0;
}

//listen
if (listen(sock, 5) != 0)
{
printf("listen() failed\r\n");
closesocket(sock);
WSACleanup();
return 0;
}

// accept the connection
printf("Waiting for client to connect on port %d...\r\n", LISTEN_PORT);
client = accept(sock, (struct sockaddr*)&from, &fromlen);
printf("Accepted TCP connection from %s\r\n", inet_ntoa(from.sin_addr));

// receive the first packet
int bytes = recvfrom(client, buf, sizeof(buf), 0, (struct
sockaddr*)&from, &fromlen);
printf("Received %d bytes from client\r\n", bytes);

// close the connection
printf("Closing connection\r\n\r\n", bytes);
closesocket(client);
shutdown(sock, SD_BOTH);
closesocket(sock);
}

WSACleanup();
return 0;
}

================================================
SSLClient (CE 5.0, .NET CF 2.0) (just a blank form with a "Send" button on it)
================================================
...
private const string SERVER = "192.168.1.92";
private const int PORT = 9091;

private void SendSSLRequest()
{
HttpWebRequest request = null;
HttpWebResponse response = null;

try
{
request = (HttpWebRequest)(WebRequest.Create("https://" +
SERVER.ToString() + ":" + PORT.ToString() + "/"));
request.Timeout = 5000;
request.ReadWriteTimeout = 5000;
response = (HttpWebResponse)request.GetResponse();
}
catch (Exception ex)
{
}
finally
{
if (response != null)
{
response.Close();
response = null;
}
if (request != null)
{
request.Abort();
request = null;
}
}
}
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top