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  package org.apache.hc.client5.http.cache;
28  
29  import java.net.URI;
30  import java.time.Instant;
31  import java.util.Collection;
32  import java.util.HashSet;
33  import java.util.Iterator;
34  import java.util.Locale;
35  import java.util.Set;
36  
37  import org.apache.hc.client5.http.impl.cache.CacheKeyGenerator;
38  import org.apache.hc.client5.http.utils.DateUtils;
39  import org.apache.hc.core5.annotation.Contract;
40  import org.apache.hc.core5.annotation.ThreadingBehavior;
41  import org.apache.hc.core5.http.Header;
42  import org.apache.hc.core5.http.HttpHeaders;
43  import org.apache.hc.core5.http.HttpHost;
44  import org.apache.hc.core5.http.HttpMessage;
45  import org.apache.hc.core5.http.HttpRequest;
46  import org.apache.hc.core5.http.HttpResponse;
47  import org.apache.hc.core5.http.HttpStatus;
48  import org.apache.hc.core5.http.message.BasicHeader;
49  import org.apache.hc.core5.http.message.HeaderGroup;
50  import org.apache.hc.core5.http.message.MessageSupport;
51  import org.apache.hc.core5.util.Args;
52  
53  
54  
55  
56  
57  
58  @Contract(threading = ThreadingBehavior.IMMUTABLE)
59  public class HttpCacheEntryFactory {
60  
61      
62  
63  
64      public static final HttpCacheEntryFactory INSTANCE = new HttpCacheEntryFactory();
65  
66      private static HeaderGroup headers(final Iterator<Header> it) {
67          final HeaderGroup headerGroup = new HeaderGroup();
68          while (it.hasNext()) {
69              headerGroup.addHeader(it.next());
70          }
71          return headerGroup;
72      }
73  
74      HeaderGroup mergeHeaders(final HttpCacheEntry entry, final HttpResponse response) {
75          final HeaderGroup headerGroup = new HeaderGroup();
76          for (final Iterator<Header> it = entry.headerIterator(); it.hasNext(); ) {
77              final Header entryHeader = it.next();
78              final String headerName = entryHeader.getName();
79              if (!response.containsHeader(headerName)) {
80                  headerGroup.addHeader(entryHeader);
81              }
82          }
83          final Set<String> responseHopByHop = MessageSupport.hopByHopConnectionSpecific(response);
84          for (final Iterator<Header> it = response.headerIterator(); it.hasNext(); ) {
85              final Header responseHeader = it.next();
86              final String headerName = responseHeader.getName();
87              if (!responseHopByHop.contains(headerName.toLowerCase(Locale.ROOT))) {
88                  headerGroup.addHeader(responseHeader);
89              }
90          }
91          return headerGroup;
92      }
93  
94      
95  
96  
97      static HeaderGroup filterHopByHopHeaders(final HttpMessage message) {
98          final Set<String> hopByHop = MessageSupport.hopByHopConnectionSpecific(message);
99          final HeaderGroup headerGroup = new HeaderGroup();
100         for (final Iterator<Header> it = message.headerIterator(); it.hasNext(); ) {
101             final Header header = it.next();
102             if (!hopByHop.contains(header.getName())) {
103                 headerGroup.addHeader(header);
104             }
105         }
106         return headerGroup;
107     }
108 
109     static void ensureDate(final HeaderGroup headers, final Instant instant) {
110         if (!headers.containsHeader(HttpHeaders.DATE)) {
111             headers.addHeader(new BasicHeader(HttpHeaders.DATE, DateUtils.formatStandardDate(instant)));
112         }
113     }
114 
115     
116 
117 
118 
119 
120 
121 
122 
123 
124     public HttpCacheEntry createRoot(final HttpCacheEntry latestVariant,
125                                      final Collection<String> variants) {
126         Args.notNull(latestVariant, "Request");
127         Args.notNull(variants, "Variants");
128         return new HttpCacheEntry(
129                 latestVariant.getRequestInstant(),
130                 latestVariant.getResponseInstant(),
131                 latestVariant.getRequestMethod(),
132                 latestVariant.getRequestURI(),
133                 headers(latestVariant.requestHeaderIterator()),
134                 latestVariant.getStatus(),
135                 headers(latestVariant.headerIterator()),
136                 null,
137                 variants);
138     }
139 
140     
141 
142 
143 
144 
145 
146 
147 
148 
149 
150     public HttpCacheEntry create(final Instant requestInstant,
151                                  final Instant responseInstant,
152                                  final HttpHost host,
153                                  final HttpRequest request,
154                                  final HttpResponse response,
155                                  final Resource resource) {
156         Args.notNull(requestInstant, "Request instant");
157         Args.notNull(responseInstant, "Response instant");
158         Args.notNull(host, "Host");
159         Args.notNull(request, "Request");
160         Args.notNull(response, "Origin response");
161         final String s = CacheKeyGenerator.getRequestUri(host, request);
162         final URI uri = CacheKeyGenerator.normalize(s);
163         final HeaderGroup requestHeaders = filterHopByHopHeaders(request);
164         
165         requestHeaders.removeHeaders(HttpHeaders.AUTHORIZATION);
166         final HeaderGroup responseHeaders = filterHopByHopHeaders(response);
167         ensureDate(responseHeaders, responseInstant);
168         return new HttpCacheEntry(
169                 requestInstant,
170                 responseInstant,
171                 request.getMethod(),
172                 uri.toASCIIString(),
173                 requestHeaders,
174                 response.getCode(),
175                 responseHeaders,
176                 resource,
177                 null);
178     }
179 
180     
181 
182 
183 
184 
185 
186 
187 
188 
189 
190 
191     public HttpCacheEntry createUpdated(
192             final Instant requestInstant,
193             final Instant responseInstant,
194             final HttpHost host,
195             final HttpRequest request,
196             final HttpResponse response,
197             final HttpCacheEntry entry) {
198         Args.notNull(requestInstant, "Request instant");
199         Args.notNull(responseInstant, "Response instant");
200         Args.notNull(response, "Origin response");
201         Args.check(response.getCode() == HttpStatus.SC_NOT_MODIFIED,
202                 "Response must have 304 status code");
203         Args.notNull(entry, "Cache entry");
204         if (HttpCacheEntry.isNewer(entry, response)) {
205             return entry;
206         }
207         final String s = CacheKeyGenerator.getRequestUri(host, request);
208         final URI uri = CacheKeyGenerator.normalize(s);
209         final HeaderGroup requestHeaders = filterHopByHopHeaders(request);
210         
211         requestHeaders.removeHeaders(HttpHeaders.AUTHORIZATION);
212         final HeaderGroup mergedHeaders = mergeHeaders(entry, response);
213         return new HttpCacheEntry(
214                 requestInstant,
215                 responseInstant,
216                 request.getMethod(),
217                 uri.toASCIIString(),
218                 requestHeaders,
219                 entry.getStatus(),
220                 mergedHeaders,
221                 entry.getResource(),
222                 null);
223     }
224 
225     
226 
227 
228 
229     public HttpCacheEntry copy(final HttpCacheEntry entry) {
230         if (entry == null) {
231             return null;
232         }
233         return new HttpCacheEntry(
234                 entry.getRequestInstant(),
235                 entry.getResponseInstant(),
236                 entry.getRequestMethod(),
237                 entry.getRequestURI(),
238                 headers(entry.requestHeaderIterator()),
239                 entry.getStatus(),
240                 headers(entry.headerIterator()),
241                 entry.getResource(),
242                 entry.hasVariants() ? new HashSet<>(entry.getVariants()) : null);
243     }
244 
245 }