1 2 /* 3 * Copyright (C) Valentin V. Bartenev 4 * Copyright (C) NGINX, Inc. 5 */ 6 7 #include "php.h" 8 #include "SAPI.h" 9 #include "php_main.h" 10 #include "php_variables.h" 11 12 #include <nxt_main.h> 13 #include <nxt_application.h> 14 15 16 typedef struct { 17 size_t max_name; 18 19 nxt_str_t *cookie; 20 nxt_str_t *content_type; 21 nxt_str_t *content_length; 22 23 nxt_str_t script; 24 nxt_str_t query; 25 26 size_t script_name_len; 27 28 off_t content_length_n; 29 } nxt_php_ctx_t; 30 31 32 nxt_int_t nxt_php_init(nxt_thread_t *thr); 33 nxt_int_t nxt_php_request_init(nxt_app_request_t *r); 34 nxt_int_t nxt_php_request_header(nxt_app_request_t *r, 35 nxt_app_header_field_t *field); 36 nxt_int_t nxt_php_handler(nxt_app_request_t *r); 37 38 39 nxt_int_t nxt_python_init(); 40 41 42 static nxt_int_t nxt_php_opts(nxt_log_t *log); 43 44 45 static int nxt_php_startup(sapi_module_struct *sapi_module); 46 static int nxt_php_send_headers(sapi_headers_struct *sapi_headers); 47 static char *nxt_php_read_cookies(void); 48 static void nxt_php_register_variables(zval *track_vars_array); 49 static void nxt_php_log_message(char *message); 50 51 #define NXT_PHP7 1 52 53 #ifdef NXT_PHP7 54 static size_t nxt_php_unbuffered_write(const char *str, 55 size_t str_length TSRMLS_DC); 56 static size_t nxt_php_read_post(char *buffer, size_t count_bytes TSRMLS_DC); 57 #else 58 static int nxt_php_unbuffered_write(const char *str, uint str_length TSRMLS_DC); 59 static int nxt_php_read_post(char *buffer, uint count_bytes TSRMLS_DC); 60 #endif 61 62 63 static sapi_module_struct nxt_php_sapi_module = 64 { 65 (char *) "cli-server", 66 (char *) "nginext", 67 68 nxt_php_startup, /* startup */ 69 php_module_shutdown_wrapper, /* shutdown */ 70 71 NULL, /* activate */ 72 NULL, /* deactivate */ 73 74 nxt_php_unbuffered_write, /* unbuffered write */ 75 NULL, /* flush */ 76 NULL, /* get uid */ 77 NULL, /* getenv */ 78 79 php_error, /* error handler */ 80 81 NULL, /* header handler */ 82 nxt_php_send_headers, /* send headers handler */ 83 NULL, /* send header handler */ 84 85 nxt_php_read_post, /* read POST data */ 86 nxt_php_read_cookies, /* read Cookies */ 87 88 nxt_php_register_variables, /* register server variables */ 89 nxt_php_log_message, /* log message */ 90 91 NULL, NULL, NULL, NULL, NULL, NULL, 92 NULL, NULL, 0, 0, NULL, NULL, NULL, 93 NULL, NULL, NULL, 0, NULL, NULL, NULL 94 }; 95 96 97 static nxt_str_t nxt_php_path; 98 static nxt_str_t nxt_php_root; 99 static nxt_str_t nxt_php_script; 100 101 102 nxt_int_t 103 nxt_php_init(nxt_thread_t *thr) 104 { 105 if (nxt_php_opts(thr->log)) { 106 return NXT_ERROR; 107 } 108 109 sapi_startup(&nxt_php_sapi_module); 110 nxt_php_startup(&nxt_php_sapi_module); 111 112 return NXT_OK; 113 } 114 115 116 static nxt_int_t 117 nxt_php_opts(nxt_log_t *log) 118 { 119 char **argv; 120 u_char *p; 121 nxt_uint_t i; 122 123 argv = nxt_process_argv; 124 125 while (*argv != NULL) { 126 p = (u_char *) *argv++; 127 128 if (nxt_strcmp(p, "--php") == 0) { 129 if (*argv == NULL) { 130 nxt_log_error(NXT_LOG_ERR, log, 131 "no argument for option \"--php\""); 132 return NXT_ERROR; 133 } 134 135 p = (u_char *) *argv; 136 137 nxt_php_root.data = p; 138 nxt_php_path.data = p; 139 140 i = 0; 141 142 for ( /* void */ ; p[i] != '\0'; i++) { 143 if (p[i] == '/') { 144 nxt_php_script.data = &p[i]; 145 nxt_php_root.len = i; 146 } 147 } 148 149 nxt_php_path.len = i; 150 nxt_php_script.len = i - nxt_php_root.len; 151 152 nxt_log_error(NXT_LOG_INFO, log, "php script \"%V\" root: \"%V\"", 153 &nxt_php_script, &nxt_php_root); 154 155 return NXT_OK; 156 } 157 } 158 159 nxt_log_error(NXT_LOG_ERR, log, "no option \"--php\" specified"); 160 161 return NXT_ERROR; 162 } 163 164 165 nxt_int_t 166 nxt_php_request_init(nxt_app_request_t *r) 167 { 168 nxt_php_ctx_t *ctx; 169 170 ctx = nxt_mem_zalloc(r->mem_pool, sizeof(nxt_php_ctx_t)); 171 if (nxt_slow_path(ctx == NULL)) { 172 return NXT_ERROR; 173 } 174 175 r->ctx = ctx; 176 177 return NXT_OK; 178 } 179 180 181 182 nxt_int_t 183 nxt_php_request_header(nxt_app_request_t *r, nxt_app_header_field_t *field) 184 { 185 nxt_php_ctx_t *ctx; 186 187 static const u_char cookie[6] = "Cookie"; 188 static const u_char content_length[14] = "Content-Length"; 189 static const u_char content_type[12] = "Content-Type"; 190 191 ctx = r->ctx; 192 193 ctx->max_name = nxt_max(ctx->max_name, field->name.len); 194 195 if (field->name.len == sizeof(cookie) 196 && nxt_memcasecmp(field->name.data, cookie, sizeof(cookie)) == 0) 197 { 198 ctx->cookie = &field->value; 199 200 } else if (field->name.len == sizeof(content_length) 201 && nxt_memcasecmp(field->name.data, content_length, 202 sizeof(content_length)) == 0) 203 { 204 ctx->content_length = &field->value; 205 ctx->content_length_n = nxt_off_t_parse(field->value.data, 206 field->value.len); 207 208 } else if (field->name.len == sizeof(content_type) 209 && nxt_memcasecmp(field->name.data, content_type, 210 sizeof(content_type)) == 0) 211 { 212 ctx->content_type = &field->value; 213 field->value.data[field->value.len] = '\0'; 214 } 215 216 return NXT_OK; 217 } 218 219 220 #define ABS_MODE 1 221 222 223 #if !ABS_MODE 224 static const u_char root[] = "/home/vbart/Development/tests/php/wordpress"; 225 #endif 226 227 228 nxt_int_t 229 nxt_php_handler(nxt_app_request_t *r) 230 { 231 u_char *query; 232 #if !ABS_MODE 233 u_char *p; 234 #endif 235 nxt_php_ctx_t *ctx; 236 zend_file_handle file_handle; 237 238 #if ABS_MODE 239 if (nxt_php_path.len == 0) { 240 return NXT_ERROR; 241 } 242 #endif 243 244 r->header.path.data[r->header.path.len] = '\0'; 245 r->header.method.data[r->header.method.len] = '\0'; 246 247 ctx = r->ctx; 248 249 query = nxt_memchr(r->header.path.data, '?', r->header.path.len); 250 251 if (query != NULL) { 252 ctx->script_name_len = query - r->header.path.data; 253 254 ctx->query.data = query + 1; 255 ctx->query.len = r->header.path.data + r->header.path.len 256 - ctx->query.data; 257 258 } else { 259 ctx->script_name_len = r->header.path.len; 260 } 261 262 #if !ABS_MODE 263 ctx->script.len = sizeof(root) - 1 + ctx->script_name_len; 264 ctx->script.data = nxt_mem_nalloc(r->mem_pool, ctx->script.len + 1); 265 266 if (nxt_slow_path(ctx->script.data == NULL)) { 267 return NXT_ERROR; 268 } 269 270 p = nxt_cpymem(ctx->script.data, root, sizeof(root) - 1); 271 p = nxt_cpymem(p, r->header.path.data, ctx->script_name_len); 272 *p = '\0'; 273 #endif 274 275 SG(server_context) = r; 276 SG(request_info).request_uri = (char *) r->header.path.data; 277 SG(request_info).request_method = (char *) r->header.method.data; 278 279 SG(request_info).proto_num = 1001; 280 281 SG(request_info).query_string = (char *) ctx->query.data; 282 SG(request_info).content_length = ctx->content_length_n; 283 284 if (ctx->content_type != NULL) { 285 SG(request_info).content_type = (char *) ctx->content_type->data; 286 } 287 288 SG(sapi_headers).http_response_code = 200; 289 290 SG(request_info).path_translated = NULL; 291 292 file_handle.type = ZEND_HANDLE_FILENAME; 293 #if ABS_MODE 294 file_handle.filename = (char *) nxt_php_path.data; 295 #else 296 file_handle.filename = (char *) ctx->script.data; 297 #endif 298 file_handle.free_filename = 0; 299 file_handle.opened_path = NULL; 300 301 #if ABS_MODE 302 nxt_log_debug(r->log, "run script %V in absolute mode", &nxt_php_path); 303 #else 304 nxt_log_debug(r->log, "run script %V", &ctx->script); 305 #endif 306 307 if (nxt_slow_path(php_request_startup() == FAILURE)) { 308 return NXT_ERROR; 309 } 310 311 php_execute_script(&file_handle TSRMLS_CC); 312 php_request_shutdown(NULL); 313 314 return NXT_OK; 315 } 316 317 318 static int 319 nxt_php_startup(sapi_module_struct *sapi_module) 320 { 321 return php_module_startup(sapi_module, NULL, 0); 322 } 323 324 325 #ifdef NXT_PHP7 326 static size_t 327 nxt_php_unbuffered_write(const char *str, size_t str_length TSRMLS_DC) 328 #else 329 static int 330 nxt_php_unbuffered_write(const char *str, uint str_length TSRMLS_DC) 331 #endif 332 { 333 nxt_app_request_t *r; 334 335 r = SG(server_context); 336 337 nxt_app_write(r, (u_char *) str, str_length); 338 339 return str_length; 340 } 341 342 343 static int 344 nxt_php_send_headers(sapi_headers_struct *sapi_headers TSRMLS_DC) 345 { 346 size_t len; 347 nxt_app_request_t *r; 348 sapi_header_struct *h; 349 zend_llist_position zpos; 350 u_char *p, *status, buf[4096]; 351 352 static const u_char default_repsonse[] 353 = "HTTP/1.1 200 OK\r\n" 354 "Server: nginext/0.1\r\n" 355 "Content-Type: text/html; charset=UTF-8\r\n" 356 "Connection: close\r\n" 357 "\r\n"; 358 359 static const u_char default_headers[] 360 = "Server: nginext/0.1\r\n" 361 "Connection: close\r\n"; 362 363 r = SG(server_context); 364 365 if (SG(request_info).no_headers == 1) { 366 nxt_app_write(r, default_repsonse, sizeof(default_repsonse) - 1); 367 return SAPI_HEADER_SENT_SUCCESSFULLY; 368 } 369 370 if (SG(sapi_headers).http_status_line) { 371 status = (u_char *) SG(sapi_headers).http_status_line + 9; 372 len = nxt_strlen(status); 373 374 p = nxt_cpymem(buf, "HTTP/1.1 ", sizeof("HTTP/1.1 ") - 1); 375 p = nxt_cpymem(p, status, len); 376 *p++ = '\r'; *p++ = '\n'; 377 378 } else if (SG(sapi_headers).http_response_code) { 379 p = nxt_cpymem(buf, "HTTP/1.1 ", sizeof("HTTP/1.1 ") - 1); 380 p = nxt_sprintf(p, buf + sizeof(buf), "%03d", 381 SG(sapi_headers).http_response_code); 382 *p++ = '\r'; *p++ = '\n'; 383 384 } else { 385 p = nxt_cpymem(buf, "HTTP/1.1 200 OK\r\n", 386 sizeof("HTTP/1.1 200 OK\r\n") - 1); 387 } 388 389 p = nxt_cpymem(p, default_headers, sizeof(default_headers) - 1); 390 391 h = zend_llist_get_first_ex(&sapi_headers->headers, &zpos); 392 393 while (h) { 394 p = nxt_cpymem(p, h->header, h->header_len); 395 *p++ = '\r'; *p++ = '\n'; 396 397 h = zend_llist_get_next_ex(&sapi_headers->headers, &zpos); 398 } 399 400 *p++ = '\r'; *p++ = '\n'; 401 402 nxt_app_write(r, buf, p - buf); 403 404 return SAPI_HEADER_SENT_SUCCESSFULLY; 405 } 406 407 408 #ifdef NXT_PHP7 409 static size_t 410 nxt_php_read_post(char *buffer, size_t count_bytes TSRMLS_DC) 411 #else 412 static int 413 nxt_php_read_post(char *buffer, uint count_bytes TSRMLS_DC) 414 #endif 415 { 416 off_t rest; 417 size_t size; 418 ssize_t n; 419 nxt_err_t err; 420 nxt_php_ctx_t *ctx; 421 nxt_app_request_t *r; 422 423 r = SG(server_context); 424 ctx = r->ctx; 425 426 rest = ctx->content_length_n - SG(read_post_bytes); 427 428 nxt_log_debug(r->log, "nxt_php_read_post %O", rest); 429 430 if (rest == 0) { 431 return 0; 432 } 433 434 size = 0; 435 #ifdef NXT_PHP7 436 count_bytes = (size_t) nxt_min(rest, (off_t) count_bytes); 437 #else 438 count_bytes = (uint) nxt_min(rest, (off_t) count_bytes); 439 #endif 440 441 if (r->body_preread.len != 0) { 442 size = nxt_min(r->body_preread.len, count_bytes); 443 444 nxt_memcpy(buffer, r->body_preread.data, size); 445 446 r->body_preread.len -= size; 447 r->body_preread.data += size; 448 449 if (size == count_bytes) { 450 return size; 451 } 452 } 453 454 nxt_log_debug(r->log, "recv %z", (size_t) count_bytes - size); 455 456 n = recv(r->event_conn->socket.fd, buffer + size, count_bytes - size, 0); 457 458 if (nxt_slow_path(n <= 0)) { 459 err = (n == 0) ? 0 : nxt_socket_errno; 460 461 nxt_log_error(NXT_LOG_ERR, r->log, "recv(%d, %uz) failed %E", 462 r->event_conn->socket.fd, (size_t) count_bytes - size, 463 err); 464 465 return size; 466 } 467 468 return size + n; 469 } 470 471 472 static char * 473 nxt_php_read_cookies(TSRMLS_D) 474 { 475 u_char *p; 476 nxt_php_ctx_t *ctx; 477 nxt_app_request_t *r; 478 479 r = SG(server_context); 480 ctx = r->ctx; 481 482 if (ctx->cookie == NULL) { 483 return NULL; 484 } 485 486 p = ctx->cookie->data; 487 p[ctx->cookie->len] = '\0'; 488 489 return (char *) p; 490 } 491 492 493 static void 494 nxt_php_register_variables(zval *track_vars_array TSRMLS_DC) 495 { 496 u_char *var, *p, ch; 497 nxt_uint_t i, n; 498 nxt_php_ctx_t *ctx; 499 nxt_app_request_t *r; 500 nxt_app_header_field_t *fld; 501 502 static const u_char prefix[5] = "HTTP_"; 503 504 r = SG(server_context); 505 ctx = r->ctx; 506 507 nxt_log_debug(r->log, "php register variables"); 508 509 php_register_variable_safe((char *) "PHP_SELF", 510 (char *) r->header.path.data, 511 ctx->script_name_len, track_vars_array TSRMLS_CC); 512 513 php_register_variable_safe((char *) "SERVER_PROTOCOL", 514 (char *) r->header.version.data, 515 r->header.version.len, track_vars_array TSRMLS_CC); 516 517 #if ABS_MODE 518 php_register_variable_safe((char *) "SCRIPT_NAME", 519 (char *) nxt_php_script.data, 520 nxt_php_script.len, track_vars_array TSRMLS_CC); 521 522 php_register_variable_safe((char *) "SCRIPT_FILENAME", 523 (char *) nxt_php_path.data, 524 nxt_php_path.len, track_vars_array TSRMLS_CC); 525 526 php_register_variable_safe((char *) "DOCUMENT_ROOT", 527 (char *) nxt_php_root.data, 528 nxt_php_root.len, track_vars_array TSRMLS_CC); 529 #else 530 php_register_variable_safe((char *) "SCRIPT_NAME", 531 (char *) r->header.path.data, 532 ctx->script_name_len, track_vars_array TSRMLS_CC); 533 534 php_register_variable_safe((char *) "SCRIPT_FILENAME", 535 (char *) ctx->script.data, ctx->script.len, 536 track_vars_array TSRMLS_CC); 537 538 php_register_variable_safe((char *) "DOCUMENT_ROOT", (char *) root, 539 sizeof(root) - 1, track_vars_array TSRMLS_CC); 540 #endif 541 542 php_register_variable_safe((char *) "REQUEST_METHOD", 543 (char *) r->header.method.data, 544 r->header.method.len, track_vars_array TSRMLS_CC); 545 546 php_register_variable_safe((char *) "REQUEST_URI", 547 (char *) r->header.path.data, 548 r->header.path.len, track_vars_array TSRMLS_CC); 549 550 if (ctx->query.data != NULL) { 551 php_register_variable_safe((char *) "QUERY_STRING", 552 (char *) ctx->query.data, 553 ctx->query.len, track_vars_array TSRMLS_CC); 554 } 555 556 if (ctx->content_type != NULL) { 557 php_register_variable_safe((char *) "CONTENT_TYPE", 558 (char *) ctx->content_type->data, 559 ctx->content_type->len, track_vars_array TSRMLS_CC); 560 } 561 562 if (ctx->content_length != NULL) { 563 php_register_variable_safe((char *) "CONTENT_LENGTH", 564 (char *) ctx->content_length->data, 565 ctx->content_length->len, track_vars_array TSRMLS_CC); 566 } 567 568 var = nxt_mem_nalloc(r->mem_pool, sizeof(prefix) + ctx->max_name + 1); 569 570 if (nxt_slow_path(var == NULL)) { 571 return; 572 } 573 574 nxt_memcpy(var, prefix, sizeof(prefix)); 575 576 for (i = 0; i < r->header.fields_num; i++) { 577 fld = &r->header.fields[i]; 578 p = var + sizeof(prefix); 579 580 for (n = 0; n < fld->name.len; n++, p++) { 581 582 ch = fld->name.data[n]; 583 584 if (ch >= 'a' && ch <= 'z') { 585 *p = ch & ~0x20; 586 continue; 587 } 588 589 if (ch == '-') { 590 *p = '_'; 591 continue; 592 } 593 594 *p = ch; 595 } 596 597 *p = '\0'; 598 599 php_register_variable_safe((char *) var, (char *) fld->value.data, 600 fld->value.len, track_vars_array TSRMLS_CC); 601 } 602 603 return; 604 } 605 606 607 static void 608 nxt_php_log_message(char *message TSRMLS_DC) 609 { 610 return; 611 } 612