-
-
Notifications
You must be signed in to change notification settings - Fork 393
/
Copy pathCookieJar.cs
137 lines (119 loc) · 4.45 KB
/
CookieJar.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
using System;
using System.Collections;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using Flurl.Util;
namespace Flurl.Http
{
/// <summary>
/// A collection of FlurlCookies that can be attached to one or more FlurlRequests, either explicitly via WithCookies
/// or implicitly via a CookieSession. Stores cookies received via Set-Cookie response headers.
/// </summary>
public class CookieJar : IReadOnlyCollection<FlurlCookie>
{
private readonly ConcurrentDictionary<string, FlurlCookie> _dict = new ConcurrentDictionary<string, FlurlCookie>();
/// <summary>
/// Adds a cookie to the jar or replaces one with the same Name/Domain/Path.
/// Throws InvalidCookieException if cookie is invalid.
/// </summary>
/// <param name="name">Name of the cookie.</param>
/// <param name="value">Value of the cookie.</param>
/// <param name="originUrl">URL of request that sent the original Set-Cookie header.</param>
/// <param name="dateReceived">Date/time that original Set-Cookie header was received. Defaults to current date/time. Important for Max-Age to be enforced correctly.</param>
public CookieJar AddOrReplace(string name, object value, string originUrl, DateTimeOffset? dateReceived = null) =>
AddOrReplace(new FlurlCookie(name, value.ToInvariantString(), originUrl, dateReceived));
/// <summary>
/// Adds a cookie to the jar or replaces one with the same Name/Domain/Path.
/// Throws InvalidCookieException if cookie is invalid.
/// </summary>
public CookieJar AddOrReplace(FlurlCookie cookie) {
if (!TryAddOrReplace(cookie, out var reason))
throw new InvalidCookieException(reason);
return this;
}
/// <summary>
/// Adds a cookie to the jar or updates if one with the same Name/Domain/Path already exists,
/// but only if it is valid and not expired.
/// </summary>
/// <returns>true if cookie is valid and was added or updated. If false, provides descriptive reason.</returns>
public bool TryAddOrReplace(FlurlCookie cookie, out string reason) {
if (!cookie.IsValid(out reason))
return false;
if (cookie.IsExpired(out reason)) {
// when server sends an expired cookie, it's effectively an instruction for client to delete it.
// https://stackoverflow.com/a/53573622/62600
_dict.TryRemove(cookie.GetKey(), out _);
return false;
}
cookie.Lock(); // makes immutable
_dict[cookie.GetKey()] = cookie;
return true;
}
/// <summary>
/// Removes all cookies matching the given predicate.
/// </summary>
public CookieJar Remove(Func<FlurlCookie, bool> predicate) {
var keys = _dict.Where(kv => predicate(kv.Value)).Select(kv => kv.Key).ToList();
foreach (var key in keys)
_dict.TryRemove(key, out _);
return this;
}
/// <summary>
/// Removes all cookies from this CookieJar
/// </summary>
public CookieJar Clear() {
_dict.Clear();
return this;
}
/// <summary>
/// Writes this CookieJar to a TextWriter. Useful for persisting to a file.
/// </summary>
public void WriteTo(TextWriter writer) {
foreach (var cookie in _dict.Values)
cookie.WriteTo(writer);
}
/// <summary>
/// Instantiates a CookieJar that was previously persisted using WriteTo.
/// </summary>
public static CookieJar LoadFrom(TextReader reader) {
if (reader == null) return null;
var jar = new CookieJar();
while (reader.Peek() >= 0) {
var cookie = FlurlCookie.LoadFrom(reader);
if (cookie != null)
jar.AddOrReplace(cookie);
}
return jar;
}
/// <summary>
/// Returns a string representing this CookieJar.
/// </summary>
public override string ToString() {
var writer = new StringWriter();
WriteTo(writer);
return writer.ToString();
}
/// <summary>
/// Instantiates a CookieJar that was previously persisted using ToString.
/// </summary>
public static CookieJar LoadFromString(string s) => LoadFrom(new StringReader(s));
/// <inheritdoc/>
public IEnumerator<FlurlCookie> GetEnumerator() => _dict.Values.GetEnumerator();
/// <inheritdoc/>
IEnumerator IEnumerable.GetEnumerator() => _dict.GetEnumerator();
/// <inheritdoc/>
public int Count => _dict.Count;
}
/// <summary>
/// Exception thrown when attempting to add or update an invalid FlurlCookie to a CookieJar.
/// </summary>
public class InvalidCookieException : Exception
{
/// <summary>
/// Creates a new InvalidCookieException.
/// </summary>
public InvalidCookieException(string reason) : base(reason) { }
}
}