1
// Copyright 2013 Google Inc.
3
// Licensed under the Apache License, Version 2.0 (the "License");
4
// you may not use this file except in compliance with the License.
5
// You may obtain a copy of the License at
7
// http://www.apache.org/licenses/LICENSE-2.0
9
// Unless required by applicable law or agreed to in writing, software
10
// distributed under the License is distributed on an "AS IS" BASIS,
11
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
// See the License for the specific language governing permissions and
13
// limitations under the License.
15
// Author: sligocki@google.com (Shawn Ligocki)
17
#ifndef NET_INSTAWEB_SYSTEM_PUBLIC_IN_PLACE_RESOURCE_RECORDER_H_
18
#define NET_INSTAWEB_SYSTEM_PUBLIC_IN_PLACE_RESOURCE_RECORDER_H_
20
#include "net/instaweb/http/public/async_fetch.h"
21
#include "net/instaweb/http/public/http_value.h"
22
#include "net/instaweb/http/public/inflating_fetch.h"
23
#include "net/instaweb/http/public/request_context.h"
24
#include "net/instaweb/util/public/basictypes.h"
25
#include "net/instaweb/util/public/string.h"
26
#include "net/instaweb/util/public/string_util.h"
27
#include "net/instaweb/util/public/writer.h"
28
#include "pagespeed/kernel/base/atomic_int32.h"
29
#include "pagespeed/kernel/http/http_options.h"
30
#include "pagespeed/kernel/http/request_headers.h"
32
namespace net_instaweb {
36
class ResponseHeaders;
40
// Records a copy of a resource streamed through it and saves the result to
41
// the cache if it's cacheable. Used in the In-Place Resource Optimization
42
// (IPRO) flow to get resources into the cache.
43
class InPlaceResourceRecorder : public Writer {
46
// Headers should only be used to determine if context was gzip'd by
50
// Headers are complete.
54
// Does not take ownership of request_headers, cache nor handler.
55
// Like other callbacks, InPlaceResourceRecorder is self-owned and will
56
// delete itself when DoneAndSetHeaders() is called.
57
InPlaceResourceRecorder(
58
const RequestContextPtr& request_context,
59
StringPiece url, StringPiece fragment,
60
const RequestHeaders::Properties& request_properties,
61
int max_response_bytes, int max_concurrent_recordings,
62
HTTPCache* cache, Statistics* statistics, MessageHandler* handler);
64
// Normally you should use DoneAndSetHeaders rather than deleting this
66
virtual ~InPlaceResourceRecorder();
68
static void InitStats(Statistics* statistics);
70
// These take a handler for compatibility with the Writer API, but the handler
72
virtual bool Write(const StringPiece& contents, MessageHandler* handler);
74
// Flush is a no-op because we have to buffer up the whole contents before
76
virtual bool Flush(MessageHandler* handler) { return true; }
78
// Sometimes the response headers prohibit IPRO:
79
// * If it's not an IPRO content type.
80
// * If it's not served as cacheable.
81
// * If there's a content length, and it's over our max.
82
// In these cases, shift into the failed state and stop any resource
85
// At this time we might also realize that there are too many IPRO recordings
86
// going on and skip IPRO for that reason. In that case we don't mark the
87
// resource as not ipro-cacheable.
89
// You must call ConsiderResponseHeaders() with whatever information is
90
// available before payload. If it's only enough to determine if content
91
// is gzip'ed, pass in kPreliminaryHeaders. If it's the complete final
92
// headers, pass in kFullHeaders.
94
// Call DoneAndSetHeaders() after the entire payload and headers are
95
// available. Note that only Content-Encoding: from ConsiderResponseHeaders
96
// will be used to determine whether to gunzip content or not. This is done
97
// since in Apache we can only capture the full headers after mod_deflate has
98
// already run, while content is captured before.
100
// Does not take ownership of response_headers.
101
void ConsiderResponseHeaders(HeadersKind headers_kind,
102
ResponseHeaders* response_headers);
104
// Call if something went wrong. The results will not be added to cache. You
105
// still need to call DoneAndSetHeaders().
106
void Fail() { failure_ = true; }
108
// Call when finished and the final response headers are known.
109
// Because of Apache's quirky filter order, we cannot get both the
110
// uncompressed final contents and the complete headers at the same time.
112
// Set entire_response_received to true if you know that the response data fed
113
// into Write() is complete. For example, if the browser cancelled the
114
// download and so this is a partial response, set entire_response_received to
115
// false so we know not to cache it.
117
// Does not take ownership of response_headers.
119
// Deletes itself. Do not use object after calling DoneAndSetHeaders().
120
void DoneAndSetHeaders(ResponseHeaders* response_headers,
121
bool entire_response_received);
123
const GoogleString& url() const { return url_; }
124
MessageHandler* handler() { return handler_; }
126
bool failed() { return failure_; }
127
bool limit_active_recordings() { return max_concurrent_recordings_ != 0; }
129
const HttpOptions& http_options() const { return http_options_; }
132
class HTTPValueFetch : public AsyncFetchUsingWriter {
134
HTTPValueFetch(const RequestContextPtr& request_context, HTTPValue* value)
135
: AsyncFetchUsingWriter(request_context, value) {}
136
virtual void HandleDone(bool /*ok*/) {}
137
virtual void HandleHeadersComplete() {}
140
bool IsIproContentType(ResponseHeaders* response_headers);
142
void DroppedDueToSize();
144
const GoogleString url_;
145
const GoogleString fragment_;
146
const RequestHeaders::Properties request_properties_;
147
const HttpOptions http_options_;
149
int64 max_response_bytes_;
150
const int max_concurrent_recordings_;
152
HTTPValue resource_value_;
153
HTTPValueFetch write_to_resource_value_;
154
InflatingFetch inflating_fetch_;
157
MessageHandler* handler_;
159
Variable* num_resources_;
160
Variable* num_inserted_into_cache_;
161
Variable* num_not_cacheable_;
162
Variable* num_failed_;
163
Variable* num_dropped_due_to_load_;
164
Variable* num_dropped_due_to_size_;
166
// Track how many simultaneous recordings are underway in this process. Not
167
// used when max_concurrent_recordings_ == 0 (unlimited).
168
static AtomicInt32 active_recordings_;
169
// The status code from the response headers for RememberNotCacheable.
171
// Something went wrong and this resource shouldn't be saved.
174
// Track that ConsiderResponseHeaders() is called with full headers
176
bool full_response_headers_considered_;
178
// Track that ConsiderResponseHeaders() is called before DoneAndSetHeaders()
179
bool consider_response_headers_called_;
181
DISALLOW_COPY_AND_ASSIGN(InPlaceResourceRecorder);
184
} // namespace net_instaweb
186
#endif // NET_INSTAWEB_SYSTEM_PUBLIC_IN_PLACE_RESOURCE_RECORDER_H_