Add optional logging of per-resource cookie information
[WebKit-https.git] / Source / WebCore / platform / network / cocoa / CookieCocoa.mm
1 /*
2  * Copyright (C) 2015 Apple, Inc.  All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
14  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
17  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24  */
25
26 #import "config.h"
27 #import "Cookie.h"
28
29 namespace WebCore {
30
31 static Vector<uint16_t> portVectorFromList(NSArray<NSNumber *> *portList)
32 {
33     Vector<uint16_t> ports;
34     ports.reserveInitialCapacity(portList.count);
35
36     for (NSNumber *port : portList)
37         ports.uncheckedAppend(port.unsignedShortValue);
38
39     return ports;
40 }
41
42 static NSString *portStringFromVector(const Vector<uint16_t>& ports)
43 {
44     if (ports.isEmpty())
45         return nil;
46
47     auto *string = [NSMutableString stringWithCapacity:ports.size() * 5];
48
49     for (size_t i = 0; i < ports.size() - 1; ++i)
50         [string appendFormat:@"%" PRIu16 ", ", ports[i]];
51
52     [string appendFormat:@"%" PRIu16, ports.last()];
53
54     return string;
55 }
56
57 static double cookieCreated(NSHTTPCookie *cookie)
58 {
59     id value = cookie.properties[@"Created"];
60
61     auto toCanonicalFormat = [](double referenceFormat) {
62         return 1000.0 * (referenceFormat + NSTimeIntervalSince1970);
63     };
64
65     if ([value isKindOfClass:[NSNumber class]])
66         return toCanonicalFormat(((NSNumber *)value).doubleValue);
67
68     if ([value isKindOfClass:[NSString class]])
69         return toCanonicalFormat(((NSString *)value).doubleValue);
70
71     return 0.0;
72 }
73
74 Cookie::Cookie(NSHTTPCookie *cookie)
75     : Cookie(cookie.name, cookie.value, cookie.domain, cookie.path, cookieCreated(cookie), [cookie.expiresDate timeIntervalSince1970] * 1000.0,
76     cookie.HTTPOnly, cookie.secure, cookie.sessionOnly, cookie.comment, cookie.commentURL, portVectorFromList(cookie.portList))
77 {
78 }
79
80 Cookie::operator NSHTTPCookie *() const
81 {
82     if (isNull())
83         return nil;
84
85     // FIXME: existing APIs do not provide a way to set httpOnly without parsing headers from scratch.
86
87     NSMutableDictionary *properties = [NSMutableDictionary dictionaryWithCapacity:11];
88
89     if (!comment.isNull())
90         [properties setObject:(NSString *)comment forKey:NSHTTPCookieComment];
91
92     if (!commentURL.isNull())
93         [properties setObject:(NSURL *)commentURL forKey:NSHTTPCookieCommentURL];
94
95     if (!domain.isNull())
96         [properties setObject:(NSString *)domain forKey:NSHTTPCookieDomain];
97
98     if (!name.isNull())
99         [properties setObject:(NSString *)name forKey:NSHTTPCookieName];
100
101     if (!path.isNull())
102         [properties setObject:(NSString *)path forKey:NSHTTPCookiePath];
103
104     if (!value.isNull())
105         [properties setObject:(NSString *)value forKey:NSHTTPCookieValue];
106
107     NSDate *expirationDate = [NSDate dateWithTimeIntervalSince1970:expires / 1000.0];
108     auto maxAge = [expirationDate timeIntervalSinceNow];
109     if (maxAge > 0)
110         [properties setObject:[NSString stringWithFormat:@"%f", maxAge] forKey:NSHTTPCookieMaximumAge];
111
112     auto* portString = portStringFromVector(ports);
113     if (portString)
114         [properties setObject:portString forKey:NSHTTPCookiePort];
115
116     if (secure)
117         [properties setObject:@YES forKey:NSHTTPCookieSecure];
118
119     if (session)
120         [properties setObject:@YES forKey:NSHTTPCookieDiscard];
121
122     [properties setObject:@"1" forKey:NSHTTPCookieVersion];
123
124     return [NSHTTPCookie cookieWithProperties:properties];
125 }
126     
127 bool Cookie::operator==(const Cookie& other) const
128 {
129     ASSERT(!name.isHashTableDeletedValue());
130     bool thisNull = isNull();
131     bool otherNull = other.isNull();
132     if (thisNull || otherNull)
133         return thisNull == otherNull;
134     
135     NSHTTPCookie *nsCookie(*this);
136     return [nsCookie isEqual:other];
137 }
138     
139 unsigned Cookie::hash() const
140 {
141     ASSERT(!name.isHashTableDeletedValue());
142     ASSERT(!isNull());
143     NSHTTPCookie *nsCookie(*this);
144     return nsCookie.hash;
145 }
146
147 } // namespace WebCore