Download | Plain Text | No Line Numbers
- /*
- * Copyright 2014-2020 Manuel Mausz
- *
- * 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 "httpd.h"
- #include "http_log.h"
- #include "http_protocol.h"
- #include "apr_strings.h"
-
- #define VERSION "0.9"
- #define PHP_MAGIC_TYPE "application/x-httpd-php"
-
- module AP_MODULE_DECLARE_DATA phpfpm_handler_module;
-
- typedef struct {
- const char *url;
- const char *body_temp_path;
- apr_table_t *php_values;
- } phpfpm_dir_conf;
-
- static apr_status_t write_body(request_rec *r, apr_file_t *file, apr_off_t *size)
- {
- apr_status_t rc = OK;
-
- if ((rc = ap_setup_client_block(r, REQUEST_CHUNKED_DECHUNK)))
- return rc;
-
- if (ap_should_client_block(r)) {
- char argsbuffer[HUGE_STRING_LEN];
- apr_off_t rsize, len_read, rpos = 0;
- apr_off_t length = r->remaining;
- apr_size_t written;
-
- while ((len_read = ap_get_client_block(r, argsbuffer,
- sizeof(argsbuffer))) > 0) {
- if (length > 0 && (rpos + len_read) > length)
- rsize = (apr_size_t)length - rpos;
- else
- rsize = len_read;
-
- rc = apr_file_write_full(file, argsbuffer, (apr_size_t) rsize,
- &written);
- if (written != rsize || rc != OK)
- return APR_ENOSPC;
- if (rc != APR_SUCCESS)
- return rc;
- rpos += rsize;
- }
-
- *size = rpos;
- }
-
- return rc;
- }
-
- static const char *pass_request_body(request_rec *r, phpfpm_dir_conf *conf,
- apr_off_t *size)
- {
- #if APR_HAS_THREADS
- apr_os_thread_t tid = apr_os_thread_current();
- char *filename = apr_psprintf(r->pool, "%s/php.%pT.XXXXXX",
- conf->body_temp_path, &tid);
- #else
- char *filename = apr_psprintf(r->pool, "%s/php.%" APR_PID_T_FMT ".XXXXXX",
- conf->body_temp_path, getpid());
- #endif
-
- apr_file_t *file;
- apr_status_t rc = apr_file_mktemp(&file, filename,
- APR_FOPEN_CREATE | APR_FOPEN_WRITE | APR_FOPEN_TRUNCATE |
- APR_FOPEN_DELONCLOSE, r->pool);
- if (rc == APR_SUCCESS)
- rc = apr_file_perms_set(filename, APR_FPROT_UREAD | APR_FPROT_UWRITE
- | APR_FPROT_GREAD | APR_FPROT_WREAD);
- if (rc == APR_SUCCESS)
- rc = write_body(r, file, size);
- return (rc == APR_SUCCESS) ? filename : NULL;
- }
-
- #define MAX_SAVED_LENGTHS 3
- static char *serialize_table(request_rec *r, const apr_array_header_t *arr)
- {
- struct {
- apr_size_t key_len;
- apr_size_t val_len;
- } saved_lengths[MAX_SAVED_LENGTHS];
- apr_size_t len = 0;
- int i;
-
- if (!arr)
- return NULL;
-
- const apr_table_entry_t *elts = (const apr_table_entry_t *)arr->elts;
- if (arr->nelts == 0)
- return NULL;
- for (i = 0; i < arr->nelts; ++i) {
- len += key_len + val_len + 2;
- if (i < MAX_SAVED_LENGTHS) {
- saved_lengths[i].key_len = key_len;
- saved_lengths[i].val_len = val_len;
- }
- }
-
- char *res = apr_palloc(r->pool, len + 1);
- char *cp = res;
- for (i = 0; i < arr->nelts; ++i) {
- apr_size_t key_len, val_len;
- if (i < MAX_SAVED_LENGTHS) {
- key_len = saved_lengths[i].key_len;
- val_len = saved_lengths[i].val_len;
- }
- else
- {
- }
-
- cp += key_len;
- *cp = '=';
- cp++;
- cp += val_len;
- *cp = '\n';
- cp++;
- }
- *cp = '\0';
-
- return res;
- }
-
- static int handler(request_rec *r)
- {
- if (!r->filename || r->proxyreq
- return DECLINED;
-
- if (r->finfo.filetype == 0) {
- ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
- "script '%s' not found or unable to stat", r->filename);
- return HTTP_NOT_FOUND;
- }
- if (r->finfo.filetype == APR_DIR) {
- ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
- "attempt to invoke directory '%s' as script", r->filename);
- return HTTP_FORBIDDEN;
- }
-
- phpfpm_dir_conf *conf =
- ap_get_module_config(r->per_dir_config, &phpfpm_handler_module);
- if (!conf->url)
- return DECLINED;
-
- /* handle body temp path */
- if (conf->body_temp_path) {
- const char *filename = NULL;
- apr_off_t size;
-
- if (r->method_number == M_POST) {
- const char *ct = apr_table_get(r->headers_in, "Content-Type");
- if (ct && !strncasecmp(ct, "multipart/form-data", 19))
- filename = pass_request_body(r, conf, &size);
- }
- else if (r->method_number == M_PUT) {
- /* HTTP PUT (e.g. webdav) */
- filename = pass_request_body(r, conf, &size);
- }
-
- if (filename) {
- apr_table_setn(r->subprocess_env, "REQUEST_BODY_FILE", filename);
- apr_table_set(r->headers_in, "Content-Length",
- apr_off_t_toa(r->pool, size));
- /*
- * mod_proxy_fcgi tries to read the input in order to determine the content-length:
- * see https://svn.apache.org/viewvc?view=revision&revision=1884069
- * we prevent that by creating an empty input brigade beforehand
- */
- apr_bucket_brigade *input_brigade = apr_brigade_create(r->pool, r->connection->bucket_alloc);
- apr_pool_userdata_setn(input_brigade, "proxy-fcgi-input", NULL, r->pool);
- }
- }
-
- #if AP_SERVER_MAJORVERSION_NUMBER >= 2 && AP_SERVER_MINORVERSION_NUMBER >= 4 \
- && AP_SERVER_PATCHLEVEL_NUMBER >= 61
- r->handler = conf->url;
- #else
- r->proxyreq = PROXYREQ_REVERSE;
- r->filename = apr_pstrcat(r->pool, conf->url, r->filename, NULL);
- r->handler = "proxy-server";
- #endif
-
- const char *php_values =
- serialize_table(r, apr_table_elts(conf->php_values));
- if (php_values)
- apr_table_setn(r->subprocess_env, "PHP_VALUE", php_values);
-
- return DECLINED;
- }
-
- static void *create_dir_config(apr_pool_t *p, char *dummy)
- {
- phpfpm_dir_conf *conf = apr_palloc(p, sizeof(*conf));
- conf->url = NULL;
- conf->body_temp_path = NULL;
- conf->php_values = apr_table_make(p, 4);
- return conf;
- }
-
- static void *merge_dir_config(apr_pool_t *p, void *base_, void *add_)
- {
- phpfpm_dir_conf *base = (phpfpm_dir_conf *)base_;
- phpfpm_dir_conf *add = (phpfpm_dir_conf *)add_;
- phpfpm_dir_conf *conf = apr_pcalloc(p, sizeof(*conf));
-
- conf->url = (add->url == NULL) ? base->url : add->url;
- conf->body_temp_path = (add->body_temp_path == NULL) ? base->body_temp_path
- : add->body_temp_path;
- conf->php_values = apr_table_overlay(p, base->php_values, add->php_values);
- apr_table_compress(conf->php_values, APR_OVERLAP_TABLES_SET);
- return conf;
- }
-
- static const char *add_phpfpm_url(cmd_parms *cmd, void *conf_,
- const char *arg)
- {
- phpfpm_dir_conf *conf = (phpfpm_dir_conf *)conf_;
- conf->url = arg;
- return NULL;
- }
-
- static const char *add_php_value(cmd_parms *cmd, void *conf_,
- const char *name, const char *value)
- {
- phpfpm_dir_conf *conf = (phpfpm_dir_conf *)conf_;
- apr_table_setn(conf->php_values, name, value);
- return NULL;
- }
-
- static const char *add_phpfpm_body_temp_path(cmd_parms *cmd, void *conf_,
- const char *arg)
- {
- phpfpm_dir_conf *conf = (phpfpm_dir_conf *)conf_;
- conf->body_temp_path = arg;
- return NULL;
- }
-
- static const char *add_php_flag(cmd_parms *cmd, void *conf,
- const char *name, const char *value)
- {
- char *bool_val = apr_palloc(cmd->pool, sizeof(char) * 2);
-
- if (!strcasecmp(value, "On") || (value[0] == '1' && value[1] == '\0'))
- bool_val[0] = '1';
- else
- bool_val[0] = '0';
- bool_val[1] = 0;
-
- return add_php_value(cmd, conf, name, bool_val);
- }
-
- static const command_rec config_cmds[] =
- {
- AP_INIT_TAKE1("phpfpm_url", add_phpfpm_url,
- NULL, RSRC_CONF|ACCESS_CONF,
- "URL to PHP-FPM."),
- AP_INIT_TAKE1("phpfpm_body_temp_path", add_phpfpm_body_temp_path,
- NULL, RSRC_CONF|ACCESS_CONF,
- "Directory for storing temporary files holding client request"
- " bodies."),
- AP_INIT_TAKE2("php_value", add_php_value,
- NULL, OR_OPTIONS|OR_FILEINFO,
- "PHP Value Modifier"),
- AP_INIT_TAKE2("php_flag", add_php_flag,
- NULL, OR_OPTIONS|OR_FILEINFO,
- "PHP Flag Modifier"),
- {NULL}
- };
-
- static void register_hooks(apr_pool_t *p)
- {
- static const char * const aszSucc[] = { "mod_proxy.c", NULL };
- ap_hook_handler(handler, NULL, aszSucc, APR_HOOK_REALLY_FIRST);
- }
-
- AP_DECLARE_MODULE(phpfpm_handler) =
- {
- STANDARD20_MODULE_STUFF,
- create_dir_config, /* create per-dir config structure */
- merge_dir_config, /* merge per-dir config structures */
- NULL, /* create per-server config structure */
- NULL, /* merge per-server config structures */
- config_cmds, /* command-table */
- register_hooks /* register hooks */
- };
-