4

In our Outlook COM add-in, we're making an API call to our server using the .NET HTTPWebRequest method. One of our customers is running into a System.Net.WebException with the message The remote name could not be resolved: 'mydomain.com' and WebExceptionStatus.NameResolutionFailure as the status. All the users from this particular customer's company are using outlook/the addin from behind a VPN so we are piggy-backing proxy configuration from IE in order to make the request.

Our API calls work for a period of time but then it randomly disconnects and then does not allow future requests to go through either. Once the users close and restart Outlook though, it seems to work just fine again without changing any network configuration or reconnecting to wifi etc.

Other posts like this suggested retrying with a sleep in between. We have added a retry mechanism with 3 attempts, each with a sleep in between but that has not resolved the intermitent issue.

Our domain is hooked up to an AWS Classic Load Balancer so mydomain.com actually resolves a CNAME record to an AWS static domain, pointing to the ELB. I'm not sure if that would have any impact on the request or routing it.

The strange part is we also have a web browser component that loads a web page in a sidebar from the exact same domain as the API calls. It works perfectly and loads a URL from the same domain. The users can also load the URL in their browsers without any issues. It just appears that the HTTPWebRequest is running into the domain resolution issue. We've checked that it's not just a matter of a weak wifi signal. Since they are able to use IE which has the same proxy config to access the site just fine, I don't think it's that.

We're at a loss for how to gracefully recover and have the request try again. I've looked into suggestions from this answer and this other answer, we'll be trying those next. Unfortunately, we are not able to make the requests use direct IP addresses as some of the other answers suggest. That also eliminates the ability to edit the hosts file to point straight to it. The reason is we can't assign a static IP on a classic ELB.

We're considering trying to set the host to use the CNAME record from AWS directly but this is going to cause SSL errors as it doesn't have a valid cert for the CNAME entry. Is there a way to get around that by masking it via a header, similar to the IP approach?

Feel free to ask for more information, I will do my best to provide it.

Any suggestions on what to try / troubleshoot are welcome!

Update: We’re targeting .NET v4.5

Here's the code

var result = string.Empty;
bool retrying = false;
int retries = 0;
HttpWebRequest webRequest = null;

try
{
    ServicePointManager.ServerCertificateValidationCallback =
        CertificateCheck;
    ServicePointManager.MaxServicePoints = 4;
    ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;

retry:
    webRequest = (HttpWebRequest)WebRequest.Create(uriParam);
    webRequest.Timeout = 120000;
    webRequest.Method = "POST";
    webRequest.ContentType = "application/x-www-form-urlencoded";
    webRequest.Accept = acceptParam;
    webRequest.Headers.Add("Cookie", cookieParam);
    webRequest.UseDefaultCredentials = true;
    webRequest.Proxy = null;
    webRequest.KeepAlive = true; //default
    webRequest.ServicePoint.ConnectionLeaseTimeout = webRequest.Timeout;
    webRequest.ServicePoint.MaxIdleTime = webRequest.Timeout;
    webRequest.ContentLength = dataParam.Length;

    using (var reqStream = webRequest.GetRequestStream())
    {
        reqStream.Write(dataParam, 0, dataParam.Length);
        reqStream.Flush();
        reqStream.Close();
    }

    try
    {
        using (WebResponse webResponse = webRequest.GetResponse())
        {
            using (var responseStream = webResponse.GetResponseStream())
            {
                if (responseStream != null)
                {
                    using (var reader = new StreamReader(responseStream))
                    {
                        result = reader.ReadToEnd();
                    }
                }
            }
            webResponse.Close();
        }

    }
    catch (WebException)
    {
        if (retrying && retries == 3)
        {
            //don't retry any more
            return string.Empty;
        }

        retrying = true;
        retries++;
        webRequest.Abort();
        System.Threading.Thread.Sleep(2000);
        goto retry;
    }
}
catch (Exception ex)
{
    Log.Error(ex);
    result = string.Empty;
}
finally
{
    webRequest?.Abort();
}

return result;
Danesh
  • 145
  • 7
  • Can you post the code you're referring to? Also, the .Net Framework you're currently targeting. – Jimi Apr 27 '19 at 00:56
  • @Jimi Updated the question. It's targetting .NET v4.5 – Danesh Apr 29 '19 at 15:02
  • It's hard to say something specific that would solve the problem at hand, because I would change almost everything you're doing now; for other settings, I ignore the meaning: for example `.UseDefaultCredentials = true;`. Is this service using NTLM/Kerberos authentication? Anyway, I'ld suggest to remove all the *retry* stuff and this: `ServicePointManager.MaxServicePoints = 4;`. Does the service use some load balancer? You don't know? Well, in any case set `webRequest..AllowAutoRedirect = true;`, so you don't need to handle redirection (quite common) *manually*. Leave the default Timeout. – Jimi Apr 29 '19 at 17:42
  • Remove all `webRequest.Abort();` things. Add a `CookieContainer` to the WebRequest. I have no idea if trying to set a cookie in the header of the WebRequest is a requirement. Add `webRequest.Headers.Add(HttpRequestHeader.CacheControl, "no-cache");`. Add `webRequest.ServicePoint.Expect100Continue = false;` Possibly, update to .Net Framework 4.7.1+. The new versions of the Framework contain many improvements and are much more reliable. You'ld notice more if you were using HttpClient instead of WebRequest, but it applies to the latter in any case. – Jimi Apr 29 '19 at 17:42
  • Also, note that (I don't know if this is the case, just a general information) when you POST something, you might get redirected. You need to read the StatusCode of the response: if it's `HttpStatusCode.Moved`, `HttpStatusCode.Found` (common) or `HttpStatusCode.RedirectMethod`, you might have to follow the redirection *manually*: the redirected endpoint in passed in the `Location` header. This always apply if you need to authenticate the request before being redirected to the actual EndPoint (the service that provides the API) or a LandingPage. – Jimi Apr 29 '19 at 17:53
  • (I'm a colleague of the OP) AllowAutoDirect defaults to true anyway. Note that these problems occurred previously without the Abort() method being called for failed requests, and also without setting MaxServicePoints (which was suggested in other SO posts). Is a CookieContainer necessary in addition to just adding a cookie to the Headers colllection? But the core question should be what is a "minimal" HttpWebRequest that's guaranteed to work across all network environments? The current issues are primarily occurring in a small number of environments and working well for the majority of users – Eric Legault May 01 '19 at 17:56

0 Answers0