Http Client Protocol Issues

If you use any of these solutions, Please let me know so I can track if any of this is useful to you! Thanks! This is an area to share observations I have made working with Http Client Protocols and the associated technologies. I currently work for the Microsoft team that supports the WinInet, WinHTTP and System.Net API's and classes associated with these technologies. This is not a replacement for Microsoft Support, but an area to discuss these technologies. These postings are provided "AS IS" with no warranties, and confer no rights. Use of included code samples are subject to the terms specified at Microsoft - Information on Terms of Use

  • Understanding Connection Limits and New Proxy Connection Limits in WinInet and Internet Explorer

    Because of RFC 2616 section 8.1.4 (http://www.w3.org/Protocols/rfc2616/rfc2616-sec8.html) we have traditionally limited the number of persistent connections to 2 per server.  This is because of the strong language in this RFC: " A single-user client SHOULD NOT maintain more than 2 connections with any server or proxy."

    Practically speaking with today's modern web servers, there is no reason for such a small number of client connections to a server.  This is especially true with the rich WebPages and WebBased applications hosted in WebPages.  Indeed, at times you can get into a situation where you are attempting to download resources for a WebPage and a script is executed to download additional data giving the appearance of a web browser.  The ability to download several resources simultaneously has huge advantages when it comes to downloading resources quickly and gives the ability to provide a more responsive web experience.

    While you could always change the number of connections WinInet (and Internet Explorer) used by default, in the past, you could not also control the number of Proxy Connections separately (also covered in RFC 2616).  There is now a setting that will allow you to set the proxy connection limit independent from the number of persistent connections to a server.

    Here is a brief Q&A I put together to help you get your head around these settings:

    What does setting the max Server and Proxy connections do?  This limits the number of possible connection at one time to a given host either direct or through a proxy.

    What happens if I try to make 4 connections and the default number of persistent connections (proxy or server) is set to 2?  Two connections will be made and two will be put in a queue to ‘wait’ for an available connection to be freed.  Once one of the first two connections gets a response, another ‘waiting’ connection will use that available connection in a FIFO manner.

    If my app has 2 requests to one host and 2 to another, does that mean only 2 connections at a time will be used total, and there will be two connections waiting?  No, this means that 2 connections per host will be used so in this example there will be no connections waiting.

    Why limit the proxy connections in WinInet to begin with?  We always have as a function of limiting the HTTP connections to the end point server.  We did this because the RFC 2616 tells us to:
    http://www.w3.org/Protocols/rfc2616/rfc2616-sec8.html  (see 8.1.4 Practical Considerations)

    Why add a new limit for the proxy connections?  In the event you need to customize the number of proxy connections.  The default is set to 4 which is enough to satisfy the defaults for WinInet which is 2 for HTTP 1.1 servers and 4 for HTTP 1.0 servers. 

    What was it before?  The value did not exist before so it is the same as the server settings.  If the proxy was Http 1.0 it was 4.  If it was http 1.1 it was 2. 

    What if I want to have more server connections, should I also set the MaxConnections to proxy to follow this?  No!  The number of proxy connections will automatically follow the MaxConnections for the server, your application should probably do the same.  In most cases (unless your proxy has some other strange requirement) it should be the same as your server connections.  If for some reason you want this different then set the MaxConnectionsPerProxy manually.  If you do not have a reason for this to be different then do not use this setting.

    How do I set the MaxConnectionsPerProxy?  Either in Code (see this blog: http://blogs.msdn.com/jpsanders/archive/2009/06/08/understanding-the-new-wininet-option-internet-option-max-conns-per-proxy.aspx) or with this registry setting:

    Important This section, method, or task contains steps that tell you how to modify the registry. However, serious problems might occur if you modify the registry incorrectly. Therefore, make sure that you follow these steps carefully. For added protection, back up the registry before you modify it. Then, you can restore the registry if a problem occurs. For more information about how to back up and restore the registry, click the following article number to view the article in the Microsoft Knowledge Base:

    322756  (http://support.microsoft.com/kb/322756/) How to back up and restore the registry in Windows

    To change the number of files that you can download through your proxy at one time to 2, follow these steps:

    1.     Start Registry Editor.

    2.     Locate the following key in the registry:
    HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Internet Settings

    3.     On the Edit menu, point to New, click DWORD Value, and then add the following registry values:
    Value name: MaxConnectionsPerProxy
    Value data: 2
    Base: Decimal

    4.     Exit Registry Editor.

     

    Let me know if this article was useful to you!

  • InfoPath form and other xml Office documents do not open from Internet Explorer - Raw XML displays instead

    I came across this problem.  The key to this was that the documents opened fine when clicking on them, so I knew the Office installation itself was fine.  Some rogue application on install or removal had apparently removed the appropriate Content Type registry key so Internet Explorer was unable to associate the XML file extension with any application.  Restoring the default OS registry entry allowed everything to start functioning again.  Warning, modify the registry at your own risk.  Microsoft cannot guarantee that problems resulting from the incorrect use of Registry Editor can be solved. Use Registry Editor at your own risk.

    Windows Registry Editor Version 5.00

    [HKEY_CLASSES_ROOT\.xml]
    "Content Type"="text/xml"

    Why did this rogue deletion of this registry key affect IE but not the InfoPath application itself?

    Since all the registry entries for the application were correct the associations in the registry for opening the file are in place.  The issue is that with the 'Content Type' entry missing, Internet Explorer (more precisely Urlmon used by IE) is unable to determine what to do with this particular Content Type (XML). When this key is correct, this MIME type is found in the MIME Database registry location and the appropriate application can be invoked.

    Since Office is installed the file will be passed to MSOXMLED.EXE which will determine the correct application to invoke by looking inside the XML for the appropriate entry indicating what application that particular XML is associated with.  For example: <?mso-application progid="Word.Document"?> indicates that this is a Word Document.

    Let me know if this blog helped you by dropping me a note! 

     

  • Some .chm files do not work on Windows 7 - Process Monitor - Navigation to the webpage was canceled

    This took me a couple of minutes to find so I thought I would share this here.

    I downloaded ProcMon (process monitor) and could view the help file.  It would open but the right pane had a message: "Navigation to the webpage was canceled"  The left pane was functional however.

    I found the help file.  This is a .CHM file stored in the directory where Procmon was installed.

    To be able to use is, Right Click on the procmon.chm file and in the general tab, click on Unblock!

    Works fine now!

     Let me know if you found this useful by dropping me a note please!

  • WWSAPI samples in Windows 7 SDK for RC: "Unable to add URL to HTTP URL group."

    I like to build and run with UAC on.  When running the HttpCalculatorService example I got this error: 

     Failure: errorCode=0x80070005
    Unable to add URL to HTTP URL group.
    Access is denied.

    Running from an administrator command prompt I do not get the error!

    Obviously I do not have permissions to something.  Some investigation revealed that I need to add the user I wanted to run as to the urlacl (http://msdn.microsoft.com/en-us/library/cc307223(vs.85).aspx)

    So this is the command that allowd me to run (replace myDOMAIN\myUSERNAME with the domain and user you wish to run the Service under): netsh http add urlacl url=http://+:80/example user=myDOMAIN\myUSERNAME.

    To do this in code you could use this API: http://msdn.microsoft.com/en-us/library/aa364503(VS.85).aspx but the code your are running would need to have the necessary permissions so you are in kind of a chicken and the egg situation!

    Drop me a comment and let me know if this helped you please! 

     

  • Understanding the new WinInet options: INTERNET_SUPPRESS_COOKIE_PERSIST and INTERNET_SUPPRESS_COOKIE_PERSIST_RESET

    These options are well documented.  Important notes:

    These flags affect the process that you set this option from. 

    You do not need to pass an InternetHandle (but no error if you do).

    Sample code:

    bool abRes = false;
    DWORD adOption = INTERNET_SUPPRESS_COOKIE_PERSIST;
    abRes= InternetSetOption(hInternet, INTERNET_OPTION_SUPPRESS_BEHAVIOR ,&adOption,
    sizeof(adOption));
    adOption = INTERNET_SUPPRESS_COOKIE_PERSIST_RESET;
    abRes= InternetSetOption(hInternet, INTERNET_OPTION_SUPPRESS_BEHAVIOR ,&adOption,
    sizeof(adOption));

     

  • Understanding the new WinInet option: INTERNET_OPTION_MAX_CONNS_PER_PROXY

    The documentation on this is very straight forward (http://msdn.microsoft.com/en-us/library/aa385328(VS.85).aspx).  At the time of publishing however the documentation has a slight error.  You cannot pass a handle in for the first argument.  It must be NULL or the call will return false and GetLastError() will show the error is: ERROR_INTERNET_INVALID_OPERATION.

     The default value for this option is 4 connections.

     Also see this post: http://blogs.msdn.com/jpsanders/archive/2009/06/29/understanding-connection-limits-and-new-proxy-connection-limits-in-wininet-and-internet-explorer.aspx

    Let me know if this article was useful to you!

  • Understanding the new WinInet option INTERNET_OPTION_SERVER_CERT_CHAIN_CONTEXT

    With the release of Internet Explorer 8 comes a new option you can query for with programming with the WinInet APIs: INTERNET_OPTION_SERVER_CERT_CHAIN_CONTEXT.  The MSDN documentation tells you it allows you to get the PCCERT_CHAIN_CONTEXT, and not much more.  It does not show you how to properly get the Context.  I like to see things in action so here is some sample code.

    This option is documented here: http://msdn.microsoft.com/en-us/library/aa385328(VS.85).aspx (note that if you are not using the latest SDK headers, the value for this option is also documented here).

    To sum it up, use this option to get the Server Certificate Chain Context  to use with other Crypto API functions  To understand this option you can modify the HttpDump example in the Platforms SDK and see how this option can be used.   Since this is an option for the request you get this option using the request handle after you execute the request.  I am not a Crypto API guru so I stole some code examples from MSDN just to dump out some information about the Certificate Chain. 
     
    The most important (and undocumented) part of getting this option is to pass a Pointer to a  PCCERT_CHAIN_CONTEXT.  Below is a code snippet that I added to the HttpDump sample in the Windows 7 SDK (RC).

    First, there is a problem with that sample when compiled for unicode.  I fixed it with this replacement code:

    C++ code listing for sample (Copy Code):

    dwSize=0;
    TCHAR *myBuff = NULL;

    // First time we will find out the size of the headers.
    HttpQueryInfo (hReq,HTTP_QUERY_RAW_HEADERS_CRLF, NULL, &dwSize, NULL);
    myBuff =
    new TCHAR [dwSize+1];
    // Now we call HttpQueryInfo again to get the headers.
    if (!HttpQueryInfo (hReq,HTTP_QUERY_RAW_HEADERS_CRLF, (LPVOID) myBuff,
    &dwSize, NULL))
    {
        ErrorOut (GetLastError(), TEXT(
    "HttpQueryInfo"));
    }
    else
    {
        *(myBuff + dwSize) =
    '\0';
        wcout << myBuff << endl;
    }

    delete myBuff;

    Next I added this code just before closing the request handle ( 'if (!InternetCloseHandle (hReq) )' ).

    C++ code listing for sample (Copy Code):

    PCCERT_CHAIN_CONTEXT CertCtx=NULL;
    DWORD cbCertSize =
    sizeof(&CertCtx);

    //GetCertificate information
    if (InternetQueryOption(hReq,INTERNET_OPTION_SERVER_CERT_CHAIN_CONTEXT ,(LPVOID)&CertCtx,&cbCertSize))
    {
    //---------------------------------------------------------------
    // Display some of the contents of the chain.
    PCCERT_CHAIN_CONTEXT pChainContext=CertCtx;
    printf(
    "The size of the chain context is %d. \n",pChainContext->cbSize);
    printf(
    "%d simple chains found.\n",pChainContext->cChain);
    printf(
    "\nError status for the chain:\n");
    switch(pChainContext->TrustStatus.dwErrorStatus)
    {
    case CERT_TRUST_NO_ERROR :
    printf(
    "No error found for this certificate or chain.\n");
    break;
    case CERT_TRUST_IS_NOT_TIME_VALID:
    printf(
    "This certificate or one of the certificates in the certificate chain is not time-valid.\n");
    break;
    case CERT_TRUST_IS_NOT_TIME_NESTED:
    printf(
    "Certificates in the chain are not properly time-nested.\n");
    break;
    case CERT_TRUST_IS_REVOKED:
    printf(
    "Trust for this certificate or one of the certificates in the certificate chain has been revoked.\n");
    break;
    case CERT_TRUST_IS_NOT_SIGNATURE_VALID:
    printf(
    "The certificate or one of the certificates in the certificate chain does not have a valid signature.\n");
    break;
    case CERT_TRUST_IS_NOT_VALID_FOR_USAGE:
    printf(
    "The certificate or certificate chain is not valid in its proposed usage.\n");
    break;
    case CERT_TRUST_IS_UNTRUSTED_ROOT:
    printf(
    "The certificate or certificate chain is based on an untrusted root.\n");
    break;
    case CERT_TRUST_REVOCATION_STATUS_UNKNOWN:
    printf(
    "The revocation status of the certificate or one of the certificates in the certificate chain is unknown.\n");
    break;
    case CERT_TRUST_IS_CYCLIC :
    printf(
    "One of the certificates in the chain was issued by a certification authority that the original certificate had certified.\n");
    break;
    case CERT_TRUST_IS_PARTIAL_CHAIN:
    printf(
    "The certificate chain is not complete.\n");
    break;
    case CERT_TRUST_CTL_IS_NOT_TIME_VALID:
    printf(
    "A CTL used to create this chain was not time-valid.\n");
    break;
    case CERT_TRUST_CTL_IS_NOT_SIGNATURE_VALID:
    printf(
    "A CTL used to create this chain did not have a valid signature.\n");
    break;
    case CERT_TRUST_CTL_IS_NOT_VALID_FOR_USAGE:
    printf(
    "A CTL used to create this chain is not valid for this usage.\n");
    }
    // End switch

    printf("\nInfo status for the chain:\n");
    switch(pChainContext->TrustStatus.dwInfoStatus)
    {
    case 0:
    printf(
    "No information status reported.\n");
    break;
    case CERT_TRUST_HAS_EXACT_MATCH_ISSUER :
    printf(
    "An exact match issuer certificate has been found for this certificate.\n");
    break;
    case CERT_TRUST_HAS_KEY_MATCH_ISSUER:
    printf(
    "A key match issuer certificate has been found for this certificate.\n");
    break;
    case CERT_TRUST_HAS_NAME_MATCH_ISSUER:
    printf(
    "A name match issuer certificate has been found for this certificate.\n");
    break;
    case CERT_TRUST_IS_SELF_SIGNED:
    printf(
    "This certificate is self-signed.\n");
    break;
    case CERT_TRUST_IS_COMPLEX_CHAIN:
    printf(
    "The certificate chain created is a complex chain.\n");
    break;
    case CERT_TRUST_HAS_PREFERRED_ISSUER:
    printf(
    "The certificate chain has a preferred issuer.\n");
    break;
    case CERT_TRUST_HAS_ISSUANCE_CHAIN_POLICY:
    printf(
    "The certificate chain has issuance chain policy.\n");
    break;
    case CERT_TRUST_HAS_VALID_NAME_CONSTRAINTS:
    printf(
    "The certificate chain valid name contraints.\n");
    break;
    case CERT_TRUST_IS_PEER_TRUSTED:
    printf(
    "The certificate chain is peer trusted.\n");
    break;
    case CERT_TRUST_HAS_CRL_VALIDITY_EXTENDED:
    printf(
    "The certificate chain has CRL validity extended.\n");
    break;
    case CERT_TRUST_IS_FROM_EXCLUSIVE_TRUST_STORE:
    printf(
    "The certificate chain was found in a store specified by hExclusiveRoot or hExclusiveTrustedPeople.\n");
    break;
    }
    // end switch

    CERT_SIMPLE_CHAIN *simpleCertificateChainWithinContext = NULL;
    for (int i=0; i<pChainContext->cChain; i++)
    {
        simpleCertificateChainWithinContext=pChainContext->rgpChain[i];
        // for each certificate chain in this context...
       
    for (int simpleCertChainIndex = 0;
        simpleCertChainIndex < simpleCertificateChainWithinContext->cElement;
        simpleCertChainIndex++)
        {

            // get the CertContext from the array
            PCCERT_CONTEXT pCertContext =
            simpleCertificateChainWithinContext->rgpElement[simpleCertChainIndex]->pCertContext;

            //-------------------------------------------------------------------
           
    // Find and print the name of the subject of the certificate
           
    // just retrieved.
           
    TCHAR pszNameString[256]; 
            
    if(CertGetNameString(
            pCertContext,
            CERT_NAME_SIMPLE_DISPLAY_TYPE,
            0,
            NULL,
            pszNameString,
            128))
            {
                wprintf(L
    "Certificate for %s has been retrieved.\n", pszNameString);
            }
           
    else
           
    {
                wprintf(L
    "CertGetName failed. \n");
            }

            // Get the issuer now...
           
    if(CertGetNameString( pCertContext,
            CERT_NAME_SIMPLE_DISPLAY_TYPE,
            CERT_NAME_ISSUER_FLAG,
            NULL,
            pszNameString,
            128))
            {
                wprintf(L
    "Certificate issuer is %s.\n\n", pszNameString);
            }
            else
            {
                wprintf(L
    "CertGetName failed. \n");
            }
        }
    }

    //important! Free the CertCtx
    CertFreeCertificateChain(CertCtx);
    }
    else
    {
      ErrorOut (GetLastError(), TEXT(
    "HttpQueryInfo"));
    }

    The above code simply illustrates getting the CertContext.  I will leave the actual implementation and usage of this context to all of you Crypto API types!  Please drop me a message if you found this blog useful!

     

  • Understanding the New WinInet flag: INTERNET_COOKIE_HTTPONLY

    There are a couple of new Cookie flags introduced with the Internet Explorer 8 WinInet.dll.  The INTERNET_COOKIE_HTTPONLY flag allows you to read the HttpOnly cookies in your WinInet Code.  This flag is documented here: http://msdn.microsoft.com/en-us/library/aa384714(VS.85).aspx.  As always, I like to see examples of how this flag works!

    Here is a sample ASPX page to create some standard and httponly cookies:

    aspx code listing for sample (Copy Code):

    <%@ Page Language="C#" %>


    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
        "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
    <script runat="server">
        void Page_Load(object sender, EventArgs e)
        {
            // Create a new HttpCookie.
            HttpCookie myHttpCookie = new HttpCookie("LastVisit", "sometime");
     myHttpCookie.Expires = DateTime.Now.AddYears(1);

            // By default, the HttpOnly property is set to false
            // unless specified otherwise in configuration.

            myHttpCookie.Name = "MyHttpCookie";
     myHttpCookie.Path = "/";
            Response.AppendCookie(myHttpCookie);

            // Show the name of the cookie.
            Response.Write(myHttpCookie.Name);

            // Create an HttpOnly cookie.
            HttpCookie myHttpOnlyCookie = new HttpCookie("LastVisit", "sometime later");

            // Setting the HttpOnly value to true, makes
            // this cookie accessible only to ASP.NET.

            myHttpOnlyCookie.HttpOnly = true;
     myHttpCookie.Expires = DateTime.Now.AddYears(1);
            myHttpOnlyCookie.Name = "MyHttpOnlyCookie";
            Response.AppendCookie(myHttpOnlyCookie);

            // Show the name of the HttpOnly cookie.
            Response.Write(myHttpOnlyCookie.Name);
            Response.Write("jeff");
        }
    </script>


    <html  >
    <head runat="server">
        <title>ASP.NET Example</title>
    </head>
    <body>
    <script type="text/javascript">
    function getCookie(NameOfCookie)
    {
        if (document.cookie.length > 0)
    {
        begin = document.cookie.indexOf(NameOfCookie+"=");
        if (begin != -1)
       {
        begin += NameOfCookie.length+1;
          end = document.cookie.indexOf(";", begin);
          if (end == -1) end = document.cookie.length;
          return unescape(document.cookie.substring(begin, end));      
          }
      }
    return null; 
    }
    </script>

    <script type="text/javascript">

        // This code returns the cookie name.
        alert("Getting HTTP Cookie");
        alert(getCookie("MyHttpCookie"));

        // Because the cookie is set to HttpOnly,
        // this returns null.
        alert("Getting HTTP Only Cookie");
        alert(getCookie("MyHttpOnlyCookie"));

    </script>


    </body>
    </html>

    When you run this page you will note InternetExplorer jscript will not allow you to read the value of MyHttpOnlyCookie.  This new flag will allow you to read that cookie from code however!

    To investigate this I decided to use my favorite sample 'httpauth' from the Platform SDK. 

    I added this code to the end of the function, just before closing the handles (note the empty error conditions that you need to fill in):

    C++ code listing for sample (Copy Code):

    fprintf (stderr, "\n");
    char szCookieBuf[512];
    DWORD ccCookieBufSize=512;
    DWORD dwErr=0;

    if (!InternetGetCookieEx("http://jsanders4/","MyHttpCookie",szCookieBuf,&ccCookieBufSize,0,NULL))
    {

        dwErr=GetLastError();
       
    switch (dwErr)
        {

            case ERROR_INSUFFICIENT_BUFFER:
                break;

            case ERROR_NO_MORE_ITEMS:
               
    break;

            default:
               
    break;

        };

    }
    else
    {
        fprintf (stderr,
    "Cookie found: %s\n", szCookieBuf);
    }

    ccCookieBufSize=512;

    if (!InternetGetCookieEx("http://jsanders4/","MyHttpOnlyCookie",szCookieBuf,&ccCookieBufSize,INTERNET_COOKIE_HTTPONLY,NULL))
    {

        dwErr=GetLastError();
        switch (dwErr)
        {

            case ERROR_INSUFFICIENT_BUFFER:
                break;

            case ERROR_NO_MORE_ITEMS:
               
    break;

            default:
               
    break;

        };

    }
    else
    {
        fprintf (stderr,
    "Cookie found: %s\n", szCookieBuf);
    }

    I put the page to write the cookies on one of my servers and pointed the httpauth.exe to that page.  This code works fine and does read the HttpOnly cookie.  Try and remove the flag and you will see the call fail!

    Let me know if this was a help to you!

  • Understanding the New WinInet option: INTERNET_OPTION_SUPPRESS_SERVER_AUTH

    With the release of Internet Explorer 8 comes a new option for WinInet programming: INTERNET_OPTION_SUPPRESS_SERVER_AUTH.  The MSDN documentation is very specific and describes how the option affects authorization, but I like to see things in action!  How about some sample code for INTERNET_OPTION_SUPPRESS_SERVER_AUTH?

    This option is documented here: http://msdn.microsoft.com/en-us/library/aa385328(VS.85).aspx (note that if you are not using the latest SDK headers, the value for this option is also documented here).

    To sum it up, use this option so your WinInet application will allow you to use credentials to authorize through a proxy, but don't pass credentials to the endpoint server.

    To understand this option you can modify the HttpAuth example in the Platforms SDK and see how this option can be used to create a sample.  Then you can use Fiddler (http://www.fiddlertool.com) to request proxy credentials and verify that you cannot pass credentials to an endpoint server.

    Since this is an option for the request you set this on the request handle just before you execute the request:

    InternetSetOption(hRequest,INTERNET_OPTION_SUPPRESS_SERVER_AUTH,NULL,0);

    // Send request.
    fRet = HttpSendRequest( hRequest, // request handle
    "", // header string
    0, // header length
    NULL, // post data
    0 // post length
    );

    Next configure Fiddler to require proxy authentication by selecting the menu item 'Rules' and check the 'Require Proxy Authentication' option.  If you look at the help documentation on this feature you will discover the password and user id is '1' for this setting.

    Finally run the HttpAuth sample and see that it will prompt you for the Proxy authorization, and once you enter these credentials you cannot send credentials to the end point server.  Even if you use the custom UI and use InternetSetOption to set the username and password, WinInet will not send these credentials.

     Note that in the documentation for this option, it suggests you use the INTERNET_OPTION_NO_COOKIES option as well to prevent Cookie based Authentication to the end point server.

    Let me know if this was useful to you!

  • Web Service call results in Exception: A socket operation was attempted to an unreachable host

    There can be many reasons for this error.  In some cases this error can be confusing.  I have seen a few instances where an ASP.Net webservice call resulted in this error.  More perplexing is the fact that Internet Explorer on the same machine can reach the WebService fine.  If a firewall was blocking the traffic, most likely the Internet Explorer request to the same port would also be blocked.  Also to add to the confusion is when adding the usesystemdefault=true (default setting) did the proxy not get used.  The reason that usesystemdefault did not get the proxy is that IE stores the hard coded proxy information in the registry of the logged on user.  When ASP.Net runs, it is not running in the context of the currently logged on user (normally) and so it cannot read the same information that IE is reading to connect to the proxy.

    Some other errors around this exeption include: WebException: Unable to connect to the remote server, SocketException (0x2751): A socket operation was attempted to an unreachable host, Socket operation encountered a dead network

    Typical call stacks look something like this.
    Exception Details: System.Net.Sockets.SocketException: A socket operation was attempted to an unreachable host
    [SocketException (0x2751): A socket operation was attempted to an unreachable host <<ipaddr:port>>]
     System.Net.Sockets.Socket.DoConnect(EndPoint endPointSnapshot, SocketAddress socketAddress) +1073657
       System.Net.Sockets.Socket.InternalConnect(EndPoint remoteEP) +33
       System.Net.ServicePoint.ConnectSocketInternal


    WebException: Unable to connect to the remote server
       System.Net.HttpWebRequest.GetRequestStream() +1534317
       System.Web.Services.Protocols.SoapHttpClientProtocol.Invoke


    Each time, these issues were related to proxy settings.  IE was able to get to the WebService but .NET could not.  The reason is, the Proxy information was manually configured for IE. 

    A simple change to the Web.Config fixes the issue:
    You will need to change "http://192.168.1.10:3128"  to be the address of your proxy server.  You should be able to determine the proxy from your Internet Explorer Settings.
    <configuration>
      <system.net>
        <defaultProxy>
          <proxy
            proxyaddress="http://192.168.1.10:3128"
            bypassonlocal="true"
          />
        </defaultProxy>
      </system.net>
    </configuration>


    Here is an article on how proxy detection works in the 2.0 framework:
    http://msdn.microsoft.com/en-us/magazine/cc300743.aspx

     You may also now need to provide credentials to get through the proxy.  If the call does not go through and the exception indicates an HTTP 407 error, you can add the credentials to your config as well.  Here are articles on the <system.net> schema and proxy setting: http://msdn.microsoft.com/en-us/library/kd3cf2ex(VS.80).aspx, http://msdn.microsoft.com/en-us/library/sa91de1e(VS.80).aspx


    If the proxy settings are not the issue, ProcMon may help you identify any permissions related issues on the server.  You can download this tool from http://www.microsoft.com/sysinternals

    Leave me a message if you were able to solve a problem with this article!

     

  • How to enable WinHttp Tracing on Vista, 2008 and Windows 7

    WinhttpTracecfg.exe for Vista does not exist, so how can you get a WinHttp trace in Vista and above?

    WinHttpTraceCfg.exe has been replaced in Vista and above with the netsh winhttp command.
    see this blog:
    http://blogs.msdn.com/wndp/archive/2007/03/21/winhttp-configuration-for-windows-vista.aspx

    A typical command to enable full tracing for WinHttp would look like this:

    C:\Windows\system32>netsh winhttp set tracing trace-file-prefix="C:\Temp\WinHttpLog" level=verbose format=hex state=enabled

    You would reproduce the issue and then turn off tracing like this:

    C:\Windows\system32>netsh winhttp set tracing state=disabled

     This would leave the other parameters you set previously to remain so the next time you want to enable tracing simply type:

    C:\Windows\system32>netsh winhttp set tracing state=enabled

     

    Other parameters are available:

    C:\Windows\system32>netsh winhttp set tracing

    will show you the options:

    Usage:  set tracing
            [output=]file|debugger|both
            [trace-file-prefix=]<string>
            [level=]default|verbose
            [format=]ansi|hex
            [max-trace-file-size=]<number>
            [state=]enabled|disabled

    Parameters:

      Tag                   Value
      trace-file-prefix   - Prefix for the log file (can include a path)
                            specify "*" to delete an existing prefix
      output              - Where the trace entries are written/displayed to
      level               - How much information to log
      format              - Display format of network traffic (hex or ansi)
      max-trace-file-size - Maximum size of the trace file (in bytes)
      state               - Enables or disables winhttp tracing

    Examples:

      set tracing trace-file-prefix="C:\Temp\Test3" level=verbose format=hex
      set tracing output=debugger max-trace-file-size=512000 state=enabled

    You can display the existing settings with this command:

    C:\Windows\system32>netsh winhttp show tracing

    Current WinHTTP Tracing settings:

        Tracing is not enabled.

        Trace File Prefix: C:\Temp\Test3
        Trace Output: file
        Trace Level:  verbose (headers, APIs, and entity body)
        Network Traffic Format: hex
        Maximum size of trace file (in bytes): 65535

    Let me know if this helps!

  • WWSAPI samples in Windows 7 SDK for RC: "The program can’t start because MSVCR90.dll is missing from your computer"

    I was messing with the latest samples in the RC for Win7 SDK (Microsoft Windows SDK for Windows 7 and .NET Framework 3.5 SP1: RC).

     

    I build some of the WWSAPI samples and for the x86 platform, the samples would fail when I run them because of a missing dll.  Here is the error message I got:

     

    "The program can’t start because MSVCR90.dll is missing from your computer.  Try reinstalling the program to fix this problem."

     

    What is strange is that the dll does exist on the computer.  After researching I found that a manifest would solve the issue. 

     

    I went into the Property Pages for the project, under Configuration Properties, Linker, Manifest File I set Generate Manifest to Yes.

     

    This fixed the issue.

  • Understanding MaxServicePointIdleTime and DefaultConnectionLimit

    To understand these settings you need to understand how the HttpWebRequest class relates to the ServicePointManager and ServicePoint classes.  When you make a request with the HttpWebRequest class, the ServicePointManager provides a ServicePoint object to the HttpWebRequest object to handle the connection to a web resource (web server).  This ServicePoint object manages all the requests for a particular URI Host and protocol.  For example:  There will be one ServicePoint object for all the requests in my sample because they are all going to the server JSAN1 and using the HTTP protocol.  The ServicePoint object has some settings to control how it services the requests.  You can set the default values for these settings on the ServicePointManager before any ServicePoint objects are created.  Once the ServicePoint object is created these default values will not affect existing ServicePoint objects, only newly created ones.

    The ServicePoint model allows optimization, control and performance enhancements for the request.  When you make an HTTP protocol request, ultimately that request and response will take place over a TCP Socket.  Creating and initializing this TCP Socket is a very expensive operation in terms of computer resources and time so if you can re-use this connection for the next request, you have saved the time of creating and initializing a new socket.  The HTTP 1.1 protocol provisions for this optimization by keeping the socket connection alive for subsequent requests by the client to the server. 
    Reference - How ServicePointManager works: http://msdn.microsoft.com/en-us/library/system.net.servicepointmanager.aspx (see remarks)
    Reference - ServicePoint: http://msdn.microsoft.com/en-us/library/system.net.servicepoint.aspx

    The ServicePoint object contains a queue of connections to the server identified by the URI of the server passed in.  By default the number of simultaneous connections to a server is controlled by the ServicePoint.ConnectionLimit property.  This property is set on this object when the ServicePointManager creates the ServicePoint object for your connections to a host.  Connections to the server are queued and will be served by the connections to the server as they become freed from use.  The default number of connections is set to 2.  Ref: http://msdn.microsoft.com/en-us/library/system.net.servicepoint.connectionlimit.aspx

    Obviously if the number of connections your application has requested to a resource is greater than the connection limit, requests are queued and may timeout before they are taken from the queue and processed on an active connection.  An example may help you understand this.  This example creates 64 Worker threads and executes HTTP requests on them.  Create a simple HTTP page on a server in your network (not on the same machine you run this code on) and run this code, passing the URI to the page on the command line.  You will see some timeout exceptions generated:

    Complete code listing for sample (Copy Code):

    using System;

    using System.Net;

    using System.Threading;

    using System.Text;

    using System.IO;

     

    // ClientGetAsync issues the async request.

    class ClientGetAsync

    {

        // This class is used in the worker thread to pass information in

        public class threadStateInfo

        {

            public string strUrl; // the URL we will be making a request on

            public ManualResetEvent evtReset; // used to signal this worker thread is finished

            public int iReqNumber; // used to indicate which request this is

        }

     

        // this does the work of making the http request from a worker thread

        static void ThreadProc(Object stateInfo)

        {

            threadStateInfo tsInfo = stateInfo as threadStateInfo;

            try

            {

                HttpWebRequest aReq = WebRequest.Create(tsInfo.strUrl) as HttpWebRequest;

                aReq.Timeout = 4000;  // I want the request to only take 4 seconds, otherwise there is some problem

                Console.WriteLine("Begin Request {0}", tsInfo.iReqNumber);

                HttpWebResponse aResp = aReq.GetResponse() as HttpWebResponse;

    System.Threading.Thread.Sleep(500); //simulate a half second delay for the server to process the request

                aResp.Close(); // if you do not do this close, you will timeout for sure!  The socket will not be freed.

                Console.WriteLine("End Request {0}", tsInfo.iReqNumber);

            }

            catch (WebException theEx)

            {

                Console.WriteLine("Exception for Request {0}: {1}",tsInfo.iReqNumber,theEx.Message);

            }

     

            //signal the main thread this request is done

            tsInfo.evtReset.Set();            

        }

     

        public static void Main(string[] args)

        {

            if (args.Length < 1)

            {

                showusage();

                return;

            }

            // The number of worker threads to make simultaneous requests (do not go over 64)       

            int numberRequests = 64;

         

            // Used to keep the Main thread alive until all requests are done

            ManualResetEvent[] manualEvents = new ManualResetEvent[numberRequests];

         

            // Get the URI from the command line.

            string httpSite = args[0];

     

            // 2 is Default Connection Limit

            ServicePointManager.DefaultConnectionLimit = 2;

     

            // Get the current settings for the Thread Pool

            int minWorker, minIOC;

            ThreadPool.GetMinThreads(out minWorker, out minIOC);

            // without setting the thread pool up, there was enough of a delay to cause timeouts!

            ThreadPool.SetMinThreads(numberRequests,minIOC);

     

            for (int i = 0; i < numberRequests; i++)

            {

                //Create a class to pass info to the thread proc

                threadStateInfo theInfo = new threadStateInfo();

                manualEvents[i] = new ManualResetEvent(false); // thread done event

                theInfo.evtReset = manualEvents[i];

                theInfo.iReqNumber = i; //just to track what request this is

                theInfo.strUrl = httpSite; // the URL to open

                ThreadPool.QueueUserWorkItem(ThreadProc, theInfo);  //Let the thread pool do the work

            }

         

            // Wait until the ManualResetEvent is set so that the application

            // does not exit until after the callback is called.

            // can wait on a maximum of 64 handles here.

            WaitHandle.WaitAll(manualEvents);

     

            Console.WriteLine("done!");

        }

     

        public static void showusage() {

          Console.WriteLine("Attempts to GET a URL");

          Console.WriteLine("\r\nUsage:");

          Console.WriteLine("   ClientGetAsync URL");

          Console.WriteLine("   Example:");

          Console.WriteLine("      ClientGetAsync http://www.contoso.com/");

        }

     

    }

    Please read the comments in the code.  There are some interesting points here.  The first is that if you do not set the ThreadPool MinThreads property to the anticipated number of threads you will use, the delay of creating these threads on the fly is enough to fail all of the requests with a timeout exception.  Next, note the Close() call on the response.  No matter what we do to the code, if you do not do the Close(), those sockets will remain open and a new socket will be created for each request (you will see that in a network trace).  This will also prevent any of the connections in the ServicePoint object from being freed, so no additional requests can be served.  Another thing that is sort of cool about this code is that it simulates the ASP.NET environment as far as the thread pool idea goes.  If your sample code works OK in this test app, chances are you will be able successfully run similar code in ASP.NET with the same ThreadPool and Connection settings.  Reference Asp.Net tuning: http://msdn.microsoft.com/en-us/library/ms998583.aspx

    Next play with this line of code:

    // 2 is Default Connection Limit

    ServicePointManager.DefaultConnectionLimit = 2;

    Set it to 12 and see how the number of successful requests increases.  Now set it to 4.  You will see a different number of requests succeed and fail depending on the sleep value simulated in the request (System.Threading.Thread.Sleep(500);), the response time of your target server to provide the content, network traffic and the performance of your machine.  Similar factors will affect your real world application.  Also, I set the timeout for the web request to 4 seconds because I expect the result to come back very quickly.  This may or may not be realistic in your situation.  Only testing and careful design will allow you to determine what best works for your application.       

    The question often comes up “why can’t I set the DefaultConnectionLimit to a HUGE number”?  You can set this number in the hundreds if you want.  This will take more computer resources however.  More threads need to be created, more sockets open and the target server will be taxed more.  It is all a balance of resources and expected performance.  These factors need to be considered during design and testing.

    The value MaxServicePointIdleTime comes in to play when you consider performance as well.  This value indicates how long a socket will be kept open after a request and response, before the client gracefully closes the socket down.  Again, the design and performance of your system will determine what a proper value would be for you situation.  If you close unused sockets down too fast, then the socket will never get reused by another request (remember I said the connection creation time is very expensive).  If the value is too long, then sockets (read resources, threads cpu time and memory) may be used unnecessarily at the source and target computer.  Finding the correct balance is important.  In addition, if the MaxServicePointIdleTime is not less than the server Keep-Alive timeout, there is a chance that your code will start using a connection at the same time the server is sending a message to close the socket resulting in a WebException.

    Example App Design

    Let’s work through an example together.  Let’s say this application is designed to handle 40 users every 4 seconds.  Let’s also assume it will take .5 seconds to process a web request in your code.  If this was a single threaded application, 40 users processed in 4 seconds means 4/40 or .1 seconds per user would have to be the performance of your application.  Given that the request to the backend server is .5 seconds (normally) there is no way that your single threaded application can handle this load and meet the spec.  The very best you could hope for is 20 seconds!  So you decide to create a multi threaded solution based on my example and try to apply the principles in this article.  Change the number of requests to 40 in the sample code to simulate all the requests coming in.  If this were a single threaded app, then the best we could do is 4 seconds/.5 processing time = 8 requests.  So in theory if we could do 6 simultaneous requests over the 4 second time period at 8 requests per thread, we would get abound 6*8 or 48 requests done in 4 seconds.  So set the DefaultConnectionLimit to 6 and let’s test.

    Well, my results are: about 10 of the 40 connections timed out.  Why is that, and what in my calculation is wrong?  First, the sleep value I have in my code to simulate the time it takes to get a request back from the server is added to the actual time it took for the request to come back, so the socket was not freed in exactly .5 seconds, it was a little longer.  Also, it takes some time for those 40 worker threads to start processing, they are background worker threads an run at a lower priority than the main thread, so the main thread (adding the work) is going to get a higher priority and is running in a tight loop and so the worker threads will be delayed a little before they start working.  Finally there is the overhead of the connections initially spinning up!  To see if the time spent to get the connections up is part of the delay, I added this code to see if once the 6 connections are created and maintained by the ServicePoint object, would the performance be as I expected after the      WaitHandle.WaitAll(manualEvents); code in the sample code:

    for
    (int i = 0; i < numberRequests; i++)

            {

                //Create a class to pass info to the thread proc

                threadStateInfo theInfo = new threadStateInfo();

                manualEvents[i].Reset();//Unsignalled state           
                theInfo.evtReset = manualEvents[i];

                theInfo.iReqNumber = i+40; //just to track what request this is

                theInfo.strUrl = httpSite; // the URL to open

                ThreadPool.QueueUserWorkItem(ThreadProc, theInfo);  //Let the thread pool do the work

     

            }

     

            WaitHandle.WaitAll(manualEvents);

    Sure enough!  On my system, once the 6 connections are created in the first loop, each of the second set of requests succeeds.

    Next let’s play with the MaxServicePointIdleTime to see how this affects the performance.  Let’s say in our case we expect that the 40 user in 4 seconds rate occurs in bursts every 6 seconds.  In other words, 40 users come in, there is 4 seconds processing time, 2 second delay and another 40 users come in.  Simulate this in code with this sleep statement just before the new loop you added in the previous step:

    System.Threading.Thread.Sleep(2000);

    On my machine that seemed to perform well.  But let’s see how the MaxServicePointIdleTime affects this.  Set the idle time to .5 seconds at the top of the code:
    // 2 is Default Connection Limit
    ServicePointManager.DefaultConnectionLimit = 6;
    ServicePointManager.MaxServicePointIdleTime = 500;

    I was surprised to see that this still succeeded.  I started Netmon to see what was happening with the network sockets, and indeed with the timeout, more sockets were being used.  I picked a couple of the conversations and filtered by the port and indeed saw that the connection was being reset by the client after .5 seconds.  Apparently in MY case, the overhead of the ServicePoint object initializing internal structures was greater than the cost of setting up the socket.  Realize in my case the target server was doing nothing else and was on the same switch that the source computer is on so the TCP overhead for creating the connection was indeed negligible.

    All this said, pick a MaxServicePointIdleTime that is less than the Keep-Alive timeout of the server and small enough so that you do not have ports open when it is not necessary.  In my scenario I expected 40 users in a burst after 2 seconds of activity so a value of 2-6 seconds seems like a good choice.  I set it to 2500 milliseconds.

    Next I wanted to get a good setting for the DefaultConnectionLimit that would allow the requests to succeed all of the time.  Simple experimentation showed that 10 seemed to always succeed.  Confident that I had a good bunch of settings, I now had to plan for the fact that sometimes the network, local resources or target server may create a timeout condition.  One way to address this would be to increase the HttpWebRequest.Timeout value.  Another may be to increase the number of connections available.  Finally you could simply retry the request when it fails.

    To simulate the system under a heavy load and test retrying the request, I increased the sleep value in my Worker ThreadProc to sleep for a second.  I then added a retry immediately after the catch:

    static void ThreadProc(Object stateInfo)

        {

            threadStateInfo tsInfo = stateInfo as threadStateInfo;

            bool success = false;

            try

            {

                HttpWebRequest aReq = WebRequest.Create(tsInfo.strUrl) as HttpWebRequest;

                aReq.Timeout = 4000;  // I want the request to only take 4 seconds, otherwise there is some problem

                Console.WriteLine("Begin Request {0}", tsInfo.iReqNumber);

                HttpWebResponse aResp = aReq.GetResponse() as HttpWebResponse;

                System.Threading.Thread.Sleep(1000); //simulate a half second delay for the server to process the request

                aResp.Close(); // if you do not do this close, you will timeout for sure!  The socket will not be freed.

                Console.WriteLine("End Request {0}", tsInfo.iReqNumber);

                success = true;

            }

            catch (WebException theEx)

            {

                Console.WriteLine("Exception for Request {0}: {1}",tsInfo.iReqNumber,theEx.Message);

            }

            try

            {

                if (success == false)

                {

                    Console.WriteLine("Retry Request {0}:", tsInfo.iReqNumber);

                    HttpWebRequest aReq = WebRequest.Create(tsInfo.strUrl) as HttpWebRequest;

                    aReq.Timeout = 4000;  // I want the request to only take 4 seconds, otherwise there is some problem

                    Console.WriteLine("Begin Retry Request {0}", tsInfo.iReqNumber);

                    HttpWebResponse aResp = aReq.GetResponse() as HttpWebResponse;

                    System.Threading.Thread.Sleep(1000); //simulate a half second delay for the server to process the request

                    aResp.Close(); // if you do not do this close, you will timeout for sure!  The socket will not be freed.

                    Console.WriteLine("End Retry Request {0}", tsInfo.iReqNumber);

                    success = true;

                }

            }

            catch (WebException theEx)

            {

                Console.WriteLine("2nd Exception for Request {0}: {1}", tsInfo.iReqNumber, theEx.Message);

            }

     

     

     

            //signal the main thread this request is done

            tsInfo.evtReset.Set();            

        }


    This allowed all of the requests to succeed!  Retrying the after the exception has the benefit that we do not use additional resources unnecessarily and in the event there is a temporary problem with the system, the requests will still go through. 


    This allowed all of the requests to succeed!  Retrying the after the exception has the benefit that we do not use additional resources unnecessarily and in the event there is a temporary problem with the system, the requests will still go through. 


    Summary

    This is only a simple example but should give you some hands on experience with the two settings; MaxServicePointIdleTime and DefaultConnectionLimit.  If you work through this document and sample code on your own, you will have a good idea of how these values can be changed to help you create an application that is efficient and that will work for you.  NOTHING can replace careful design, initial specification and analysis.  You will need to load test and see how your system behaves in the normal case and when unexpected events affect the normal processing.  You should be able to extend this knowledge to ASP.NET and Windows applications that use the HttpWebRequest class and successfully design or troubleshoot them.  Network Monitor will be a great tool for you to see what is actually happening over the network as well.  I only showed once how I used the tool to see the connection activity over the network, but I use this tool daily to troubleshoot HTTP issues. 

    Let me know if you find this a useful resource!

     

  • IIS 7 ADSI Error: System.Runtime.InteropServices.COMException (0x80005000): Unknown error (0x80005000)

    The following code worked fine on IIS 6 but when used on IIS it failed:

    System.DirectoryServices.DirectoryEntry iisServer;
    iisServer =
    new System.DirectoryServices.DirectoryEntry("IIS://jsanders4/W3SVC/1");
    System.DirectoryServices.
    DirectoryEntry rootFolder = iisServer.Children.Find("Root", "IIsWebVirtualDir"); //exception here

    The exception was as follows: 

    [System.Runtime.InteropServices.COMException] {"Unknown error (0x80005000)"} 
    System.Runtime.InteropServices.COMException

    Unknown error (0x80005000)
       at System.DirectoryServices.DirectoryEntry.Bind(Boolean throwIfFail)
       at System.DirectoryServices.DirectoryEntry.Bind()
       at System.DirectoryServices.DirectoryEntry.get_IsContainer()
       at System.DirectoryServices.DirectoryEntries.CheckIsContainer()
       at System.DirectoryServices.DirectoryEntries.Find(String name, String schemaC
    lassName) 

     

    The error code is not translated to anything.  This indicates that the ADSI provider for IIS://jsanders4/W3SVC/1 does not exist or it is inaccessible.

    Opening up IIS manager you can see that the webserver 'jsanders4' is up and running and the primary website ID is indeed 1.  The logical conclusion then is that the ADSI provider for IIS://jsanders4 must be the problem.

    IIS 7 does not install an ADSI provider by default.  You can enable it however as a Role Service for the IIS Web Server.  You need to enable the IIS 6 Metabase Compatiblity role service.  Probably a better way to proceed is to change your code to use the WMI provider for IIS 7 http://msdn.microsoft.com/en-us/library/aa347459.aspx

    After installing the IIS 6 Metabase Compatiblity role service the error changed:     

    [System.Runtime.InteropServices.COMException] {"Access is denied.\r\n"} System.Runtime.InteropServices.COMException
    ErrorCode 0x80070005

    Access is denied.

       at System.DirectoryServices.DirectoryEntry.Bind(Boolean throwIfFail)
       at System.DirectoryServices.DirectoryEntry.Bind()
       at System.DirectoryServices.DirectoryEntry.get_IsContainer()
       at System.DirectoryServices.DirectoryEntries.CheckIsContainer()
       at System.DirectoryServices.DirectoryEntries.Find(String name, String schemaClassName)

    This is because Windows Server 2008 is locked down with UAC.  You need to run the program as Administrator to execute this program. Another alternative is to set the account that is running this program with the rights: Logon as a Service"/ "Logon as a Batch Job"

    I hope this helps you!  Let me know if you used it to solve an issue.

  • How to get Certificate Information Using WinInet APIs

    There are two different structures you can query in order to retrieve server certificate information.  You must do some sort of request to complete the SSL server certificate exchange, and then you can retrieve this information.  Here is an example of that technique.

    // CertificateInfo.cpp : Defines the entry point for the console application.
    // This is sample code. You are responsible for input validation, and error processing.
    // The following macros define the minimum required platform. The minimum required platform
    // is the earliest version of Windows, Internet Explorer etc. that has the necessary features to run
    // your application. The macros work by enabling all features available on platform versions up to and
    // including the version specified.
    // Modify the following defines if you have to target a platform prior to the ones specified below.
    // Refer to MSDN for the latest info on corresponding values for different platforms.
    #ifndef _WIN32_WINNT // Specifies that the minimum required platform is Windows Vista.
    #define _WIN32_WINNT 0x0600 // Change this to the appropriate value to target other versions of Windows.
    #endif
    #ifndef
    WIN32_LEAN_AND_MEAN
    #define WIN32_LEAN_AND_MEAN
    #endif
    #include
    <windows.h>
    #include <stdio.h>
    #include <iostream>
    #include <tchar.h>
    #include <wininet.h> // make sure you link with WinInet.lib

    int _tmain(int argc, _TCHAR* argv[])
    {

      if (argc < 2)
      {

        std::cout << "Please specify and HTTPS address to query the certificate information for" << std::endl;
      }
      else
      {
        // Get Scheme and HostName from URL passed in
        ::URL_COMPONENTS urlComp;
        ::ZeroMemory((
    void *)&urlComp,sizeof(URL_COMPONENTS));
        urlComp.dwSchemeLength = -1;
        urlComp.dwHostNameLength = -1;
        urlComp.dwStructSize = sizeof(URL_COMPONENTS);
        if (!::InternetCrackUrl(argv[1],wcslen(argv[1]),0,&urlComp))
        {
            std::cout <<
    "InternetCrackUrl failed" << std::endl;
        }
        else
        {
           
    if (urlComp.nScheme != INTERNET_SCHEME_HTTPS)
            {
                std::cout <<
    "Please specify and HTTPS address to query the certificate information for: https://www.someserver.com" << std::endl;
            }
            else
            {
                std::wcout <<
    "opening connection to host: " << (LPWSTR)urlComp.lpszHostName << std::endl;

                HINTERNET hInternet = ::InternetOpen(_T(
    "Test Certificate Info"), INTERNET_OPEN_TYPE_PRECONFIG, NULL, NULL, 0);
                if (hInternet)
                {
                    HINTERNET hConnect = ::InternetConnect( hInternet, urlComp.lpszHostName, INTERNET_DEFAULT_HTTPS_PORT, NULL, NULL, INTERNET_SERVICE_HTTP, INTERNET_FLAG_SECURE, NULL);
                    if (hConnect)
                    {
                       
    // you could use HEAD,GET or POST here. HEAD will not return the body of the page so will be more efficient
                        HINTERNET hRequest = ::HttpOpenRequest(hConnect,_T("HEAD"), argv[1], NULL, NULL,
    NULL, INTERNET_FLAG_SECURE, NULL); 
     
                        
    if (NULL != hRequest)
                        { 
                           
    // Send
                            BOOL fRet = ::HttpSendRequest( hRequest, _T(""), 0, NULL, 0);

                            if (fRet)
                            {
                               
    char certificateInfoStr[2048];
                                certificateInfoStr[0] =
    '\0';
                                DWORD certInfoLength = 2048;
                                if ( TRUE  == ::InternetQueryOption( hRequest, INTERNET_OPTION_SECURITY_CERTIFICATE, &certificateInfoStr, &certInfoLength) )
                                {
                                   
    if ( certificateInfoStr )
                                    {
                                        std::cout << (
    char *) certificateInfoStr << std::endl;
                                    }
                                } 
                                
    else
                                {
                                    // process error here
                                   
    DWORD error = GetLastError(); 
                                }

                                INTERNET_CERTIFICATE_INFO certificateInfo;
                                certInfoLength =
    sizeof(INTERNET_CERTIFICATE_INFO);
                               
    if ( TRUE == InternetQueryOption( hRequest,
                                                                                  INTERNET_OPTION_SECURITY_CERTIFICATE_STRUCT,
                                                                                  &certificateInfo,
                                                                                  &certInfoLength) )
                                { 
                                   
    // free up memory with LocalFree()

                                    if ( certificateInfo.lpszEncryptionAlgName )
                                    {
                                        std::cout << (
    char *) certificateInfo.lpszEncryptionAlgName << std::endl;
                                        LocalFree( certificateInfo.lpszEncryptionAlgName);
                                    }

                                    if ( certificateInfo.lpszIssuerInfo )
                                    {
                                        std::cout << (
    char *) certificateInfo.lpszIssuerInfo << std::endl;
                                        LocalFree( certificateInfo.lpszIssuerInfo );
                                    }

                                    if ( certificateInfo.lpszProtocolName )
                                    {
                                        std::cout << (
    char *) certificateInfo.lpszProtocolName << std::endl;
                                        LocalFree( certificateInfo.lpszProtocolName );
                                    }

                                    if ( certificateInfo.lpszSignatureAlgName )
                                    {
                                        std::cout << (
    char *) certificateInfo.lpszSignatureAlgName << std::endl;
                                        LocalFree( certificateInfo.lpszSignatureAlgName );
                                    }

                                    if ( certificateInfo.lpszSubjectInfo )
                                    {
                                        std::cout << (
    char *) certificateInfo.lpszSubjectInfo << std::endl;
                                        LocalFree( certificateInfo.lpszSubjectInfo );
                                    }

                                } 
                               
    else 
                                { 
                                       
    // process error
                                        DWORD error = GetLastError(); 
                                } 
                            }

                            ::InternetCloseHandle(hRequest);
                        }
                       
    else 
                       
    {
                            std::cout <<
    "HttpOpenRequest failed" << std::endl;
                        }

                        ::InternetCloseHandle(hConnect);

                    }
                    else
                    {
                        std::cout <<
    "InternetConnect failed" << std::endl;
                    }
                    ::InternetCloseHandle(hInternet);
                }
                
    else
               
    {
                     std::cout <<
    "InternetOpen failed" << std::endl;
                }
            }
        }
      }

    return 0;
    }

More Posts Next page »

© 2009 Microsoft Corporation. All rights reserved. Terms of Use  |  Trademarks  |  Privacy Statement
Microsoft
Page view tracker