I've spend several months working with HttpWebRequest & CookieContainer, and simply had a miserable time making it work.
My question is whether there's a better approach/workaround to it, and if not hopefully these bugs will be fixed by next 'patch'.
Issue #1. HttpWebRequest does not setCookies for redirects until the end of redirect.
Suppose you have a scenario where you log in, gets a 302 redirect with a SetCookie header. Now, the new redirected page verifies that the cookie is set before sending the correct response. Using HttpWebRequest & CookieContainer, the second redirect request does not have the cookie that response #1 specified, and thus you can never log in using these APIs.
The only workaround I found so far is to manually do the redirects myself. This is simply inefficient and no reason why HttpWebRequest should not do this set cookie automatically between redirects.
This kind of redirect scenario happens quite often. I bet even some of Microsoft's own webpage does this.
Issue #2. Cannot remove cookies from cookie container.
This doesn't really need explanation. Why would it not let me remove a cookie The only silly approach is to make a new one and copy/add all the cookies except the one you want to remove. Setting a null value does not remove it either.
Are these known issues

HttpWebRequest & CookieContainer broken design & implementation
AndyPham
John,
I have confirmed that cookies set in redirect responses are sent when the request is automatically sent to the redirected location. Below is a simple little app that demonstrates it. Following the code is the output. I've highlighted the Cookie header being sent to the redirected location as seen by the server side.
using System;
using System.IO;
using System.Net;
using System.Threading;
using System.Text;
namespace ConsoleApp {
class Program {
static string host = "localhost";
static string domain = "";
static string cookieDomain = domain;
static string prefix1 = "http://" + host + domain + ":8080/redirect/";
static string prefix2 = "http://" + host + domain + ":8080/sendbody/";
static string url1 = prefix1 + "file.txt";
static string url2 = prefix2 + "file.txt";
static void Main(string[] args) {
// start listeners on both prefixes
HttpListener listener1 = new HttpListener();
listener1.Prefixes.Add(prefix1);
listener1.Start();
ThreadPool.QueueUserWorkItem(new WaitCallback(RedirectorLoop), listener1);
HttpListener listener2 = new HttpListener();
listener2.Prefixes.Add(prefix2);
listener2.Start();
ThreadPool.QueueUserWorkItem(new WaitCallback(ServiceLoop), listener2);
// uncomment next line to show redirect response
//DoClientRequest(url1, false, null);
// do a client request, allow redirect, supply a cookie container
DoClientRequest(url1, true, new CookieContainer());
}
static void DoClientRequest(string uri, bool allowRedirect, CookieContainer cc) {
Console.WriteLine("=============================================================================");
Console.WriteLine("Client Request: \"{0}\"", uri);
Console.WriteLine("AllowRedirect: {0}", allowRedirect);
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(uri);
request.AllowAutoRedirect = allowRedirect;
request.CookieContainer = cc;
HttpWebResponse response = null;
try {
response = (HttpWebResponse)request.GetResponse();
}
catch(WebException e) {
Console.WriteLine("=============================================================================");
Console.WriteLine("Client GetResponse failed with this exception:\n-------------\n{0}\n-------------", e);
Console.WriteLine("StatusCode:{0:d}", ((HttpWebResponse)e.Response).StatusCode);
Console.WriteLine("StatusDescription: {0}", ((HttpWebResponse)e.Response).StatusDescription);
Console.WriteLine("ResponseUri is {0}", e.Response.ResponseUri);
}
if(response != null) {
Console.WriteLine("=============================================================================");
Console.WriteLine("Client Response:");
Console.WriteLine("{0:d} {1}", response.StatusCode, response.StatusDescription);
Console.WriteLine(response.Headers);
Stream responseStream = response.GetResponseStream();
int bytesRead;
byte[] buffer = new byte[512];
do {
bytesRead = responseStream.Read(buffer, 0, buffer.Length);
string body = Encoding.ASCII.GetString(buffer, 0, bytesRead);
Console.WriteLine(body);
} while(bytesRead > 0);
responseStream.Close();
}
}
static void RedirectorLoop(object state) {
HttpListener listener = (HttpListener)state;
while(true) {
HttpListenerContext context = listener.GetContext();
HttpListenerRequest rq = context.Request;
Console.WriteLine("=============================================================================");
Console.WriteLine("Server Received Request:");
Console.WriteLine("{0} {1}", rq.HttpMethod, rq.Url);
Console.WriteLine(rq.Headers);
Console.WriteLine("Server Redirecting to: " + url2);
HttpListenerResponse rs = context.Response;
rs.Redirect(url2);
rs.AddHeader("Set-Cookie", CreateCookieHeaderValue("Name", "Value", "/", ""));
rs.Close();
}
}
static void ServiceLoop(object state) {
HttpListener listener = (HttpListener)state;
while(true) {
HttpListenerContext context = listener.GetContext();
HttpListenerRequest rq = context.Request;
Console.WriteLine("=============================================================================");
Console.WriteLine("Server Received Request:");
Console.WriteLine("{0} {1}", rq.HttpMethod, rq.Url);
Console.WriteLine(rq.Headers);
Console.WriteLine("Server Sending Body");
HttpListenerResponse rs = context.Response;
byte[] buffer = System.Text.Encoding.UTF8.GetBytes("<HTML><BODY> Hello World! </BODY></HTML>");
rs.ContentLength64 = buffer.Length;
rs.OutputStream.Write(buffer, 0, buffer.Length);
rs.OutputStream.Close();
rs.Close();
}
}
static string CreateCookieHeaderValue(string name, string value, string path, string domain) {
if (name == null) throw new ArgumentNullException("name");
if (name.Length == 0) throw new ArgumentException("empty string", "name");
if (value == null) throw new ArgumentNullException("value");
if (value.Length == 0) throw new ArgumentException("empty string", value);
StringBuilder sb = new StringBuilder(128);
sb.AppendFormat("{0}={1}", name, value);
if (path != null && path.Length > 0) sb.AppendFormat("; path={0}", path);
if (domain != null && domain.Length > 0) sb.AppendFormat("; domain={0}", domain);
sb.Append(";");
return sb.ToString();
}
}
=============================================================================
Client Request: "http://localhost:8080/redirect/file.txt"
AllowRedirect: True
=============================================================================
Server Received Request:
GET http://localhost:8080/redirect/file.txt
Connection: Keep-Alive
Host: localhost:8080
Server Redirecting to: http://localhost:8080/sendbody/file.txt
=============================================================================
Server Received Request:
GET http://localhost:8080/sendbody/file.txt
Cookie: Name=Value
Host: localhost:8080
Server Sending Body
=============================================================================
Client Response:
200 OK
Content-Length: 40
Date: Wed, 28 Jun 2006 23:03:45 GMT
Server: Microsoft-HTTPAPI/1.0
<HTML><BODY> Hello World! </BODY></HTML>
}
soconne
Still looking for some response after one week:
(1) Yes, we know the issue, and we plan to fix it in the next patch, which is scheduled for ...
(2) This is not a bug. We believe that Set-Cookie headers should be ignored until all the redirects are done, then set the cookie afterwards.
(3) We are looking into it.
(4) Here's a better way of getting around the flaw....
ChiaraE
John,
Thank you for you patience. I have not yet validated #1, but will look into it.
For #2: You are correct you cannot remove a cookie. Instead they will expire based on the cookies expiration date. While it does not give exactly what you want, you can control the size of the container and the number of cookies per domain kept in the container. This will purge older cookies in favor of newer ones.
Todd Biggs - Windows Live
I'm request page http://server/a.aspx then redirected into http://server/b.aspx
At http://server/a.aspx should receive cookies A, and redirected to http://server/b.aspx receive cookies B, but why my cookies container only receive cookies B, and cookies A is missing. How can I receive all cookies (A and B) with HttpWebRequest and HttpWebResponse
Thanks,