summaryrefslogblamecommitdiffhomepage
path: root/src/nxt_php_sapi.c
blob: df7cf243042865d69342be473374409e0c4f211f (plain) (tree)
1
2
3
4
5
6
7
8
9
10
11
12
  
                            









                                     
                       


                             
 













                                                          
 

















                                                                             
                                                            

                                                                            

                                                                        

                                                               

                                                                             




                                                                               
                                                                     
                                                                         
                                                

                                                                    
                                                         
      













                                                                                
                    







                                                       
                                            













                                                                













                                                            
                                                         











                                                      
 


                                                           

  
 




                                       
 



                                

                                                         
 
                             
                          

  
 
                                               

                   
                      
                
         
                 
  

 
                                 


                              

 


                                                           
                           
                                        
                                                      



                                             
                               
                           
 
                                                    

                                                      
 

                        
                     
 
                          
                                             

                         
 



                             
 

                                             
                                                                           



                                           
 
                                      
 

                                               
 
                                                           
                                                   


                                                 
 

                                                               
 

                                                             
 

                                                 
                                                                              

                             
 


                                                                
                                                            





                              
 
                                              




                                                   

                                                                         
 


                                                                  
 


                                                               

     
                              






                                                                  
     
 




                                


                                              
                          

                                                          
      
 

      
                                       


















                                                                        
                                          
 







                                                                         









































                                                                 

                  
 
 


                                                                          


                                  











                                                                           


                                                                               














                                                             



             
 
 






                                                                 
 

                                                                           
 


                            
 












































                                                                       
     






                                                                       

                                                                           










                                            

 

      
 
           



















































                                                                        
                                                
 

                                                                 

     









                                                   
     





























                                                     
 
                                     
                                                 
                                        
 

                                   
                                     

         
                                                              
                                                  
                                                                   
                                       


                                                       
         
 
                                                                   
                                                   

                                     
                                                                     

         

                  


                                   
 


                                                                    
 
                                      
 


                                                                          
 

                                                       
 
                                                                     
     
 

                                                 
 
                                                   






                                              
                                                      


                                   
                                                                         
 
                                     


                                                                   
 
            

                                                                            
     
 
              
                                                          


                                                                  


                                                                
                  

     

                     


                                               

     
                                   
 

                                                  
     





                                                
                                                    










                                                                      
                          
                            
 
                             
 

                                                            


                          




                                    


                                                                 







                                                          

                             
                   
 
                                                    
 
                                           




                                                    


                                             

                                                            
 





                                                                    
 

                                                        
 
                                                              
 

                                                     

            





                                                                      

     



                                                                    





                                                                      
 



                                
 


                                                                         

     


                                                           
 

                                       












                                                             
                            
 
                             
 
                                                                            
 
                                                                





                              
                            
 
                             
 
                                                         
 
                       





                                                            




                                        
 
                             
 

                     
 
                                                          
 


                                                                              
 

                                                                            
 





                                                                             
 



                                                           
 
















                                                                              



                                                            
 
            



                                                                      
     
 



                                                         
 



                                                                         

                                                                     
 



                                                                      
 



                                                                                



                                                                            


                                           
 

                                                               

     

                                                         
 


                                                                           
 

                                                       
 

                                                                         
     
 
 
 




                                                                       
 
                               
 
                                                                             
 


                                                           
 
 






                                                               
 
 

                                                                
                                                                     


                                      
     
 

                                                                              
                                                                 
                                                           


 
                                                



                                                       
                                            
      
 
                                                                      
 
/*
 * Copyright (C) Max Romanov
 * Copyright (C) Valentin V. Bartenev
 * Copyright (C) NGINX, Inc.
 */

#include "php.h"
#include "SAPI.h"
#include "php_main.h"
#include "php_variables.h"

#include <nxt_main.h>
#include <nxt_router.h>
#include <nxt_unit.h>
#include <nxt_unit_request.h>


#if PHP_MAJOR_VERSION >= 7
#   define NXT_PHP7 1
#   if PHP_MINOR_VERSION >= 1
#       define NXT_HAVE_PHP_LOG_MESSAGE_WITH_SYSLOG_TYPE 1
#   else
#       define NXT_HAVE_PHP_INTERRUPTS 1
#   endif
#   define NXT_HAVE_PHP_IGNORE_CWD 1
#else
#   define NXT_HAVE_PHP_INTERRUPTS 1
#   if PHP_MINOR_VERSION >= 4
#       define NXT_HAVE_PHP_IGNORE_CWD 1
#   endif
#endif


typedef struct nxt_php_run_ctx_s  nxt_php_run_ctx_t;

#ifdef NXT_PHP7
typedef int (*nxt_php_disable_t)(char *p, size_t size);
#else
typedef int (*nxt_php_disable_t)(char *p, uint TSRMLS_DC);
#endif


static nxt_int_t nxt_php_init(nxt_task_t *task, nxt_common_app_conf_t *conf);

static void nxt_php_str_trim_trail(nxt_str_t *str, u_char t);
static void nxt_php_str_trim_lead(nxt_str_t *str, u_char t);
nxt_inline u_char *nxt_realpath(const void *c);

static void nxt_php_request_handler(nxt_unit_request_info_t *req);

static int nxt_php_startup(sapi_module_struct *sapi_module);
static void nxt_php_set_options(nxt_task_t *task, nxt_conf_value_t *options,
    int type);
static nxt_int_t nxt_php_alter_option(nxt_str_t *name, nxt_str_t *value,
    int type);
static void nxt_php_disable(nxt_task_t *task, const char *type,
    nxt_str_t *value, char **ptr, nxt_php_disable_t disable);
static int nxt_php_send_headers(sapi_headers_struct *sapi_headers TSRMLS_DC);
static char *nxt_php_read_cookies(TSRMLS_D);
static void nxt_php_set_sptr(nxt_unit_request_info_t *req, const char *name,
    nxt_unit_sptr_t *v, uint32_t len, zval *track_vars_array TSRMLS_DC);
nxt_inline void nxt_php_set_str(nxt_unit_request_info_t *req, const char *name,
    nxt_str_t *s, zval *track_vars_array TSRMLS_DC);
static void nxt_php_set_cstr(nxt_unit_request_info_t *req, const char *name,
    const char *str, uint32_t len, zval *track_vars_array TSRMLS_DC);
static void nxt_php_register_variables(zval *track_vars_array TSRMLS_DC);
#ifdef NXT_HAVE_PHP_LOG_MESSAGE_WITH_SYSLOG_TYPE
static void nxt_php_log_message(char *message, int syslog_type_int);
#else
static void nxt_php_log_message(char *message TSRMLS_DC);
#endif

#ifdef NXT_PHP7
static size_t nxt_php_unbuffered_write(const char *str,
    size_t str_length TSRMLS_DC);
static size_t nxt_php_read_post(char *buffer, size_t count_bytes TSRMLS_DC);
#else
static int nxt_php_unbuffered_write(const char *str, uint str_length TSRMLS_DC);
static int nxt_php_read_post(char *buffer, uint count_bytes TSRMLS_DC);
#endif


static sapi_module_struct  nxt_php_sapi_module =
{
    (char *) "cli-server",
    (char *) "unit",

    nxt_php_startup,             /* startup */
    php_module_shutdown_wrapper, /* shutdown */

    NULL,                        /* activate */
    NULL,                        /* deactivate */

    nxt_php_unbuffered_write,    /* unbuffered write */
    NULL,                        /* flush */
    NULL,                        /* get uid */
    NULL,                        /* getenv */

    php_error,                   /* error handler */

    NULL,                        /* header handler */
    nxt_php_send_headers,        /* send headers handler */
    NULL,                        /* send header handler */

    nxt_php_read_post,           /* read POST data */
    nxt_php_read_cookies,        /* read Cookies */

    nxt_php_register_variables,  /* register server variables */
    nxt_php_log_message,         /* log message */
    NULL,                        /* get request time */
    NULL,                        /* terminate process */

    NULL,                        /* php_ini_path_override */
#ifdef NXT_HAVE_PHP_INTERRUPTS
    NULL,                        /* block_interruptions */
    NULL,                        /* unblock_interruptions */
#endif
    NULL,                        /* default_post_reader */
    NULL,                        /* treat_data */
    NULL,                        /* executable_location */

    0,                           /* php_ini_ignore */
#ifdef NXT_HAVE_PHP_IGNORE_CWD
    1,                           /* php_ini_ignore_cwd */
#endif
    NULL,                        /* get_fd */

    NULL,                        /* force_http_10 */

    NULL,                        /* get_target_uid */
    NULL,                        /* get_target_gid */

    NULL,                        /* input_filter */

    NULL,                        /* ini_defaults */
    0,                           /* phpinfo_as_text */

    NULL,                        /* ini_entries */
    NULL,                        /* additional_functions */
    NULL                         /* input_filter_init */
};


struct nxt_php_run_ctx_s {
    char                       *cookie;
    nxt_str_t                  script;
    nxt_unit_request_info_t    *req;
};


static nxt_str_t nxt_php_path;
static nxt_str_t nxt_php_root;
static nxt_str_t nxt_php_script;
static nxt_str_t nxt_php_index = nxt_string("index.php");


static uint32_t  compat[] = {
    NXT_VERNUM, NXT_DEBUG,
};


NXT_EXPORT nxt_app_module_t  nxt_app_module = {
    sizeof(compat),
    compat,
    nxt_string("php"),
    PHP_VERSION,
    NULL,
    nxt_php_init,
};


static nxt_task_t  *nxt_php_task;
#ifdef ZTS
static void        ***tsrm_ls;
#endif


static nxt_int_t
nxt_php_init(nxt_task_t *task, nxt_common_app_conf_t *conf)
{
    u_char              *p;
    nxt_str_t           rpath, ini_path;
    nxt_str_t           *root, *path, *script, *index;
    nxt_port_t          *my_port, *main_port;
    nxt_runtime_t       *rt;
    nxt_unit_ctx_t      *unit_ctx;
    nxt_unit_init_t     php_init;
    nxt_conf_value_t    *value;
    nxt_php_app_conf_t  *c;

    static nxt_str_t  file_str = nxt_string("file");
    static nxt_str_t  user_str = nxt_string("user");
    static nxt_str_t  admin_str = nxt_string("admin");

    nxt_php_task = task;

    c = &conf->u.php;

    if (c->root == NULL) {
        nxt_alert(task, "php root is empty");
        return NXT_ERROR;
    }

    root = &nxt_php_root;
    path = &nxt_php_path;
    script = &nxt_php_script;
    index = &nxt_php_index;

    root->start = nxt_realpath(c->root);
    if (nxt_slow_path(root->start == NULL)) {
        nxt_alert(task, "root realpath(%s) failed %E", c->root, nxt_errno);
        return NXT_ERROR;
    }

    root->length = nxt_strlen(root->start);

    nxt_php_str_trim_trail(root, '/');

    if (c->script.length > 0) {
        nxt_php_str_trim_lead(&c->script, '/');

        path->length = root->length + 1 + c->script.length;
        path->start = nxt_malloc(path->length + 1);
        if (nxt_slow_path(path->start == NULL)) {
            return NXT_ERROR;
        }

        p = nxt_cpymem(path->start, root->start, root->length);
        *p++ = '/';

        p = nxt_cpymem(p, c->script.start, c->script.length);
        *p = '\0';

        rpath.start = nxt_realpath(path->start);
        if (nxt_slow_path(rpath.start == NULL)) {
            nxt_alert(task, "script realpath(%V) failed %E", path, nxt_errno);
            return NXT_ERROR;
        }

        rpath.length = nxt_strlen(rpath.start);

        if (!nxt_str_start(&rpath, root->start, root->length)) {
            nxt_alert(task, "script is not under php root");
            return NXT_ERROR;
        }

        nxt_free(path->start);

        *path = rpath;

        script->length = c->script.length + 1;
        script->start = nxt_malloc(script->length);
        if (nxt_slow_path(script->start == NULL)) {
            return NXT_ERROR;
        }

        script->start[0] = '/';
        nxt_memcpy(script->start + 1, c->script.start, c->script.length);

        nxt_log_error(NXT_LOG_INFO, task->log,
                      "(ABS_MODE) php script \"%V\" root: \"%V\"",
                      script, root);

    } else {
        nxt_log_error(NXT_LOG_INFO, task->log,
                      "(non ABS_MODE) php root: \"%V\"", root);
    }

    if (c->index.length > 0) {
        index->length = c->index.length;
        index->start = nxt_malloc(index->length);
        if (nxt_slow_path(index->start == NULL)) {
            return NXT_ERROR;
        }

        nxt_memcpy(index->start, c->index.start, c->index.length);
    }

#ifdef ZTS
    tsrm_startup(1, 1, 0, NULL);
    tsrm_ls = ts_resource(0);
#endif

#if defined(NXT_PHP7) && defined(ZEND_SIGNALS)

#if (NXT_ZEND_SIGNAL_STARTUP)
    zend_signal_startup();
#elif defined(ZTS)
#error PHP is built with thread safety and broken signals.
#endif

#endif

    sapi_startup(&nxt_php_sapi_module);

    if (c->options != NULL) {
        value = nxt_conf_get_object_member(c->options, &file_str, NULL);

        if (value != NULL) {
            nxt_conf_get_string(value, &ini_path);

            p = nxt_malloc(ini_path.length + 1);
            if (nxt_slow_path(p == NULL)) {
                return NXT_ERROR;
            }

            nxt_php_sapi_module.php_ini_path_override = (char *) p;

            p = nxt_cpymem(p, ini_path.start, ini_path.length);
            *p = '\0';
        }
    }

    nxt_php_startup(&nxt_php_sapi_module);

    if (c->options != NULL) {
        value = nxt_conf_get_object_member(c->options, &admin_str, NULL);
        nxt_php_set_options(task, value, ZEND_INI_SYSTEM);

        value = nxt_conf_get_object_member(c->options, &user_str, NULL);
        nxt_php_set_options(task, value, ZEND_INI_USER);
    }

    nxt_memzero(&php_init, sizeof(nxt_unit_init_t));

    rt = task->thread->runtime;

    main_port = rt->port_by_type[NXT_PROCESS_MAIN];
    if (nxt_slow_path(main_port == NULL)) {
        return NXT_ERROR;
    }

    my_port = nxt_runtime_port_find(rt, nxt_pid, 0);
    if (nxt_slow_path(my_port == NULL)) {
        return NXT_ERROR;
    }

    php_init.callbacks.request_handler = nxt_php_request_handler;
    php_init.ready_port.id.pid = main_port->pid;
    php_init.ready_port.id.id = main_port->id;
    php_init.ready_port.out_fd = main_port->pair[1];

    nxt_fd_blocking(task, main_port->pair[1]);

    php_init.ready_stream = my_port->process->init->stream;

    php_init.read_port.id.pid = my_port->pid;
    php_init.read_port.id.id = my_port->id;
    php_init.read_port.in_fd = my_port->pair[0];

    nxt_fd_blocking(task, my_port->pair[0]);

    php_init.log_fd = 2;

    unit_ctx = nxt_unit_init(&php_init);
    if (nxt_slow_path(unit_ctx == NULL)) {
        return NXT_ERROR;
    }

    nxt_unit_run(unit_ctx);

    nxt_unit_done(unit_ctx);

    exit(0);

    return NXT_OK;
}


static void
nxt_php_set_options(nxt_task_t *task, nxt_conf_value_t *options, int type)
{
    uint32_t          next;
    nxt_str_t         name, value;
    nxt_conf_value_t  *value_obj;

    if (options != NULL) {
        next = 0;

        for ( ;; ) {
            value_obj = nxt_conf_next_object_member(options, &name, &next);
            if (value_obj == NULL) {
                break;
            }

            nxt_conf_get_string(value_obj, &value);

            if (nxt_php_alter_option(&name, &value, type) != NXT_OK) {
                nxt_log(task, NXT_LOG_ERR,
                        "setting PHP option \"%V: %V\" failed", &name, &value);
                continue;
            }

            if (nxt_str_eq(&name, "disable_functions", 17)) {
                nxt_php_disable(task, "function", &value,
                                &PG(disable_functions),
                                zend_disable_function);
                continue;
            }

            if (nxt_str_eq(&name, "disable_classes", 15)) {
                nxt_php_disable(task, "class", &value,
                                &PG(disable_classes),
                                zend_disable_class);
                continue;
            }
        }
    }
}


#if (NXT_PHP7)

static nxt_int_t
nxt_php_alter_option(nxt_str_t *name, nxt_str_t *value, int type)
{
    zend_string     *zs;
    zend_ini_entry  *ini_entry;

    ini_entry = zend_hash_str_find_ptr(EG(ini_directives),
                                       (char *) name->start, name->length);

    if (ini_entry == NULL) {
        return NXT_ERROR;
    }

    /* PHP exits on memory allocation errors. */
    zs = zend_string_init((char *) value->start, value->length, 1);

    if (ini_entry->on_modify
        && ini_entry->on_modify(ini_entry, zs, ini_entry->mh_arg1,
                                ini_entry->mh_arg2, ini_entry->mh_arg3,
                                ZEND_INI_STAGE_ACTIVATE)
           != SUCCESS)
    {
        zend_string_release(zs);
        return NXT_ERROR;
    }

    ini_entry->value = zs;
    ini_entry->modifiable = type;

    return NXT_OK;
}

#else  /* PHP 5. */

static nxt_int_t
nxt_php_alter_option(nxt_str_t *name, nxt_str_t *value, int type)
{
    char            *cstr;
    zend_ini_entry  *ini_entry;
    char            buf[256];

    if (nxt_slow_path(name->length >= sizeof(buf))) {
        return NXT_ERROR;
    }

    nxt_memcpy(buf, name->start, name->length);
    buf[name->length] = '\0';

    if (zend_hash_find(EG(ini_directives), buf, name->length + 1,
                       (void **) &ini_entry)
        == FAILURE)
    {
        return NXT_ERROR;
    }

    cstr = nxt_malloc(value->length + 1);
    if (nxt_slow_path(cstr == NULL)) {
        return NXT_ERROR;
    }

    nxt_memcpy(cstr, value->start, value->length);
    cstr[value->length] = '\0';

    if (ini_entry->on_modify
        && ini_entry->on_modify(ini_entry, cstr, value->length,
                                ini_entry->mh_arg1, ini_entry->mh_arg2,
                                ini_entry->mh_arg3, ZEND_INI_STAGE_ACTIVATE
                                TSRMLS_CC)
           != SUCCESS)
    {
        nxt_free(cstr);
        return NXT_ERROR;
    }

    ini_entry->value = cstr;
    ini_entry->value_length = value->length;
    ini_entry->modifiable = type;

    return NXT_OK;
}

#endif


static void
nxt_php_disable(nxt_task_t *task, const char *type, nxt_str_t *value,
    char **ptr, nxt_php_disable_t disable)
{
    char  c, *p, *start;

    p = nxt_malloc(value->length + 1);
    if (nxt_slow_path(p == NULL)) {
        return;
    }

    /*
     * PHP frees this memory on module shutdown.
     * See core_globals_dtor() for details.
     */
    *ptr = p;

    nxt_memcpy(p, value->start, value->length);
    p[value->length] = '\0';

    start = p;

    do {
        c = *p;

        if (c == ' ' || c == ',' || c == '\0') {

            if (p != start) {
                *p = '\0';

#ifdef NXT_PHP7
                if (disable(start, p - start)
#else
                if (disable(start, p - start TSRMLS_CC)
#endif
                    != SUCCESS)
                {
                    nxt_log(task, NXT_LOG_ERR,
                            "PHP: failed to disable \"%s\": no such %s",
                            start, type);
                }
            }

            start = p + 1;
        }

        p++;

    } while (c != '\0');
}


static void
nxt_php_str_trim_trail(nxt_str_t *str, u_char t)
{
    while (str->length > 0 && str->start[str->length - 1] == t) {
        str->length--;
    }

    str->start[str->length] = '\0';
}


static void
nxt_php_str_trim_lead(nxt_str_t *str, u_char t)
{
    while (str->length > 0 && str->start[0] == t) {
        str->length--;
        str->start++;
    }
}


nxt_inline u_char *
nxt_realpath(const void *c)
{
    return (u_char *) realpath(c, NULL);
}


static void
nxt_php_request_handler(nxt_unit_request_info_t *req)
{
    int                 rc;
    u_char              *p;
    nxt_str_t           path, script_name;
    nxt_unit_field_t    *f;
    zend_file_handle    file_handle;
    nxt_php_run_ctx_t   run_ctx, *ctx;
    nxt_unit_request_t  *r;

    nxt_memzero(&run_ctx, sizeof(run_ctx));

    ctx = &run_ctx;
    ctx->req = req;

    r = req->request;

    path.length = r->path_length;
    path.start = nxt_unit_sptr_get(&r->path);

    if (nxt_php_path.start == NULL) {
        if (path.start[path.length - 1] == '/') {
            script_name = nxt_php_index;

        } else {
            script_name.length = 0;
            script_name.start = NULL;
        }

        ctx->script.length = nxt_php_root.length + path.length
                             + script_name.length;
        p = ctx->script.start = nxt_malloc(ctx->script.length + 1);
        if (nxt_slow_path(p == NULL)) {
            nxt_unit_request_done(req, NXT_UNIT_ERROR);

            return;
        }

        p = nxt_cpymem(p, nxt_php_root.start, nxt_php_root.length);
        p = nxt_cpymem(p, path.start, path.length);

        if (script_name.length > 0) {
            p = nxt_cpymem(p, script_name.start, script_name.length);
        }

        *p = '\0';

    } else {
        ctx->script = nxt_php_path;
    }

    SG(server_context) = ctx;
    SG(request_info).request_uri = nxt_unit_sptr_get(&r->target);
    SG(request_info).request_method = nxt_unit_sptr_get(&r->method);

    SG(request_info).proto_num = 1001;

    SG(request_info).query_string = r->query.offset
                                    ? nxt_unit_sptr_get(&r->query) : NULL;
    SG(request_info).content_length = r->content_length;

    if (r->content_type_field != NXT_UNIT_NONE_FIELD) {
        f = r->fields + r->content_type_field;

        SG(request_info).content_type = nxt_unit_sptr_get(&f->value);
    }

    if (r->cookie_field != NXT_UNIT_NONE_FIELD) {
        f = r->fields + r->cookie_field;

        ctx->cookie = nxt_unit_sptr_get(&f->value);
    }

    SG(sapi_headers).http_response_code = 200;

    SG(request_info).path_translated = NULL;

    file_handle.type = ZEND_HANDLE_FILENAME;
    file_handle.filename = (char *) ctx->script.start;
    file_handle.free_filename = 0;
    file_handle.opened_path = NULL;

    nxt_unit_req_debug(req, "handle.filename = '%s'", ctx->script.start);

    if (nxt_php_path.start != NULL) {
        nxt_unit_req_debug(req, "run script %.*s in absolute mode",
                           (int) nxt_php_path.length,
                           (char *) nxt_php_path.start);

    } else {
        nxt_unit_req_debug(req, "run script %.*s", (int) ctx->script.length,
                           (char *) ctx->script.start);
    }

#if (NXT_PHP7)
    if (nxt_slow_path(php_request_startup() == FAILURE)) {
#else
    if (nxt_slow_path(php_request_startup(TSRMLS_C) == FAILURE)) {
#endif
        nxt_unit_req_debug(req, "php_request_startup() failed");
        rc = NXT_UNIT_ERROR;

        goto fail;
    }

    rc = NXT_UNIT_OK;

    php_execute_script(&file_handle TSRMLS_CC);
    php_request_shutdown(NULL);

fail:

    nxt_unit_request_done(req, rc);

    if (ctx->script.start != nxt_php_path.start) {
        nxt_free(ctx->script.start);
    }
}


static int
nxt_php_startup(sapi_module_struct *sapi_module)
{
    return php_module_startup(sapi_module, NULL, 0);
}


#ifdef NXT_PHP7
static size_t
nxt_php_unbuffered_write(const char *str, size_t str_length TSRMLS_DC)
#else
static int
nxt_php_unbuffered_write(const char *str, uint str_length TSRMLS_DC)
#endif
{
    int                rc;
    nxt_php_run_ctx_t  *ctx;

    ctx = SG(server_context);

    rc = nxt_unit_response_write(ctx->req, str, str_length);
    if (nxt_fast_path(rc == NXT_UNIT_OK)) {
        return str_length;
    }

    php_handle_aborted_connection();
    return 0;
}


static int
nxt_php_send_headers(sapi_headers_struct *sapi_headers TSRMLS_DC)
{
    int                      rc, fields_count;
    char                     *colon, *status_line, *value;
    uint16_t                 status;
    uint32_t                 resp_size;
    nxt_php_run_ctx_t        *ctx;
    sapi_header_struct       *h;
    zend_llist_position      zpos;
    nxt_unit_request_info_t  *req;

    ctx = SG(server_context);
    req = ctx->req;

    nxt_unit_req_debug(req, "nxt_php_send_headers");

    if (SG(request_info).no_headers == 1) {
        rc = nxt_unit_response_init(req, 200, 0, 0);
        if (nxt_slow_path(rc != NXT_UNIT_OK)) {
            return SAPI_HEADER_SEND_FAILED;
        }

        return SAPI_HEADER_SENT_SUCCESSFULLY;
    }

    resp_size = 0;
    fields_count = zend_llist_count(&sapi_headers->headers);

    for (h = zend_llist_get_first_ex(&sapi_headers->headers, &zpos);
         h;
         h = zend_llist_get_next_ex(&sapi_headers->headers, &zpos))
    {
        resp_size += h->header_len;
    }

    if (SG(sapi_headers).http_status_line) {
        status_line = SG(sapi_headers).http_status_line;

        status = nxt_int_parse((u_char *) status_line + 9, 3);

    } else if (SG(sapi_headers).http_response_code) {
        status = SG(sapi_headers).http_response_code;

    } else {
        status = 200;
    }

    rc = nxt_unit_response_init(req, status, fields_count, resp_size);
    if (nxt_slow_path(rc != NXT_UNIT_OK)) {
        return SAPI_HEADER_SEND_FAILED;
    }

    for (h = zend_llist_get_first_ex(&sapi_headers->headers, &zpos);
         h;
         h = zend_llist_get_next_ex(&sapi_headers->headers, &zpos))
    {
        colon = memchr(h->header, ':', h->header_len);
        if (nxt_slow_path(colon == NULL)) {
            nxt_unit_req_warn(req, "colon not found in header '%.*s'",
                              (int) h->header_len, h->header);
            continue;
        }

        value = colon + 1;
        while(isspace(*value)) {
            value++;
        }

        nxt_unit_response_add_field(req, h->header, colon - h->header,
                                    value,
                                    h->header_len - (value - h->header));
    }

    rc = nxt_unit_response_send(req);
    if (nxt_slow_path(rc != NXT_UNIT_OK)) {
        nxt_unit_req_debug(req, "failed to send response");

        return SAPI_HEADER_SEND_FAILED;
    }

    return SAPI_HEADER_SENT_SUCCESSFULLY;
}


#ifdef NXT_PHP7
static size_t
nxt_php_read_post(char *buffer, size_t count_bytes TSRMLS_DC)
#else
static int
nxt_php_read_post(char *buffer, uint count_bytes TSRMLS_DC)
#endif
{
    nxt_php_run_ctx_t  *ctx;

    ctx = SG(server_context);

    nxt_unit_req_debug(ctx->req, "nxt_php_read_post %d", (int) count_bytes);

    return nxt_unit_request_read(ctx->req, buffer, count_bytes);
}


static char *
nxt_php_read_cookies(TSRMLS_D)
{
    nxt_php_run_ctx_t  *ctx;

    ctx = SG(server_context);

    nxt_unit_req_debug(ctx->req, "nxt_php_read_cookies");

    return ctx->cookie;
}


static void
nxt_php_register_variables(zval *track_vars_array TSRMLS_DC)
{
    const char               *name;
    nxt_unit_field_t         *f, *f_end;
    nxt_php_run_ctx_t        *ctx;
    nxt_unit_request_t       *r;
    nxt_unit_request_info_t  *req;

    ctx = SG(server_context);

    req = ctx->req;
    r = req->request;

    nxt_unit_req_debug(req, "nxt_php_register_variables");

    php_register_variable_safe((char *) "SERVER_SOFTWARE",
                               (char *) nxt_server.start,
                               nxt_server.length, track_vars_array TSRMLS_CC);

    nxt_php_set_sptr(req, "SERVER_PROTOCOL", &r->version, r->version_length,
                     track_vars_array TSRMLS_CC);

/*
 * 'SCRIPT_NAME'
 * Contains the current script's path. This is useful for pages which need to
 * point to themselves. The __FILE__ constant contains the full path and
 * filename of the current (i.e. included) file.
 */

/*
 * 'SCRIPT_FILENAME'
 * The absolute pathname of the currently executing script.
 */

/*
 * 'DOCUMENT_ROOT'
 * The document root directory under which the current script is executing,
 * as defined in the server's configuration file.
 */

    if (nxt_php_script.start != NULL) {
    // ABS_MODE
/*
 * 'PHP_SELF'
 * The filename of the currently executing script, relative to the document
 * root. For instance, $_SERVER['PHP_SELF'] in a script at the address
 * http://example.com/foo/bar.php would be /foo/bar.php. The __FILE__ constant
 * contains the full path and filename of the current (i.e. included) file.
 * If PHP is running as a command-line processor this variable contains the
 * script name since PHP 4.3.0. Previously it was not available.
 */
        nxt_php_set_str(req, "PHP_SELF", &nxt_php_script,
                        track_vars_array TSRMLS_CC);
        nxt_php_set_str(req, "SCRIPT_NAME", &nxt_php_script,
                        track_vars_array TSRMLS_CC);

    } else {
        nxt_php_set_sptr(req, "PHP_SELF", &r->path, r->path_length,
                         track_vars_array TSRMLS_CC);
        nxt_php_set_sptr(req, "SCRIPT_NAME", &r->path, r->path_length,
                         track_vars_array TSRMLS_CC);
    }

    nxt_php_set_str(req, "SCRIPT_FILENAME", &ctx->script,
                    track_vars_array TSRMLS_CC);
    nxt_php_set_str(req, "DOCUMENT_ROOT", &nxt_php_root,
                    track_vars_array TSRMLS_CC);

    nxt_php_set_sptr(req, "REQUEST_METHOD", &r->method, r->method_length,
                     track_vars_array TSRMLS_CC);
    nxt_php_set_sptr(req, "REQUEST_URI", &r->target, r->target_length,
                     track_vars_array TSRMLS_CC);
    nxt_php_set_sptr(req, "QUERY_STRING", &r->query, r->query_length,
                     track_vars_array TSRMLS_CC);

    nxt_php_set_sptr(req, "REMOTE_ADDR", &r->remote, r->remote_length,
                     track_vars_array TSRMLS_CC);
    nxt_php_set_sptr(req, "SERVER_ADDR", &r->local, r->local_length,
                     track_vars_array TSRMLS_CC);

    nxt_php_set_sptr(req, "SERVER_NAME", &r->server_name, r->server_name_length,
                     track_vars_array TSRMLS_CC);
    nxt_php_set_cstr(req, "SERVER_PORT", "80", 2, track_vars_array TSRMLS_CC);

    if (r->tls) {
        nxt_php_set_cstr(req, "HTTPS", "on", 2, track_vars_array TSRMLS_CC);
    }

    f_end = r->fields + r->fields_count;
    for (f = r->fields; f < f_end; f++) {
        name = nxt_unit_sptr_get(&f->name);

        nxt_php_set_sptr(req, name, &f->value, f->value_length,
                         track_vars_array TSRMLS_CC);
    }

    if (r->content_length_field != NXT_UNIT_NONE_FIELD) {
        f = r->fields + r->content_length_field;

        nxt_php_set_sptr(req, "CONTENT_LENGTH", &f->value, f->value_length,
                         track_vars_array TSRMLS_CC);
    }

    if (r->content_type_field != NXT_UNIT_NONE_FIELD) {
        f = r->fields + r->content_type_field;

        nxt_php_set_sptr(req, "CONTENT_TYPE", &f->value, f->value_length,
                         track_vars_array TSRMLS_CC);
    }
}


static void
nxt_php_set_sptr(nxt_unit_request_info_t *req, const char *name,
    nxt_unit_sptr_t *v, uint32_t len, zval *track_vars_array TSRMLS_DC)
{
    char  *str;

    str = nxt_unit_sptr_get(v);

    nxt_unit_req_debug(req, "php: register %s='%.*s'", name, (int) len, str);

    php_register_variable_safe((char *) name, str, len,
                               track_vars_array TSRMLS_CC);
}


nxt_inline void
nxt_php_set_str(nxt_unit_request_info_t *req, const char *name,
    nxt_str_t *s, zval *track_vars_array TSRMLS_DC)
{
    nxt_php_set_cstr(req, name, (char *) s->start, s->length,
                     track_vars_array TSRMLS_CC);
}


static void
nxt_php_set_cstr(nxt_unit_request_info_t *req, const char *name,
    const char *cstr, uint32_t len, zval *track_vars_array TSRMLS_DC)
{
    if (nxt_slow_path(cstr == NULL)) {
        return;
    }

    nxt_unit_req_debug(req, "php: register %s='%.*s'", name, (int) len, cstr);

    php_register_variable_safe((char *) name, (char *) cstr, len,
                               track_vars_array TSRMLS_CC);
}


#ifdef NXT_HAVE_PHP_LOG_MESSAGE_WITH_SYSLOG_TYPE
static void
nxt_php_log_message(char *message, int syslog_type_int)
#else
static void
nxt_php_log_message(char *message TSRMLS_DC)
#endif
{
    nxt_log(nxt_php_task, NXT_LOG_NOTICE, "php message: %s", message);
}