1
// Copyright 2010 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: lsong@google.com (Libo Song)
16
// jmarantz@google.com (Joshua Marantz)
18
// The Apache handler for rewriten resources and a couple other Apache hooks.
20
#ifndef NET_INSTAWEB_APACHE_INSTAWEB_HANDLER_H_
21
#define NET_INSTAWEB_APACHE_INSTAWEB_HANDLER_H_
23
#include "net/instaweb/apache/apache_writer.h"
24
#include "net/instaweb/http/public/request_context.h"
25
#include "net/instaweb/rewriter/public/rewrite_options.h"
26
#include "net/instaweb/rewriter/public/rewrite_query.h"
27
#include "pagespeed/apache/apache_fetch.h"
28
#include "pagespeed/kernel/base/basictypes.h"
29
#include "pagespeed/kernel/base/scoped_ptr.h"
30
#include "pagespeed/kernel/base/string.h"
31
#include "pagespeed/kernel/base/string_util.h"
32
#include "pagespeed/kernel/base/thread_system.h"
33
#include "pagespeed/kernel/http/content_type.h"
34
#include "pagespeed/kernel/http/google_url.h"
36
#include "apr_pools.h" // for apr_status_t
37
// The httpd header must be after the instaweb_context.h. Otherwise,
38
// the compiler will complain
39
// "strtoul_is_not_a_portable_function_use_strtol_instead".
42
namespace net_instaweb {
44
class ApacheRequestContext;
45
class ApacheRewriteDriverFactory;
46
class ApacheServerContext;
47
class InPlaceResourceRecorder;
50
class ResponseHeaders;
53
class SystemRewriteOptions;
55
// Context for handling a request, computing options and request headers in
58
// TODO(jmarantz): There are many static methods in this class. Some
59
// of them need to stay that way as they are used as C entry points.
60
// Some are helper methods, and we could adopt a policy of requiring
61
// the static methods to instantiate the class (possibly making its
62
// constructor lighter weight) and then calling them explicitly. For
63
// the time being, we'll leave these static methods with the
64
// non-compliant lower_case_names_with_underscores naming convention
65
// and fix their naming when we update whether they should be static
67
class InstawebHandler {
69
explicit InstawebHandler(request_rec* request);
72
// Any PageSpeed query params are removed.
73
const GoogleUrl& stripped_gurl() const { return stripped_gurl_; }
74
const RequestContextPtr request_context() const { return request_context_; }
75
bool use_custom_options() const { return custom_options_.get() != NULL; }
76
const QueryParams& query_params() { return rewrite_query_.query_params(); }
77
const QueryParams& pagespeed_query_params() {
78
return rewrite_query_.pagespeed_query_params();
80
const QueryParams& pagespeed_option_cookies() {
81
return rewrite_query_.pagespeed_option_cookies();
84
void SetupSpdyConnectionIfNeeded();
85
void RemoveStrippedResponseHeadersFromApacheRequest();
87
// Makes a driver from the request_context and options. Note that
88
// this can only be called once, as it potentially mutates the options
89
// as it transfers ownership of custom_options.
90
RewriteDriver* MakeDriver();
92
// Allocates a Fetch object associated with the current request and
93
// the specified URL. Include in debug_info anything that's cheap to create
94
// and would be informative if something went wrong with the fetch.
95
// If any uses will be from other threads you must set buffered=true to keep
96
// your other thread from getting blocked if our output is being read by a
98
ApacheFetch* MakeFetch(
99
const GoogleString& url, bool buffered, StringPiece debug_info);
101
// Allocates a Fetch object associated with the current request and its URL.
102
// Please read the comment above before setting buffered=false.
103
ApacheFetch* MakeFetch(bool buffered, StringPiece debug_info) {
104
return MakeFetch(original_url_, buffered, debug_info);
107
// Attempts to handle this as a proxied resource (see
108
// MapProxyDomain). Returns false if the proxy handling didn't
109
// occur, and another handler should take over the request.
110
bool HandleAsProxy();
112
// Attempts to handle this as an in-place resource. Returns false if
113
// the in-place handling didn't occur, and another handler should take
115
bool HandleAsInPlace();
117
// Unconditionally handles a resource that looks like a .pagespeed. resource,
118
// whether the result is success or failure.
119
void HandleAsPagespeedResource();
121
// Waits for an outstanding fetch (obtained by MakeFetch) to complete or time
122
// out. Returns true if the fetch completes, false if we time out and abandon
123
// the request. On failure we haven't sent out headers or any other content,
124
// so it's safe to decline this request and another Apache content handler can
128
RequestHeaders* ReleaseRequestHeaders() { return request_headers_.release(); }
130
// Returns the options, whether they were custom-computed due to htaccess
131
// file, query params, or headers, or were the default options for the vhost.
132
const SystemRewriteOptions* options() { return options_; }
134
// Was this request made by mod_pagespeed itself? If so, we should not try to
135
// handle it, just let Apache deal with it like normal.
136
static bool is_pagespeed_subrequest(request_rec* request);
138
// Handle mod_pagespeed-specific requests. Handles both .pagespeed. rewritten
139
// resources and /mod_pagespeed_statistics, /mod_pagespeed_beacon, etc.
140
static apr_status_t instaweb_handler(request_rec* request);
142
// Save the original URL as a request "note" before mod_rewrite has
143
// a chance to corrupt mod_pagespeed's generated URLs, which would
144
// prevent instaweb_handler from being able to decode the resource.
145
static apr_status_t save_url_hook(request_rec *request);
147
// Implementation of the Apache 'translate_name' hook. Used by the actual hook
148
// 'save_url_hook' and directly when we already have the server context.
149
static apr_status_t save_url_in_note(request_rec *request,
150
ApacheServerContext* server_context);
152
// By default, apache imposes limitations on URL segments of around
153
// 256 characters that appear to correspond to filename limitations.
154
// To prevent that, we hook map_to_storage for our own purposes.
155
static apr_status_t instaweb_map_to_storage(request_rec* request);
157
// This must be called on any InPlaceResourceRecorder allocated by
158
// instaweb_handler before calling DoneAndSetHeaders() on it.
159
static void AboutToBeDoneWithRecorder(request_rec* request,
160
InPlaceResourceRecorder* recorder);
163
// Evaluate custom_options based upon global_options, directory-specific
164
// options and query-param/request-header options. Stores computed options
165
// in custom_options_ if needed. Sets options_ to point to the correct
167
void ComputeCustomOptions();
169
static bool IsCompressibleContentType(const char* content_type);
171
static void send_out_headers_and_body(
172
request_rec* request,
173
const ResponseHeaders& response_headers,
174
const GoogleString& output);
176
// Determines whether the url can be handled as a mod_pagespeed or in-place
177
// optimized resource, and handles it, returning true. Success status is
178
// written to the status code in the response headers.
179
static bool handle_as_resource(ApacheServerContext* server_context,
180
request_rec* request,
183
// Write response headers and send out headers and output, including
184
// the option for a custom Content-Type.
185
static void write_handler_response(const StringPiece& output,
186
request_rec* request,
187
ContentType content_type,
188
const StringPiece& cache_control);
189
static void write_handler_response(const StringPiece& output,
190
request_rec* request);
192
// Returns request URL if it was a .pagespeed. rewritten resource URL.
193
// Otherwise returns NULL. Since other Apache modules can change request->uri,
194
// we stow the original request URL in a note. This method reads that note
195
// and thus should return the URL that the browser actually requested (rather
196
// than a mod_rewrite altered URL).
197
static const char* get_instaweb_resource_url(
198
request_rec* request, ApacheServerContext* server_context);
200
// Helper function to support the LogRequestHeadersHandler. Called
201
// once for each header to write header data in a form suitable for
202
// javascript inlining. Used only for tests.
203
static int log_request_headers(void* logging_data, const char* key,
206
static void instaweb_static_handler(request_rec* request,
207
ApacheServerContext* server_context);
209
static apr_status_t instaweb_statistics_handler(
210
request_rec* request, ApacheServerContext* server_context,
211
ApacheRewriteDriverFactory* factory);
213
// Append the query params from a request into data. This just
214
// parses the query params from a request URL. For parsing the query
215
// params from a POST body, use parse_body_from_post(). Return true
216
// if successful, otherwise, returns false and sets ret to the
217
// appropriate status.
218
static bool parse_query_params(const request_rec* request, GoogleString* data,
221
// Read the body from a POST request and append to data. Return true
222
// if successful, otherwise, returns false and sets ret to the
223
// appropriate status.
224
static bool parse_body_from_post(const request_rec* request,
225
GoogleString* data, apr_status_t* ret);
227
static apr_status_t instaweb_beacon_handler(
228
request_rec* request, ApacheServerContext* server_context);
230
static bool IsBeaconUrl(const RewriteOptions::BeaconUrl& beacons,
231
const GoogleUrl& gurl);
233
request_rec* request_;
234
RequestContextPtr request_context_;
235
ApacheRequestContext* apache_request_context_; // owned by request_context_.
236
ApacheServerContext* server_context_;
237
scoped_ptr<RequestHeaders> request_headers_;
238
scoped_ptr<ResponseHeaders> response_headers_;
239
GoogleString original_url_;
240
GoogleUrl stripped_gurl_; // Any PageSpeed query params are removed.
241
scoped_ptr<SystemRewriteOptions> custom_options_;
243
// These options_ can be in one of three states:
244
// - they can point to the config's global_options
245
// - they can point to the custom_options_
246
// - after driver creation, they can point to the rewrite_driver_->options()
247
// Thus this set of options is not owned by this class.
249
// In all three of these states, the pointer and semantics will always be
250
// the same. Only the ownership changes.
251
const SystemRewriteOptions* options_;
252
RewriteDriver* rewrite_driver_;
253
int num_response_attributes_;
254
RewriteQuery rewrite_query_;
257
DISALLOW_COPY_AND_ASSIGN(InstawebHandler);
260
} // namespace net_instaweb
262
#endif // NET_INSTAWEB_APACHE_INSTAWEB_HANDLER_H_