/* * Copyright 2012 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: jefftk@google.com (Jeff Kaufman) // // Collects output from pagespeed and buffers it until nginx asks for it. // Notifies nginx via NgxEventConnection to call ReadCallback() when // the headers are computed, when a flush should be performed, and when done. // // - nginx creates a base fetch and passes it to a new proxy fetch. // - The proxy fetch manages rewriting and thread complexity, and through // several chained steps passes rewritten html to HandleWrite(). // - Written data is buffered. // - When HandleHeadersComplete(), HandleFlush(), or HandleDone() is called by // PSOL, events are written to NgxEventConnection which will end up being // handled by ReadCallback() on nginx's thread. // When applicable, request processing will be continued via a call to // ps_base_fetch_handler(). // - ps_base_fetch_handler() will pull the header and body bytes from PSOL // via CollectAccumulatedWrites() and write those to the module's output. // // This class is referred to in three places: the proxy fetch, nginx's request, // and pending events written to the associated NgxEventConnection. It must stay // alive until the proxy fetch and nginx request are finished, and no more // events are pending. // - The proxy fetch will call Done() to indicate this. // - nginx will call Detach() when the associated request is handled // completely (e.g. the request context is about to be destroyed). // - ReadCallback() will call DecrementRefCount() on instances associated to // events it handles. // // When the last reference is dropped, this class will delete itself. // // TODO(jmarantz): consider sharing the cache-invalidation infrastructure // with ApacheFetch, using a common base class. #ifndef NGX_BASE_FETCH_H_ #define NGX_BASE_FETCH_H_ extern "C" { #include } #include #include "ngx_pagespeed.h" #include "ngx_event_connection.h" #include "ngx_server_context.h" #include "net/instaweb/http/public/async_fetch.h" #include "net/instaweb/rewriter/public/rewrite_options.h" #include "pagespeed/kernel/base/string.h" #include "pagespeed/kernel/http/headers.h" namespace net_instaweb { enum NgxBaseFetchType { kIproLookup, kHtmlTransform, kPageSpeedResource, kAdminPage, kPageSpeedProxy }; class NgxBaseFetch : public AsyncFetch { public: NgxBaseFetch(StringPiece url, ngx_http_request_t* r, NgxServerContext* server_context, const RequestContextPtr& request_ctx, PreserveCachingHeaders preserve_caching_headers, NgxBaseFetchType base_fetch_type, const RewriteOptions* options); virtual ~NgxBaseFetch(); // Statically initializes event_connection, require for PSOL and nginx to // communicate. static bool Initialize(ngx_cycle_t* cycle); // Attempts to finish up request processing queued up in the named pipe and // PSOL for a fixed amount of time. If time is up, a fast and rough shutdown // is attempted. // Statically terminates and NULLS event_connection. static void Terminate(); static void ReadCallback(const ps_event_data& data); // Puts a chain in link_ptr if we have any output data buffered. Returns // NGX_OK on success, NGX_ERROR on errors. If there's no data to send, sends // data only if Done() has been called. Indicates the end of output by // setting last_buf on the last buffer in the chain. // // Sets link_ptr to a chain of as many buffers are needed for the output. // // Called by nginx in response to seeing a byte on the pipe. ngx_int_t CollectAccumulatedWrites(ngx_chain_t** link_ptr); // Copies response headers into headers_out. // // Called by nginx before calling CollectAccumulatedWrites() for the first // time for resource fetches. Not called at all for proxy fetches. ngx_int_t CollectHeaders(ngx_http_headers_out_t* headers_out); // Called by nginx to decrement the refcount. int DecrementRefCount(); // Called by pagespeed to increment the refcount. int IncrementRefCount(); // Detach() is called when the nginx side releases this base fetch. It // sets detached_ to true and decrements the refcount. We need to know // this to be able to handle events which nginx request context has been // released while the event was in-flight. void Detach() { detached_ = true; DecrementRefCount(); } bool detached() { return detached_; } ngx_http_request_t* request() { return request_; } NgxBaseFetchType base_fetch_type() { return base_fetch_type_; } bool IsCachedResultValid(const ResponseHeaders& headers) override; private: virtual bool HandleWrite(const StringPiece& sp, MessageHandler* handler); virtual bool HandleFlush(MessageHandler* handler); virtual void HandleHeadersComplete(); virtual void HandleDone(bool success); // Indicate to nginx that we would like it to call // CollectAccumulatedWrites(). void RequestCollection(char type); // Lock must be acquired first. // Returns: // NGX_ERROR: failure // NGX_AGAIN: still has buffer to send, need to checkout link_ptr // NGX_OK: done, HandleDone has been called // Allocates an nginx buffer, copies our buffer_ contents into it, clears // buffer_. ngx_int_t CopyBufferToNginx(ngx_chain_t** link_ptr); void Lock(); void Unlock(); // Called by Done() and Release(). Decrements our reference count, and if // it's zero we delete ourself. int DecrefAndDeleteIfUnreferenced(); static NgxEventConnection* event_connection; // Live count of NgxBaseFetch instances that are currently in use. static int active_base_fetches; GoogleString url_; ngx_http_request_t* request_; GoogleString buffer_; NgxServerContext* server_context_; const RewriteOptions* options_; bool need_flush_; bool done_called_; bool last_buf_sent_; // How many active references there are to this fetch. Starts at two, // decremented once when Done() is called and once when Detach() is called. // Incremented for each event written by pagespeed for this NgxBaseFetch, and // decremented on the nginx side for each event read for it. int references_; pthread_mutex_t mutex_; NgxBaseFetchType base_fetch_type_; PreserveCachingHeaders preserve_caching_headers_; // Set to true just before the nginx side releases its reference bool detached_; bool suppress_; DISALLOW_COPY_AND_ASSIGN(NgxBaseFetch); }; } // namespace net_instaweb #endif // NGX_BASE_FETCH_H_