Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

WebClient CookieContainer works well in .NET 4.0+ but not in earlier versions

Tags:

c#

.net

webclient

I am developing an application that uses WebClient. I have this class that extends basic WebClient functionality:

public class WebClientEx : WebClient
{
    private CookieContainer _cookieContainer = new CookieContainer();

    protected override WebRequest GetWebRequest(Uri address)
    {
        WebRequest request = base.GetWebRequest(address);
        if (request is HttpWebRequest)
        {
            (request as HttpWebRequest).CookieContainer = _cookieContainer;
            (request as HttpWebRequest).AllowAutoRedirect = true;
            (request as HttpWebRequest).Timeout = 10000;
        }
        return request;
    }
}

I use WebClientEx to login to site and to request some information. It works well for 4.0 and 4.5, but it doesn't work in earlier versions such as 3.5, 3.0 etc. I added some debug code and in early versions it says that there are 0 cookies in cookie container, while 4.0+ says that there are two cookies, as it should be.

So the reason is probably that early versions of .NET Framework have some problems with storing cookies in the cookie container. How to fix that?

like image 719
Uhehesh Avatar asked Aug 19 '12 20:08

Uhehesh


2 Answers

I have confirmed the behaviour is different in .NET 3.5 compared to .NET 4.0. Is used the following code to test:

Uri sourceUri = new Uri(@"http://www.html-kit.com/tools/cookietester/");
WebClientEx webClientEx = new WebClientEx();
webClientEx.Headers[HttpRequestHeader.ContentType] = "application/x-www-form-urlencoded";
webClientEx.UploadString(sourceUri, "cn=MyCookieName&cv=MyCookieValue");
var text = webClientEx.DownloadString(sourceUri);
var doc = new HtmlAgilityPack.HtmlDocument();
doc.Load(new MemoryStream(Encoding.ASCII.GetBytes((text))));
var node = doc.DocumentNode.SelectNodes("//div").Single(n => n.InnerText.StartsWith("\r\nNumber of cookies received:"));
Debug.Assert(int.Parse(node.InnerText.Split(' ')[4]) == 1);

Of course, this doesn't answer your question; but I can see no reason why there is a difference in behaviour other than to say maybe it was fixed in .NET 4.0 and the fix hasn't be put into .NET 3.5 or prior versions.

I tried a similar thing with HttpWebRequest and had the same problem (works in 4, but not prior):

HttpWebRequest webreq = ((HttpWebRequest) (WebRequest.Create(sourceUri)));
CookieContainer cookies = new CookieContainer();

var postdata = Encoding.ASCII.GetBytes("cn=MyCookieName&cv=MyCookieValue");

webreq.CookieContainer = cookies;
webreq.Method = "POST";
webreq.ContentLength = postdata.Length;
webreq.ContentType = "application/x-www-form-urlencoded";

Stream webstream = webreq.GetRequestStream();
webstream.Write(postdata, 0, postdata.Length);
webstream.Close();

using (WebResponse response = webreq.GetResponse())
{
    webstream = response.GetResponseStream();
    using (StreamReader reader = new StreamReader(webstream))
    {
        String responseFromServer = reader.ReadToEnd();
        var doc = new HtmlAgilityPack.HtmlDocument();
        doc.Load(new MemoryStream(Encoding.ASCII.GetBytes((responseFromServer))));
        var node =
            doc.DocumentNode.SelectNodes("//div").Single(n => n.InnerText.StartsWith("\r\nNumber of cookies received:"));
        Debug.Assert(int.Parse(node.InnerText.Split(' ')[4]) == 1);
    }
}

So, there seems to be a problem with HttpWebRequest (which WebClient uses). This might be new because I've seen people use code like this before 4.0 was released (maybe prior to 3.50 and they say it worked.

If it's urgent, I would suggest contacting Microsoft Support. If you have an MSDN license the following link will detail how to make a support request with the included MSDN support tickets: http://msdn.microsoft.com/en-us/subscriptions/bb266240.aspx If you don't have MSDN you can contact Support as detailed here: https://support.microsoft.com/oas/default.aspx?Gprid=8291&st=1&wfxredirect=1&sd=gn

if it's less urgent, then you could probably log the issue at http://connect.microsoft.com/VisualStudio to see if you get a response with workarounds.

like image 133
Peter Ritchie Avatar answered Sep 22 '22 09:09

Peter Ritchie


I just created a basic IHttpHandler to test this class and it appears to work.

<%@ WebHandler Language="C#" Class="CookieTest" %>

using System;
using System.Net;
using System.Web;

public class CookieTest : IHttpHandler
{
    public class WebClientEx : WebClient
    {
        private CookieContainer _cookieContainer = new CookieContainer();

        protected override WebRequest GetWebRequest(Uri address)
        {
            WebRequest request = base.GetWebRequest(address);
            if (request is HttpWebRequest)
            {
                (request as HttpWebRequest).CookieContainer = _cookieContainer;
                (request as HttpWebRequest).AllowAutoRedirect = true;
                (request as HttpWebRequest).Timeout = 10000;
            }
            return request;
        }
    }

    public void ProcessRequest(HttpContext ctxt)
    {
        ctxt.Response.ContentType = "text/plain";

        String cmd = ctxt.Request["cmd"];
        if (cmd == "set")
        {
            ctxt.Response.Cookies.Add(new HttpCookie("test", "test"));
            ctxt.Response.Write("Cookie Set: test = test");
        }
        else if (cmd == "get")
        {
            ctxt.Response.Write("Cookie Value: test = " + ctxt.Request.Cookies["test"].Value);
        }
        else
        {
            // run out tests
            WebClientEx wc = new WebClientEx();

            ctxt.Response.Write("Running tests on .NET version: " + Environment.Version);
            ctxt.Response.Write(Environment.NewLine + Environment.NewLine);
            ctxt.Response.Write("Setting Cookie...");
            ctxt.Response.Write(Environment.NewLine + Environment.NewLine);
            ctxt.Response.Write("Response: " + wc.DownloadString(ctxt.Request.Url.AbsoluteUri + "?cmd=set"));
            ctxt.Response.Write(Environment.NewLine + Environment.NewLine);
            ctxt.Response.Write("Getting Cookie...");
            ctxt.Response.Write(Environment.NewLine + Environment.NewLine);
            ctxt.Response.Write("Response: " + wc.DownloadString(ctxt.Request.Url.AbsoluteUri + "?cmd=get"));
            ctxt.Response.Write(Environment.NewLine + Environment.NewLine);
        }
    }

    public bool IsReusable
    {
        get { return true; }
    }
}

The results I get are:

Running tests on .NET version: 2.0.50727.5456

Setting Cookie...

Response: Cookie Set: test = test

Getting Cookie...

Response: Cookie Value: test = test

Does this seem correct to you?

like image 42
dana Avatar answered Sep 24 '22 09:09

dana