How can I make a copy of CookieContainer?

Given that CookieContainer instances are not thread safe .

All public static (Shared in Visual Basic) members of this type are thread safe. Any instance members do not guarantee thread safety.

So it turns out that I cannot use the same container for multiple simultaneous HTTP requests without synchronization. Unfortunately, from the documentation in MSDN it is not clear how to synchronize it correctly.

The solution will use a copy of the main container for each request, and once the request is completed, cookies from the copy can be combined back into the main container. Copying and merging can be performed in a synchronized manner.

So the question is: how can I make a copy of the cookieContainer class instance?

+3
source share
5 answers

Take a look at the CookieContainter class and you will see that concurrent scripts are supposed to occur when changes to the cookie collection take place, right?

You will notice that the author of CookieContainer made sure to use lock {} and SyncRoot around these parts, which modify the code collection, and I do not think that this approach is not addressed for parallel scripts.

In addition, you may notice that any cookie added is literally cloned , so cookies inside the container and all operations done will not spoil links to objects outside the cookie container. In the worst case, when I miss something, the clone also gives us a hint about what exactly you should copy and how you could do it, if you use the reflection approach described in other posts (I personally will not consider hacking it, as it meets the requirement and it is managed by legal and safe code :)).

In fact, all MSDN documentation mentions: "Any members of an instance are not guaranteed to be thread safe." - This is a kind of reminder, because you are right, you really need to be careful. Then with this statement you can basically assume two things: 1) Non-static members are generally unsafe. 2) Some members may be thread safe, but they are not properly documented.

+4
source

The CookieContainer class is Serializable. Since you said that you still need to serialize it, why not just use the BinaryFormatter to serialize it to MemorySteam and then Deserialize to make a copy?

I know this is too easy, so please ignore if this does not help.

 private CookieContainer CopyContainer(CookieContainer container) { using(MemoryStream stream = new MemoryStream()) { BinaryFormatter formatter = new BinaryFormatter(); formatter.Serialize(stream, container); stream.Seek(0, SeekOrigin.Begin); return (CookieContainer)formatter.Deserialize(stream); } } 
+7
source

You can use Reflection to get all cookies associated with all Uri , and then create a new CookieContainer and add them to it, perhaps for example:

 public static CookieContainer DeepClone(CookieContainer src) { CookieContainer cookieContainer = new CookieContainer(); Hashtable table = (Hashtable)src.GetType().InvokeMember("m_domainTable", BindingFlags.NonPublic | BindingFlags.GetField | BindingFlags.Instance, null, src, new object[] { }); foreach (var tableKey in table.Keys) { String str_tableKey = (string)tableKey; if (str_tableKey[0] == '.') str_tableKey = str_tableKey.Substring(1); SortedList list = (SortedList)table[tableKey].GetType().InvokeMember("m_list", BindingFlags.NonPublic | BindingFlags.GetField | BindingFlags.Instance, null, table[tableKey], new object[] { }); foreach (var listKey in list.Keys) { String url = "https://" + str_tableKey + (string)listKey; CookieCollection collection = src.GetCookies(new Uri(url)); foreach (Cookie c in collection) cookieContainer.Add(new Cookie(c.Name, c.Value, c.Path, c.Domain) { Comment = c.Comment, CommentUri = c.CommentUri, Discard = c.Discard, Expired = c.Expired, Expires = c.Expires, HttpOnly = c.HttpOnly, Port = c.Port, Secure = c.Secure, Version = c.Version }); } } return cookieContainer; } 
+2
source

You can do it with reflection. This can be improved by YMMV:

 //Set up the source cookie container var cookieContainerA = new CookieContainer(); cookieContainerA.Add(new Uri("http://foobar.com"), new Cookie("foo", "bar")); cookieContainerA.Add(new Uri("http://foobar.com"), new Cookie("baz", "qux")); cookieContainerA.Add(new Uri("http://abc123.com"), new Cookie("abc", "123")); cookieContainerA.Add(new Uri("http://abc123.com"), new Cookie("def", "456")); //Set up our destination cookie container var cookieContainerB = new CookieContainer(); //Get the domain table member var type = typeof(CookieContainer); var domainTableField = type.GetField("m_domainTable", BindingFlags.NonPublic | BindingFlags.Instance); var domainTable = (Hashtable)domainTableField.GetValue(cookieContainerA); //Iterate the domain table foreach (var obj in domainTable) { var entry = (DictionaryEntry)obj; //The domain is the key (we only need this for our Console.WriteLine later) var domain = entry.Key; var valuesProperty = entry.Value.GetType().GetProperty("Values"); var values = (IList)valuesProperty.GetValue(entry.Value); foreach (var valueObj in values) { //valueObj is a CookieCollection, cast and add to our destination container var cookieCollection = (CookieCollection)valueObj; cookieContainerB.Add(cookieCollection); //This is a dump of our source cookie container foreach (var cookieObj in cookieCollection) { var cookie = (Cookie)cookieObj; Console.WriteLine("Domain={0}, Name={1}, Value={2}", domain, cookie.Name, cookie.Value); } } } //Test the copying //var foobarCookies = cookieContainerB.GetCookies(new Uri("http://foobar.com")); //var abc123Cookies = cookieContainerB.GetCookies(new Uri("http://abc123.com")); 
+1
source

Just adding your own twist to Alan answer above to convert to / from Base64 string.

 public static string ToBase64(CookieContainer container) { string str = null; byte[] bytes = null; using (MemoryStream ms = new MemoryStream()) { BinaryFormatter bf = new BinaryFormatter(); bf.Serialize(ms, container); bytes = ms.ToArray(); } str = Convert.ToBase64String(bytes); return str; } public static CookieContainer FromBase64(string container_base64) { CookieContainer cc = null; byte[] bytes = Convert.FromBase64String(container_base64); using (MemoryStream ms = new MemoryStream(bytes)) { BinaryFormatter bf = new BinaryFormatter(); cc = (CookieContainer)bf.Deserialize(ms); } return cc; } 
0
source

Source: https://habr.com/ru/post/1403954/


All Articles