diff --git a/README.md b/README.md index b442a15b3..cf96f2990 100644 --- a/README.md +++ b/README.md @@ -6,12 +6,36 @@ This is the [nginx](http://nginx.org/) port of ## How to build nginx does not support dynamic loading of modules. You need to add -ngx_pagespeed as a build time dependency. +ngx_pagespeed as a build time dependency, and to do that you have to first build +the pagespeed optimization library. + +First build mod_pagespeed, following these instructions: +http://code.google.com/p/modpagespeed/wiki/HowToBuild + +Then move the mod_pagespeed directory to a parallel directory to your +ngx_pagespeed checkout: + + $ cd /path/to/ngx_pagespeed + $ mv /where/you/built/mod_pagespeed /path/to/mod_pagespeed + +Now build nginx: $ cd /path/to/nginx $ auto/configure --add-module=/path/to/ngx_pagespeed $ make install +While ngx_pagespeed doesn't need to be anywhere specific in relation to nginx, +the mod_pagespeed directory and the ngx_pagespeed directory must have the same +parent. + ## How to use -TODO +To your nginx.conf add to the main block or to a server or location block: + + pagespeed on; + +Then fetch a page and note that it adds a comment: + + + +That's all it does so far. \ No newline at end of file diff --git a/config b/config index 84082dd6b..65147840c 100644 --- a/config +++ b/config @@ -12,12 +12,33 @@ # See the License for the specific language governing permissions and # limitations under the License. -ngx_addon_name=ngx_pagespeed +this_dir="$(dirname ${BASH_SOURCE[0]})" +mod_pagespeed_dir="$this_dir/../mod_pagespeed/src/out/Release" +ngx_feature="psol" +ngx_feature_name="" +ngx_feature_run=no +ngx_feature_incs="#include \"net/instaweb/public/version.h\"" +pagespeed_include="$mod_pagespeed_dir/obj/gen" +ngx_feature_path="$pagespeed_include" +pagespeed_lib_dir="$mod_pagespeed_dir/obj.target/net/instaweb/" +pagespeed_libs="-linstaweb_automatic -L $pagespeed_lib_dir -lstdc++" +ngx_feature_libs="$pagespeed_libs" +ngx_feature_test="char c = MOD_PAGESPEED_VERSION_STRING[0]" -NGX_ADDON_SRCS="$NGX_ADDON_SRCS $ngx_addon_dir/src/ngx_pagespeed.c" -HTTP_AUX_FILTER_MODULES="$HTTP_AUX_FILTER_MODULES $ngx_addon_name" +# Test whether we have pagespeed and can compile and link against it. +. "$this_dir/cpp_feature" -# TODO link against libpagespeed / libpsol -# CORE_LIBS="$CORE_LIBS -lpagespeed" +if [ $ngx_found = yes ]; then + ngx_addon_name=ngx_pagespeed + NGX_ADDON_SRCS="$NGX_ADDON_SRCS $ngx_addon_dir/src/ngx_pagespeed.cc" + HTTP_AUX_FILTER_MODULES="$HTTP_AUX_FILTER_MODULES $ngx_addon_name" + CORE_LIBS="$CORE_LIBS $pagespeed_libs" + CORE_INCS="$CORE_INCS $pagespeed_include" +else + cat << END +$0: error: module ngx_pagespeed requires the pagespeed optimization library +END + exit 1 +fi have=NGX_PAGESPEED . auto/have diff --git a/cpp_feature b/cpp_feature new file mode 100644 index 000000000..4eb39859e --- /dev/null +++ b/cpp_feature @@ -0,0 +1,122 @@ +# Copyright (C) Igor Sysoev +# Copyright (C) Nginx, Inc. +# 2012-10-01 Modified from auto/feature by jefftk to support c++ test files. + +echo $ngx_n "checking for $ngx_feature ...$ngx_c" + +cat << END >> $NGX_AUTOCONF_ERR + +---------------------------------------- +checking for $ngx_feature + +END + +ngx_found=no + +if test -n "$ngx_feature_name"; then + ngx_have_feature=`echo $ngx_feature_name \ + | tr abcdefghijklmnopqrstuvwxyz ABCDEFGHIJKLMNOPQRSTUVWXYZ` +fi + +if test -n "$ngx_feature_path"; then + for ngx_temp in $ngx_feature_path; do + ngx_feature_inc_path="$ngx_feature_inc_path -I $ngx_temp" + done +fi + +cat << END > $NGX_AUTOTEST.cc + +#include +$NGX_INCLUDE_UNISTD_H +$ngx_feature_incs + +int main() { + $ngx_feature_test; + return 0; +} + +END + + +ngx_test="$CC $CC_TEST_FLAGS $CC_AUX_FLAGS $ngx_feature_inc_path \ + -o $NGX_AUTOTEST $NGX_AUTOTEST.cc $NGX_TEST_LD_OPT $ngx_feature_libs" + +ngx_feature_inc_path= + +eval "/bin/sh -c \"$ngx_test\" >> $NGX_AUTOCONF_ERR 2>&1" + + +if [ -x $NGX_AUTOTEST ]; then + + case "$ngx_feature_run" in + + yes) + # /bin/sh is used to intercept "Killed" or "Abort trap" messages + if /bin/sh -c $NGX_AUTOTEST >> $NGX_AUTOCONF_ERR 2>&1; then + echo " found" + ngx_found=yes + + if test -n "$ngx_feature_name"; then + have=$ngx_have_feature . auto/have + fi + + else + echo " found but is not working" + fi + ;; + + value) + # /bin/sh is used to intercept "Killed" or "Abort trap" messages + if /bin/sh -c $NGX_AUTOTEST >> $NGX_AUTOCONF_ERR 2>&1; then + echo " found" + ngx_found=yes + + cat << END >> $NGX_AUTO_CONFIG_H + +#ifndef $ngx_feature_name +#define $ngx_feature_name `$NGX_AUTOTEST` +#endif + +END + else + echo " found but is not working" + fi + ;; + + bug) + # /bin/sh is used to intercept "Killed" or "Abort trap" messages + if /bin/sh -c $NGX_AUTOTEST >> $NGX_AUTOCONF_ERR 2>&1; then + echo " not found" + + else + echo " found" + ngx_found=yes + + if test -n "$ngx_feature_name"; then + have=$ngx_have_feature . auto/have + fi + fi + ;; + + *) + echo " found" + ngx_found=yes + + if test -n "$ngx_feature_name"; then + have=$ngx_have_feature . auto/have + fi + ;; + + esac + +else + echo " not found" + + echo "----------" >> $NGX_AUTOCONF_ERR + cat $NGX_AUTOTEST.cc >> $NGX_AUTOCONF_ERR + echo "----------" >> $NGX_AUTOCONF_ERR + echo $ngx_test >> $NGX_AUTOCONF_ERR + echo "----------" >> $NGX_AUTOCONF_ERR +fi + +rm $NGX_AUTOTEST* diff --git a/src/ngx_pagespeed.c b/src/ngx_pagespeed.c deleted file mode 100644 index 534089d41..000000000 --- a/src/ngx_pagespeed.c +++ /dev/null @@ -1,51 +0,0 @@ -/* - * 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. - */ - -#include -#include -#include - -ngx_module_t ngx_pagespeed; - -static ngx_http_module_t ctx = { - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL -}; - -static ngx_command_t cmds[] = { - ngx_null_command -}; - -ngx_module_t ngx_pagespeed = { - NGX_MODULE_V1, - &ctx, - cmds, - NGX_HTTP_MODULE, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NGX_MODULE_V1_PADDING -}; diff --git a/src/ngx_pagespeed.cc b/src/ngx_pagespeed.cc new file mode 100644 index 000000000..84a6f706f --- /dev/null +++ b/src/ngx_pagespeed.cc @@ -0,0 +1,196 @@ +/* + * 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. + */ + +/* + * Usage: + * server { + * pagespeed on|off; + * } + */ + +extern "C" { + #include + #include + #include +} + +#include "net/instaweb/public/version.h" + +extern ngx_module_t ngx_pagespeed; + +typedef struct { + ngx_flag_t active; +} ngx_http_pagespeed_loc_conf_t; + +static ngx_command_t ngx_http_pagespeed_commands[] = { + { ngx_string("pagespeed"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, + ngx_conf_set_flag_slot, + NGX_HTTP_LOC_CONF_OFFSET, + offsetof(ngx_http_pagespeed_loc_conf_t, active), + NULL }, + + ngx_null_command +}; + +static void* +ngx_http_pagespeed_create_loc_conf(ngx_conf_t* cf) +{ + ngx_http_pagespeed_loc_conf_t* conf; + + conf = static_cast( + ngx_pcalloc(cf->pool, sizeof(ngx_http_pagespeed_loc_conf_t))); + if (conf == NULL) { + return NGX_CONF_ERROR; + } + conf->active = NGX_CONF_UNSET; + return conf; +} + +static char* +ngx_http_pagespeed_merge_loc_conf(ngx_conf_t* cf, void* parent, void* child) +{ + ngx_http_pagespeed_loc_conf_t* prev = + static_cast(parent); + ngx_http_pagespeed_loc_conf_t* conf = + static_cast(child); + + ngx_conf_merge_value(conf->active, prev->active, 0); // Default off. + + return NGX_CONF_OK; +} + +static ngx_http_output_header_filter_pt ngx_http_next_header_filter; +static ngx_http_output_body_filter_pt ngx_http_next_body_filter; + +static +ngx_int_t ngx_http_pagespeed_header_filter(ngx_http_request_t* r) +{ + // We're adding content below, so switch to 'Transfer-Encoding: chunked' and + // calculate on the fly. + ngx_http_clear_content_length(r); + return ngx_http_next_header_filter(r); +} + +// Add a buffer to the end of the buffer chain indicating that we were processed +// through ngx_pagespeed. +static +ngx_int_t ngx_http_pagespeed_body_filter(ngx_http_request_t* r, ngx_chain_t* in) +{ + // Find the end of the buffer chain. + ngx_chain_t* chain_link; + int chain_contains_last_buffer = 0; + for ( chain_link = in; chain_link != NULL; chain_link = chain_link->next ) { + if (chain_link->buf->last_buf) { + chain_contains_last_buffer = 1; + if (chain_link->next != NULL) { + ngx_log_error(NGX_LOG_ERR, (r)->connection->log, 0, + "Chain link thinks its last but has a child."); + return NGX_ERROR; + } + break; // Chain link now is the last link in the chain. + } + } + + if (!chain_contains_last_buffer) { + // None of the buffers had last_buf set, meaning we have an incomplete chain + // and are still waiting to get the final buffer. Let other body filters + // act on the buffers we have so far and wait until we're called again with + // the last buffer. + return ngx_http_next_body_filter(r, in); + } + + // Prepare a new buffer to put the note into. + ngx_buf_t* b = static_cast(ngx_calloc_buf(r->pool)); + if (b == NULL) { + return NGX_ERROR; + } + + // Write to the new buffer. + const char note[] = "\n"; + int note_len = strlen(note); + b->start = b->pos = static_cast(ngx_pnalloc(r->pool, note_len)); + strncpy((char*)b->pos, note, note_len); + b->end = b->last = b->pos + note_len; + b->temporary = 1; + + // Link the new buffer into the buffer chain. + ngx_chain_t* added_link = static_cast( + ngx_alloc_chain_link(r->pool)); + if (added_link == NULL) { + return NGX_ERROR; + } + + added_link->buf = b; + + // Add our new link to the buffer chain. + added_link->next = NULL; + chain_link->next = added_link; + + // Mark our new link as the end of the chain. + chain_link->buf->last_buf = 0; + added_link->buf->last_buf = 1; + chain_link->buf->last_in_chain = 0; + added_link->buf->last_in_chain = 1; + + return ngx_http_next_body_filter(r, in); +} + +static ngx_int_t +ngx_http_pagespeed_init(ngx_conf_t* cf) +{ + ngx_http_pagespeed_loc_conf_t* pagespeed_config; + pagespeed_config = static_cast( + ngx_http_conf_get_module_loc_conf(cf, ngx_pagespeed)); + + if (pagespeed_config->active) { + ngx_http_next_header_filter = ngx_http_top_header_filter; + ngx_http_top_header_filter = ngx_http_pagespeed_header_filter; + + ngx_http_next_body_filter = ngx_http_top_body_filter; + ngx_http_top_body_filter = ngx_http_pagespeed_body_filter; + } + + return NGX_OK; +} + +static ngx_http_module_t ngx_http_pagespeed_module_ctx = { + NULL, + ngx_http_pagespeed_init, // Post configuration. + NULL, + NULL, + NULL, + NULL, + ngx_http_pagespeed_create_loc_conf, + ngx_http_pagespeed_merge_loc_conf +}; + +ngx_module_t ngx_pagespeed = { + NGX_MODULE_V1, + &ngx_http_pagespeed_module_ctx, + ngx_http_pagespeed_commands, + NGX_HTTP_MODULE, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NGX_MODULE_V1_PADDING +}; +