| // Copyright 2013 Google Inc. |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| // |
| // Author: sligocki@google.com (Shawn Ligocki) |
| |
| #ifndef PAGESPEED_SYSTEM_IN_PLACE_RESOURCE_RECORDER_H_ |
| #define PAGESPEED_SYSTEM_IN_PLACE_RESOURCE_RECORDER_H_ |
| |
| #include "net/instaweb/http/public/async_fetch.h" |
| #include "net/instaweb/http/public/http_value.h" |
| #include "net/instaweb/http/public/inflating_fetch.h" |
| #include "net/instaweb/http/public/request_context.h" |
| #include "pagespeed/kernel/base/atomic_int32.h" |
| #include "pagespeed/kernel/base/basictypes.h" |
| #include "pagespeed/kernel/base/string.h" |
| #include "pagespeed/kernel/base/string_util.h" |
| #include "pagespeed/kernel/base/writer.h" |
| #include "pagespeed/kernel/http/http_options.h" |
| #include "pagespeed/kernel/http/request_headers.h" |
| |
| namespace net_instaweb { |
| |
| class HTTPCache; |
| class MessageHandler; |
| class ResponseHeaders; |
| class Statistics; |
| class Variable; |
| |
| // Records a copy of a resource streamed through it and saves the result to |
| // the cache if it's cacheable. Used in the In-Place Resource Optimization |
| // (IPRO) flow to get resources into the cache. |
| class InPlaceResourceRecorder : public Writer { |
| public: |
| enum HeadersKind { |
| // Headers should only be used to determine if context was gzip'd by |
| // a reverse proxy. |
| kPreliminaryHeaders, |
| |
| // Headers are complete. |
| kFullHeaders |
| }; |
| |
| // Does not take ownership of request_headers, cache nor handler. |
| // Like other callbacks, InPlaceResourceRecorder is self-owned and will |
| // delete itself when DoneAndSetHeaders() is called. |
| InPlaceResourceRecorder( |
| const RequestContextPtr& request_context, |
| StringPiece url, StringPiece fragment, |
| const RequestHeaders::Properties& request_properties, |
| int max_response_bytes, int max_concurrent_recordings, |
| HTTPCache* cache, Statistics* statistics, MessageHandler* handler); |
| |
| // Normally you should use DoneAndSetHeaders rather than deleting this |
| // directly. |
| virtual ~InPlaceResourceRecorder(); |
| |
| static void InitStats(Statistics* statistics); |
| |
| // These take a handler for compatibility with the Writer API, but the handler |
| // is not used. |
| virtual bool Write(const StringPiece& contents, MessageHandler* handler); |
| |
| // Flush is a no-op because we have to buffer up the whole contents before |
| // writing to cache. |
| virtual bool Flush(MessageHandler* handler) { return true; } |
| |
| // Sometimes the response headers prohibit IPRO: |
| // * If it's not an IPRO content type. |
| // * If it's not served as cacheable. |
| // * If there's a content length, and it's over our max. |
| // In these cases, shift into the failed state and stop any resource |
| // recording. |
| // |
| // At this time we might also realize that there are too many IPRO recordings |
| // going on and skip IPRO for that reason. In that case we don't mark the |
| // resource as not ipro-cacheable. |
| // |
| // You must call ConsiderResponseHeaders() with whatever information is |
| // available before payload. If it's only enough to determine if content |
| // is gzip'ed, pass in kPreliminaryHeaders. If it's the complete final |
| // headers, pass in kFullHeaders. |
| // |
| // Call DoneAndSetHeaders() after the entire payload and headers are |
| // available. Note that only Content-Encoding: from ConsiderResponseHeaders |
| // will be used to determine whether to gunzip content or not. This is done |
| // since in Apache we can only capture the full headers after mod_deflate has |
| // already run, while content is captured before. |
| // |
| // Does not take ownership of response_headers. |
| void ConsiderResponseHeaders(HeadersKind headers_kind, |
| ResponseHeaders* response_headers); |
| |
| // Call if something went wrong. The results will not be added to cache. You |
| // still need to call DoneAndSetHeaders(). |
| void Fail() { failure_ = true; } |
| |
| // Call when finished and the final response headers are known. |
| // Because of Apache's quirky filter order, we cannot get both the |
| // uncompressed final contents and the complete headers at the same time. |
| // |
| // Set entire_response_received to true if you know that the response data fed |
| // into Write() is complete. For example, if the browser cancelled the |
| // download and so this is a partial response, set entire_response_received to |
| // false so we know not to cache it. |
| // |
| // Does not take ownership of response_headers. |
| // |
| // Deletes itself. Do not use object after calling DoneAndSetHeaders(). |
| void DoneAndSetHeaders(ResponseHeaders* response_headers, |
| bool entire_response_received); |
| |
| const GoogleString& url() const { return url_; } |
| MessageHandler* handler() { return handler_; } |
| |
| bool failed() { return failure_; } |
| bool limit_active_recordings() { return max_concurrent_recordings_ != 0; } |
| |
| const HttpOptions& http_options() const { return http_options_; } |
| |
| private: |
| class HTTPValueFetch : public AsyncFetchUsingWriter { |
| public: |
| HTTPValueFetch(const RequestContextPtr& request_context, HTTPValue* value) |
| : AsyncFetchUsingWriter(request_context, value) {} |
| virtual void HandleDone(bool /*ok*/) {} |
| virtual void HandleHeadersComplete() {} |
| }; |
| |
| bool IsIproContentType(ResponseHeaders* response_headers); |
| |
| void DroppedDueToSize(); |
| void DroppedAsUncacheable(); |
| |
| const GoogleString url_; |
| const GoogleString fragment_; |
| const RequestHeaders::Properties request_properties_; |
| const HttpOptions http_options_; |
| |
| int64 max_response_bytes_; |
| const int max_concurrent_recordings_; |
| |
| HTTPValue resource_value_; |
| HTTPValueFetch write_to_resource_value_; |
| InflatingFetch inflating_fetch_; |
| |
| HTTPCache* cache_; |
| MessageHandler* handler_; |
| |
| Variable* num_resources_; |
| Variable* num_inserted_into_cache_; |
| Variable* num_not_cacheable_; |
| Variable* num_failed_; |
| Variable* num_dropped_due_to_load_; |
| Variable* num_dropped_due_to_size_; |
| |
| // Track how many simultaneous recordings are underway in this process. Not |
| // used when max_concurrent_recordings_ == 0 (unlimited). |
| static AtomicInt32 active_recordings_; |
| // The status code from the response headers for RememberNotCacheable. |
| int status_code_; |
| // Something went wrong and this resource shouldn't be saved. |
| bool failure_; |
| |
| // Track that ConsiderResponseHeaders() is called with full headers |
| // exactly once. |
| bool full_response_headers_considered_; |
| |
| // Track that ConsiderResponseHeaders() is called before DoneAndSetHeaders() |
| bool consider_response_headers_called_; |
| |
| DISALLOW_COPY_AND_ASSIGN(InPlaceResourceRecorder); |
| }; |
| |
| } // namespace net_instaweb |
| |
| #endif // PAGESPEED_SYSTEM_IN_PLACE_RESOURCE_RECORDER_H_ |