php_cli_server.c 78 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773
  1. /*
  2. +----------------------------------------------------------------------+
  3. | Copyright (c) The PHP Group |
  4. +----------------------------------------------------------------------+
  5. | This source file is subject to version 3.01 of the PHP license, |
  6. | that is bundled with this package in the file LICENSE, and is |
  7. | available through the world-wide-web at the following url: |
  8. | https://www.php.net/license/3_01.txt |
  9. | If you did not receive a copy of the PHP license and are unable to |
  10. | obtain it through the world-wide-web, please send a note to |
  11. | license@php.net so we can mail you a copy immediately. |
  12. +----------------------------------------------------------------------+
  13. | Author: Moriyoshi Koizumi <moriyoshi@php.net> |
  14. | Xinchen Hui <laruence@php.net> |
  15. +----------------------------------------------------------------------+
  16. */
  17. #include <stdio.h>
  18. #include <stdlib.h>
  19. #include <fcntl.h>
  20. #include <assert.h>
  21. #ifdef PHP_WIN32
  22. # include <process.h>
  23. # include <io.h>
  24. # include "win32/time.h"
  25. # include "win32/signal.h"
  26. # include "win32/php_registry.h"
  27. # include <sys/timeb.h>
  28. #else
  29. # include "php_config.h"
  30. #endif
  31. #ifdef __riscos__
  32. #include <unixlib/local.h>
  33. #endif
  34. #if HAVE_SYS_TIME_H
  35. #include <sys/time.h>
  36. #endif
  37. #if HAVE_UNISTD_H
  38. #include <unistd.h>
  39. #endif
  40. #include <signal.h>
  41. #include <locale.h>
  42. #if HAVE_DLFCN_H
  43. #include <dlfcn.h>
  44. #endif
  45. #include "SAPI.h"
  46. #include "php.h"
  47. #include "php_ini.h"
  48. #include "php_main.h"
  49. #include "php_globals.h"
  50. #include "php_variables.h"
  51. #include "zend_hash.h"
  52. #include "zend_modules.h"
  53. #include "fopen_wrappers.h"
  54. #include "http_status_codes.h"
  55. #include "zend_compile.h"
  56. #include "zend_execute.h"
  57. #include "zend_highlight.h"
  58. #include "zend_exceptions.h"
  59. #include "php_getopt.h"
  60. #ifndef PHP_WIN32
  61. # define php_select(m, r, w, e, t) select(m, r, w, e, t)
  62. # define SOCK_EINVAL EINVAL
  63. # define SOCK_EAGAIN EAGAIN
  64. # define SOCK_EINTR EINTR
  65. # define SOCK_EADDRINUSE EADDRINUSE
  66. #else
  67. # include "win32/select.h"
  68. # define SOCK_EINVAL WSAEINVAL
  69. # define SOCK_EAGAIN WSAEWOULDBLOCK
  70. # define SOCK_EINTR WSAEINTR
  71. # define SOCK_EADDRINUSE WSAEADDRINUSE
  72. #endif
  73. #include "ext/standard/file.h" /* for php_set_sock_blocking() :-( */
  74. #include "zend_smart_str.h"
  75. #include "ext/standard/html.h"
  76. #include "ext/standard/url.h" /* for php_raw_url_decode() */
  77. #include "ext/standard/php_string.h" /* for php_dirname() */
  78. #include "ext/date/php_date.h" /* for php_format_date() */
  79. #include "php_network.h"
  80. #include "php_http_parser.h"
  81. #include "php_cli_server.h"
  82. #include "php_cli_server_arginfo.h"
  83. #include "mime_type_map.h"
  84. #include "php_cli_process_title.h"
  85. #include "php_cli_process_title_arginfo.h"
  86. #define OUTPUT_NOT_CHECKED -1
  87. #define OUTPUT_IS_TTY 1
  88. #define OUTPUT_NOT_TTY 0
  89. #if HAVE_FORK
  90. # include <sys/wait.h>
  91. static pid_t php_cli_server_master;
  92. static pid_t *php_cli_server_workers;
  93. static zend_long php_cli_server_workers_max;
  94. #endif
  95. typedef struct php_cli_server_poller {
  96. fd_set rfds, wfds;
  97. struct {
  98. fd_set rfds, wfds;
  99. } active;
  100. php_socket_t max_fd;
  101. } php_cli_server_poller;
  102. typedef struct php_cli_server_request {
  103. enum php_http_method request_method;
  104. int protocol_version;
  105. char *request_uri;
  106. size_t request_uri_len;
  107. char *vpath;
  108. size_t vpath_len;
  109. char *path_translated;
  110. size_t path_translated_len;
  111. char *path_info;
  112. size_t path_info_len;
  113. char *query_string;
  114. size_t query_string_len;
  115. HashTable headers;
  116. HashTable headers_original_case;
  117. char *content;
  118. size_t content_len;
  119. const char *ext;
  120. size_t ext_len;
  121. zend_stat_t sb;
  122. } php_cli_server_request;
  123. typedef struct php_cli_server_chunk {
  124. struct php_cli_server_chunk *next;
  125. enum php_cli_server_chunk_type {
  126. PHP_CLI_SERVER_CHUNK_HEAP,
  127. PHP_CLI_SERVER_CHUNK_IMMORTAL
  128. } type;
  129. union {
  130. struct { void *block; char *p; size_t len; } heap;
  131. struct { const char *p; size_t len; } immortal;
  132. } data;
  133. } php_cli_server_chunk;
  134. typedef struct php_cli_server_buffer {
  135. php_cli_server_chunk *first;
  136. php_cli_server_chunk *last;
  137. } php_cli_server_buffer;
  138. typedef struct php_cli_server_content_sender {
  139. php_cli_server_buffer buffer;
  140. } php_cli_server_content_sender;
  141. typedef struct php_cli_server_client {
  142. struct php_cli_server *server;
  143. php_socket_t sock;
  144. struct sockaddr *addr;
  145. socklen_t addr_len;
  146. char *addr_str;
  147. size_t addr_str_len;
  148. php_http_parser parser;
  149. unsigned int request_read:1;
  150. char *current_header_name;
  151. size_t current_header_name_len;
  152. unsigned int current_header_name_allocated:1;
  153. char *current_header_value;
  154. size_t current_header_value_len;
  155. enum { HEADER_NONE=0, HEADER_FIELD, HEADER_VALUE } last_header_element;
  156. size_t post_read_offset;
  157. php_cli_server_request request;
  158. unsigned int content_sender_initialized:1;
  159. php_cli_server_content_sender content_sender;
  160. int file_fd;
  161. } php_cli_server_client;
  162. typedef struct php_cli_server {
  163. php_socket_t server_sock;
  164. php_cli_server_poller poller;
  165. int is_running;
  166. char *host;
  167. int port;
  168. int address_family;
  169. char *document_root;
  170. size_t document_root_len;
  171. char *router;
  172. size_t router_len;
  173. socklen_t socklen;
  174. HashTable clients;
  175. HashTable extension_mime_types;
  176. } php_cli_server;
  177. typedef struct php_cli_server_http_response_status_code_pair {
  178. int code;
  179. const char *str;
  180. } php_cli_server_http_response_status_code_pair;
  181. static php_cli_server_http_response_status_code_pair template_map[] = {
  182. { 400, "<h1>%s</h1><p>Your browser sent a request that this server could not understand.</p>" },
  183. { 404, "<h1>%s</h1><p>The requested resource <code class=\"url\">%s</code> was not found on this server.</p>" },
  184. { 500, "<h1>%s</h1><p>The server is temporarily unavailable.</p>" },
  185. { 501, "<h1>%s</h1><p>Request method not supported.</p>" }
  186. };
  187. #define PHP_CLI_SERVER_LOG_PROCESS 1
  188. #define PHP_CLI_SERVER_LOG_ERROR 2
  189. #define PHP_CLI_SERVER_LOG_MESSAGE 3
  190. static int php_cli_server_log_level = 3;
  191. #if HAVE_UNISTD_H || defined(PHP_WIN32)
  192. static int php_cli_output_is_tty = OUTPUT_NOT_CHECKED;
  193. #endif
  194. static const char php_cli_server_request_error_unexpected_eof[] = "Unexpected EOF";
  195. static size_t php_cli_server_client_send_through(php_cli_server_client *client, const char *str, size_t str_len);
  196. static php_cli_server_chunk *php_cli_server_chunk_heap_new_self_contained(size_t len);
  197. static void php_cli_server_buffer_append(php_cli_server_buffer *buffer, php_cli_server_chunk *chunk);
  198. static void php_cli_server_logf(int type, const char *format, ...);
  199. static void php_cli_server_log_response(php_cli_server_client *client, int status, const char *message);
  200. ZEND_DECLARE_MODULE_GLOBALS(cli_server)
  201. /* {{{ static char php_cli_server_css[]
  202. * copied from ext/standard/info.c
  203. */
  204. static const char php_cli_server_css[] = "<style>\n" \
  205. "body { background-color: #fcfcfc; color: #333333; margin: 0; padding:0; }\n" \
  206. "h1 { font-size: 1.5em; font-weight: normal; background-color: #9999cc; min-height:2em; line-height:2em; border-bottom: 1px inset black; margin: 0; }\n" \
  207. "h1, p { padding-left: 10px; }\n" \
  208. "code.url { background-color: #eeeeee; font-family:monospace; padding:0 2px;}\n" \
  209. "</style>\n";
  210. /* }}} */
  211. #ifdef PHP_WIN32
  212. int php_cli_server_get_system_time(char *buf) {
  213. struct _timeb system_time;
  214. errno_t err;
  215. if (buf == NULL) {
  216. return -1;
  217. }
  218. _ftime(&system_time);
  219. err = ctime_s(buf, 52, &(system_time.time) );
  220. if (err) {
  221. return -1;
  222. }
  223. return 0;
  224. }
  225. #else
  226. int php_cli_server_get_system_time(char *buf) {
  227. struct timeval tv;
  228. struct tm tm;
  229. gettimeofday(&tv, NULL);
  230. /* TODO: should be checked for NULL tm/return value */
  231. php_localtime_r(&tv.tv_sec, &tm);
  232. php_asctime_r(&tm, buf);
  233. return 0;
  234. }
  235. #endif
  236. static void char_ptr_dtor_p(zval *zv) /* {{{ */
  237. {
  238. pefree(Z_PTR_P(zv), 1);
  239. } /* }}} */
  240. static char *get_last_error(void) /* {{{ */
  241. {
  242. return pestrdup(strerror(errno), 1);
  243. } /* }}} */
  244. static int status_comp(const void *a, const void *b) /* {{{ */
  245. {
  246. const http_response_status_code_pair *pa = (const http_response_status_code_pair *) a;
  247. const http_response_status_code_pair *pb = (const http_response_status_code_pair *) b;
  248. if (pa->code < pb->code) {
  249. return -1;
  250. } else if (pa->code > pb->code) {
  251. return 1;
  252. }
  253. return 0;
  254. } /* }}} */
  255. static const char *get_status_string(int code) /* {{{ */
  256. {
  257. http_response_status_code_pair needle = {code, NULL},
  258. *result = NULL;
  259. result = bsearch(&needle, http_status_map, http_status_map_len, sizeof(needle), status_comp);
  260. if (result) {
  261. return result->str;
  262. }
  263. /* Returning NULL would require complicating append_http_status_line() to
  264. * not segfault in that case, so let's just return a placeholder, since RFC
  265. * 2616 requires a reason phrase. This is basically what a lot of other Web
  266. * servers do in this case anyway. */
  267. return "Unknown Status Code";
  268. } /* }}} */
  269. static const char *get_template_string(int code) /* {{{ */
  270. {
  271. size_t e = (sizeof(template_map) / sizeof(php_cli_server_http_response_status_code_pair));
  272. size_t s = 0;
  273. while (e != s) {
  274. size_t c = MIN((e + s + 1) / 2, e - 1);
  275. int d = template_map[c].code;
  276. if (d > code) {
  277. e = c;
  278. } else if (d < code) {
  279. s = c;
  280. } else {
  281. return template_map[c].str;
  282. }
  283. }
  284. return NULL;
  285. } /* }}} */
  286. static void append_http_status_line(smart_str *buffer, int protocol_version, int response_code, int persistent) /* {{{ */
  287. {
  288. if (!response_code) {
  289. response_code = 200;
  290. }
  291. smart_str_appendl_ex(buffer, "HTTP", 4, persistent);
  292. smart_str_appendc_ex(buffer, '/', persistent);
  293. smart_str_append_long_ex(buffer, protocol_version / 100, persistent);
  294. smart_str_appendc_ex(buffer, '.', persistent);
  295. smart_str_append_long_ex(buffer, protocol_version % 100, persistent);
  296. smart_str_appendc_ex(buffer, ' ', persistent);
  297. smart_str_append_long_ex(buffer, response_code, persistent);
  298. smart_str_appendc_ex(buffer, ' ', persistent);
  299. smart_str_appends_ex(buffer, get_status_string(response_code), persistent);
  300. smart_str_appendl_ex(buffer, "\r\n", 2, persistent);
  301. } /* }}} */
  302. static void append_essential_headers(smart_str* buffer, php_cli_server_client *client, int persistent) /* {{{ */
  303. {
  304. char *val;
  305. struct timeval tv = {0};
  306. if (NULL != (val = zend_hash_str_find_ptr(&client->request.headers, "host", sizeof("host")-1))) {
  307. smart_str_appends_ex(buffer, "Host: ", persistent);
  308. smart_str_appends_ex(buffer, val, persistent);
  309. smart_str_appends_ex(buffer, "\r\n", persistent);
  310. }
  311. if (!gettimeofday(&tv, NULL)) {
  312. zend_string *dt = php_format_date("D, d M Y H:i:s", sizeof("D, d M Y H:i:s") - 1, tv.tv_sec, 0);
  313. smart_str_appends_ex(buffer, "Date: ", persistent);
  314. smart_str_appends_ex(buffer, dt->val, persistent);
  315. smart_str_appends_ex(buffer, " GMT\r\n", persistent);
  316. zend_string_release_ex(dt, 0);
  317. }
  318. smart_str_appendl_ex(buffer, "Connection: close\r\n", sizeof("Connection: close\r\n") - 1, persistent);
  319. } /* }}} */
  320. static const char *get_mime_type(const php_cli_server *server, const char *ext, size_t ext_len) /* {{{ */
  321. {
  322. char *ret;
  323. ALLOCA_FLAG(use_heap)
  324. char *ext_lower = do_alloca(ext_len + 1, use_heap);
  325. zend_str_tolower_copy(ext_lower, ext, ext_len);
  326. ret = zend_hash_str_find_ptr(&server->extension_mime_types, ext_lower, ext_len);
  327. free_alloca(ext_lower, use_heap);
  328. return (const char*)ret;
  329. } /* }}} */
  330. PHP_FUNCTION(apache_request_headers) /* {{{ */
  331. {
  332. php_cli_server_client *client;
  333. HashTable *headers;
  334. zend_string *key;
  335. char *value;
  336. zval tmp;
  337. if (zend_parse_parameters_none() == FAILURE) {
  338. RETURN_THROWS();
  339. }
  340. client = SG(server_context);
  341. headers = &client->request.headers_original_case;
  342. array_init_size(return_value, zend_hash_num_elements(headers));
  343. ZEND_HASH_FOREACH_STR_KEY_PTR(headers, key, value) {
  344. ZVAL_STRING(&tmp, value);
  345. zend_symtable_update(Z_ARRVAL_P(return_value), key, &tmp);
  346. } ZEND_HASH_FOREACH_END();
  347. }
  348. /* }}} */
  349. static void add_response_header(sapi_header_struct *h, zval *return_value) /* {{{ */
  350. {
  351. char *s, *p;
  352. ptrdiff_t len;
  353. ALLOCA_FLAG(use_heap)
  354. if (h->header_len > 0) {
  355. p = strchr(h->header, ':');
  356. len = p - h->header;
  357. if (p && (len > 0)) {
  358. while (len > 0 && (h->header[len-1] == ' ' || h->header[len-1] == '\t')) {
  359. len--;
  360. }
  361. if (len) {
  362. s = do_alloca(len + 1, use_heap);
  363. memcpy(s, h->header, len);
  364. s[len] = 0;
  365. do {
  366. p++;
  367. } while (*p == ' ' || *p == '\t');
  368. add_assoc_stringl_ex(return_value, s, (uint32_t)len, p, h->header_len - (p - h->header));
  369. free_alloca(s, use_heap);
  370. }
  371. }
  372. }
  373. }
  374. /* }}} */
  375. PHP_FUNCTION(apache_response_headers) /* {{{ */
  376. {
  377. if (zend_parse_parameters_none() == FAILURE) {
  378. RETURN_THROWS();
  379. }
  380. array_init(return_value);
  381. zend_llist_apply_with_argument(&SG(sapi_headers).headers, (llist_apply_with_arg_func_t)add_response_header, return_value);
  382. }
  383. /* }}} */
  384. /* {{{ cli_server module */
  385. static void cli_server_init_globals(zend_cli_server_globals *cg)
  386. {
  387. cg->color = 0;
  388. }
  389. PHP_INI_BEGIN()
  390. STD_PHP_INI_BOOLEAN("cli_server.color", "0", PHP_INI_ALL, OnUpdateBool, color, zend_cli_server_globals, cli_server_globals)
  391. PHP_INI_END()
  392. static PHP_MINIT_FUNCTION(cli_server)
  393. {
  394. ZEND_INIT_MODULE_GLOBALS(cli_server, cli_server_init_globals, NULL);
  395. REGISTER_INI_ENTRIES();
  396. return SUCCESS;
  397. }
  398. static PHP_MSHUTDOWN_FUNCTION(cli_server)
  399. {
  400. UNREGISTER_INI_ENTRIES();
  401. return SUCCESS;
  402. }
  403. static PHP_MINFO_FUNCTION(cli_server)
  404. {
  405. DISPLAY_INI_ENTRIES();
  406. }
  407. zend_module_entry cli_server_module_entry = {
  408. STANDARD_MODULE_HEADER,
  409. "cli_server",
  410. NULL,
  411. PHP_MINIT(cli_server),
  412. PHP_MSHUTDOWN(cli_server),
  413. NULL,
  414. NULL,
  415. PHP_MINFO(cli_server),
  416. PHP_VERSION,
  417. STANDARD_MODULE_PROPERTIES
  418. };
  419. /* }}} */
  420. const zend_function_entry server_additional_functions[] = {
  421. PHP_FE(cli_set_process_title, arginfo_cli_set_process_title)
  422. PHP_FE(cli_get_process_title, arginfo_cli_get_process_title)
  423. PHP_FE(apache_request_headers, arginfo_apache_request_headers)
  424. PHP_FE(apache_response_headers, arginfo_apache_response_headers)
  425. PHP_FALIAS(getallheaders, apache_request_headers, arginfo_getallheaders)
  426. PHP_FE_END
  427. };
  428. static int sapi_cli_server_startup(sapi_module_struct *sapi_module) /* {{{ */
  429. {
  430. return php_module_startup(sapi_module, &cli_server_module_entry, 1);
  431. } /* }}} */
  432. static size_t sapi_cli_server_ub_write(const char *str, size_t str_length) /* {{{ */
  433. {
  434. php_cli_server_client *client = SG(server_context);
  435. if (!client) {
  436. return 0;
  437. }
  438. return php_cli_server_client_send_through(client, str, str_length);
  439. } /* }}} */
  440. static void sapi_cli_server_flush(void *server_context) /* {{{ */
  441. {
  442. php_cli_server_client *client = server_context;
  443. if (!client) {
  444. return;
  445. }
  446. if (!ZEND_VALID_SOCKET(client->sock)) {
  447. php_handle_aborted_connection();
  448. return;
  449. }
  450. if (!SG(headers_sent)) {
  451. sapi_send_headers();
  452. SG(headers_sent) = 1;
  453. }
  454. } /* }}} */
  455. static int sapi_cli_server_discard_headers(sapi_headers_struct *sapi_headers) /* {{{ */{
  456. return SAPI_HEADER_SENT_SUCCESSFULLY;
  457. }
  458. /* }}} */
  459. static int sapi_cli_server_send_headers(sapi_headers_struct *sapi_headers) /* {{{ */
  460. {
  461. php_cli_server_client *client = SG(server_context);
  462. smart_str buffer = { 0 };
  463. sapi_header_struct *h;
  464. zend_llist_position pos;
  465. if (client == NULL || SG(request_info).no_headers) {
  466. return SAPI_HEADER_SENT_SUCCESSFULLY;
  467. }
  468. if (SG(sapi_headers).http_status_line) {
  469. smart_str_appends(&buffer, SG(sapi_headers).http_status_line);
  470. smart_str_appendl(&buffer, "\r\n", 2);
  471. } else {
  472. append_http_status_line(&buffer, client->request.protocol_version, SG(sapi_headers).http_response_code, 0);
  473. }
  474. append_essential_headers(&buffer, client, 0);
  475. h = (sapi_header_struct*)zend_llist_get_first_ex(&sapi_headers->headers, &pos);
  476. while (h) {
  477. if (h->header_len) {
  478. smart_str_appendl(&buffer, h->header, h->header_len);
  479. smart_str_appendl(&buffer, "\r\n", 2);
  480. }
  481. h = (sapi_header_struct*)zend_llist_get_next_ex(&sapi_headers->headers, &pos);
  482. }
  483. smart_str_appendl(&buffer, "\r\n", 2);
  484. php_cli_server_client_send_through(client, ZSTR_VAL(buffer.s), ZSTR_LEN(buffer.s));
  485. smart_str_free(&buffer);
  486. return SAPI_HEADER_SENT_SUCCESSFULLY;
  487. }
  488. /* }}} */
  489. static char *sapi_cli_server_read_cookies(void) /* {{{ */
  490. {
  491. php_cli_server_client *client = SG(server_context);
  492. char *val;
  493. if (NULL == (val = zend_hash_str_find_ptr(&client->request.headers, "cookie", sizeof("cookie")-1))) {
  494. return NULL;
  495. }
  496. return val;
  497. } /* }}} */
  498. static size_t sapi_cli_server_read_post(char *buf, size_t count_bytes) /* {{{ */
  499. {
  500. php_cli_server_client *client = SG(server_context);
  501. if (client->request.content) {
  502. size_t content_len = client->request.content_len;
  503. size_t nbytes_copied = MIN(client->post_read_offset + count_bytes, content_len) - client->post_read_offset;
  504. memmove(buf, client->request.content + client->post_read_offset, nbytes_copied);
  505. client->post_read_offset += nbytes_copied;
  506. return nbytes_copied;
  507. }
  508. return 0;
  509. } /* }}} */
  510. static void sapi_cli_server_register_variable(zval *track_vars_array, const char *key, const char *val) /* {{{ */
  511. {
  512. char *new_val = (char *)val;
  513. size_t new_val_len;
  514. if (NULL == val) {
  515. return;
  516. }
  517. if (sapi_module.input_filter(PARSE_SERVER, (char*)key, &new_val, strlen(val), &new_val_len)) {
  518. php_register_variable_safe((char *)key, new_val, new_val_len, track_vars_array);
  519. }
  520. } /* }}} */
  521. static int sapi_cli_server_register_entry_cb(char **entry, int num_args, va_list args, zend_hash_key *hash_key) /* {{{ */ {
  522. zval *track_vars_array = va_arg(args, zval *);
  523. if (hash_key->key) {
  524. char *real_key, *key;
  525. uint32_t i;
  526. key = estrndup(ZSTR_VAL(hash_key->key), ZSTR_LEN(hash_key->key));
  527. for(i=0; i<ZSTR_LEN(hash_key->key); i++) {
  528. if (key[i] == '-') {
  529. key[i] = '_';
  530. } else {
  531. key[i] = toupper(key[i]);
  532. }
  533. }
  534. spprintf(&real_key, 0, "%s_%s", "HTTP", key);
  535. if (strcmp(key, "CONTENT_TYPE") == 0 || strcmp(key, "CONTENT_LENGTH") == 0) {
  536. sapi_cli_server_register_variable(track_vars_array, key, *entry);
  537. }
  538. sapi_cli_server_register_variable(track_vars_array, real_key, *entry);
  539. efree(key);
  540. efree(real_key);
  541. }
  542. return ZEND_HASH_APPLY_KEEP;
  543. }
  544. /* }}} */
  545. static void sapi_cli_server_register_variables(zval *track_vars_array) /* {{{ */
  546. {
  547. php_cli_server_client *client = SG(server_context);
  548. sapi_cli_server_register_variable(track_vars_array, "DOCUMENT_ROOT", client->server->document_root);
  549. {
  550. char *tmp;
  551. if ((tmp = strrchr(client->addr_str, ':'))) {
  552. char addr[64], port[8];
  553. const char *addr_start = client->addr_str, *addr_end = tmp;
  554. if (addr_start[0] == '[') addr_start++;
  555. if (addr_end[-1] == ']') addr_end--;
  556. strncpy(port, tmp + 1, 8);
  557. port[7] = '\0';
  558. strncpy(addr, addr_start, addr_end - addr_start);
  559. addr[addr_end - addr_start] = '\0';
  560. sapi_cli_server_register_variable(track_vars_array, "REMOTE_ADDR", addr);
  561. sapi_cli_server_register_variable(track_vars_array, "REMOTE_PORT", port);
  562. } else {
  563. sapi_cli_server_register_variable(track_vars_array, "REMOTE_ADDR", client->addr_str);
  564. }
  565. }
  566. {
  567. char *tmp;
  568. spprintf(&tmp, 0, "PHP %s Development Server", PHP_VERSION);
  569. sapi_cli_server_register_variable(track_vars_array, "SERVER_SOFTWARE", tmp);
  570. efree(tmp);
  571. }
  572. {
  573. char *tmp;
  574. spprintf(&tmp, 0, "HTTP/%d.%d", client->request.protocol_version / 100, client->request.protocol_version % 100);
  575. sapi_cli_server_register_variable(track_vars_array, "SERVER_PROTOCOL", tmp);
  576. efree(tmp);
  577. }
  578. sapi_cli_server_register_variable(track_vars_array, "SERVER_NAME", client->server->host);
  579. {
  580. char *tmp;
  581. spprintf(&tmp, 0, "%i", client->server->port);
  582. sapi_cli_server_register_variable(track_vars_array, "SERVER_PORT", tmp);
  583. efree(tmp);
  584. }
  585. sapi_cli_server_register_variable(track_vars_array, "REQUEST_URI", client->request.request_uri);
  586. sapi_cli_server_register_variable(track_vars_array, "REQUEST_METHOD", SG(request_info).request_method);
  587. sapi_cli_server_register_variable(track_vars_array, "SCRIPT_NAME", client->request.vpath);
  588. if (SG(request_info).path_translated) {
  589. sapi_cli_server_register_variable(track_vars_array, "SCRIPT_FILENAME", SG(request_info).path_translated);
  590. } else if (client->server->router) {
  591. sapi_cli_server_register_variable(track_vars_array, "SCRIPT_FILENAME", client->server->router);
  592. }
  593. if (client->request.path_info) {
  594. sapi_cli_server_register_variable(track_vars_array, "PATH_INFO", client->request.path_info);
  595. }
  596. if (client->request.path_info_len) {
  597. char *tmp;
  598. spprintf(&tmp, 0, "%s%s", client->request.vpath, client->request.path_info);
  599. sapi_cli_server_register_variable(track_vars_array, "PHP_SELF", tmp);
  600. efree(tmp);
  601. } else {
  602. sapi_cli_server_register_variable(track_vars_array, "PHP_SELF", client->request.vpath);
  603. }
  604. if (client->request.query_string) {
  605. sapi_cli_server_register_variable(track_vars_array, "QUERY_STRING", client->request.query_string);
  606. }
  607. zend_hash_apply_with_arguments(&client->request.headers, (apply_func_args_t)sapi_cli_server_register_entry_cb, 1, track_vars_array);
  608. } /* }}} */
  609. static void sapi_cli_server_log_write(int type, const char *msg) /* {{{ */
  610. {
  611. char buf[52];
  612. if (php_cli_server_log_level < type) {
  613. return;
  614. }
  615. if (php_cli_server_get_system_time(buf) != 0) {
  616. memmove(buf, "unknown time, can't be fetched", sizeof("unknown time, can't be fetched"));
  617. } else {
  618. size_t l = strlen(buf);
  619. if (l > 0) {
  620. buf[l - 1] = '\0';
  621. } else {
  622. memmove(buf, "unknown", sizeof("unknown"));
  623. }
  624. }
  625. #ifdef HAVE_FORK
  626. if (php_cli_server_workers_max > 1) {
  627. fprintf(stderr, "[%ld] [%s] %s\n", (long) getpid(), buf, msg);
  628. } else {
  629. fprintf(stderr, "[%s] %s\n", buf, msg);
  630. }
  631. #else
  632. fprintf(stderr, "[%s] %s\n", buf, msg);
  633. #endif
  634. } /* }}} */
  635. static void sapi_cli_server_log_message(const char *msg, int syslog_type_int) /* {{{ */
  636. {
  637. sapi_cli_server_log_write(PHP_CLI_SERVER_LOG_MESSAGE, msg);
  638. } /* }}} */
  639. /* {{{ sapi_module_struct cli_server_sapi_module */
  640. sapi_module_struct cli_server_sapi_module = {
  641. "cli-server", /* name */
  642. "Built-in HTTP server", /* pretty name */
  643. sapi_cli_server_startup, /* startup */
  644. php_module_shutdown_wrapper, /* shutdown */
  645. NULL, /* activate */
  646. NULL, /* deactivate */
  647. sapi_cli_server_ub_write, /* unbuffered write */
  648. sapi_cli_server_flush, /* flush */
  649. NULL, /* get uid */
  650. NULL, /* getenv */
  651. php_error, /* error handler */
  652. NULL, /* header handler */
  653. sapi_cli_server_send_headers, /* send headers handler */
  654. NULL, /* send header handler */
  655. sapi_cli_server_read_post, /* read POST data */
  656. sapi_cli_server_read_cookies, /* read Cookies */
  657. sapi_cli_server_register_variables, /* register server variables */
  658. sapi_cli_server_log_message, /* Log message */
  659. NULL, /* Get request time */
  660. NULL, /* Child terminate */
  661. STANDARD_SAPI_MODULE_PROPERTIES
  662. }; /* }}} */
  663. static int php_cli_server_poller_ctor(php_cli_server_poller *poller) /* {{{ */
  664. {
  665. FD_ZERO(&poller->rfds);
  666. FD_ZERO(&poller->wfds);
  667. poller->max_fd = -1;
  668. return SUCCESS;
  669. } /* }}} */
  670. static void php_cli_server_poller_add(php_cli_server_poller *poller, int mode, php_socket_t fd) /* {{{ */
  671. {
  672. if (mode & POLLIN) {
  673. PHP_SAFE_FD_SET(fd, &poller->rfds);
  674. }
  675. if (mode & POLLOUT) {
  676. PHP_SAFE_FD_SET(fd, &poller->wfds);
  677. }
  678. if (fd > poller->max_fd) {
  679. poller->max_fd = fd;
  680. }
  681. } /* }}} */
  682. static void php_cli_server_poller_remove(php_cli_server_poller *poller, int mode, php_socket_t fd) /* {{{ */
  683. {
  684. if (mode & POLLIN) {
  685. PHP_SAFE_FD_CLR(fd, &poller->rfds);
  686. }
  687. if (mode & POLLOUT) {
  688. PHP_SAFE_FD_CLR(fd, &poller->wfds);
  689. }
  690. #ifndef PHP_WIN32
  691. if (fd == poller->max_fd) {
  692. while (fd > 0) {
  693. fd--;
  694. if (PHP_SAFE_FD_ISSET(fd, &poller->rfds) || PHP_SAFE_FD_ISSET(fd, &poller->wfds)) {
  695. break;
  696. }
  697. }
  698. poller->max_fd = fd;
  699. }
  700. #endif
  701. } /* }}} */
  702. static int php_cli_server_poller_poll(php_cli_server_poller *poller, struct timeval *tv) /* {{{ */
  703. {
  704. memmove(&poller->active.rfds, &poller->rfds, sizeof(poller->rfds));
  705. memmove(&poller->active.wfds, &poller->wfds, sizeof(poller->wfds));
  706. return php_select(poller->max_fd + 1, &poller->active.rfds, &poller->active.wfds, NULL, tv);
  707. } /* }}} */
  708. static int php_cli_server_poller_iter_on_active(php_cli_server_poller *poller, void *opaque, int(*callback)(void *, php_socket_t fd, int events)) /* {{{ */
  709. {
  710. int retval = SUCCESS;
  711. #ifdef PHP_WIN32
  712. struct socket_entry {
  713. SOCKET fd;
  714. int events;
  715. } entries[FD_SETSIZE * 2];
  716. size_t i;
  717. struct socket_entry *n = entries, *m;
  718. for (i = 0; i < poller->active.rfds.fd_count; i++) {
  719. n->events = POLLIN;
  720. n->fd = poller->active.rfds.fd_array[i];
  721. n++;
  722. }
  723. m = n;
  724. for (i = 0; i < poller->active.wfds.fd_count; i++) {
  725. struct socket_entry *e;
  726. SOCKET fd = poller->active.wfds.fd_array[i];
  727. for (e = entries; e < m; e++) {
  728. if (e->fd == fd) {
  729. e->events |= POLLOUT;
  730. }
  731. }
  732. if (e == m) {
  733. assert(n < entries + FD_SETSIZE * 2);
  734. n->events = POLLOUT;
  735. n->fd = fd;
  736. n++;
  737. }
  738. }
  739. {
  740. struct socket_entry *e = entries;
  741. for (; e < n; e++) {
  742. if (SUCCESS != callback(opaque, e->fd, e->events)) {
  743. retval = FAILURE;
  744. }
  745. }
  746. }
  747. #else
  748. php_socket_t fd;
  749. const php_socket_t max_fd = poller->max_fd;
  750. for (fd=0 ; fd<=max_fd ; fd++) {
  751. if (PHP_SAFE_FD_ISSET(fd, &poller->active.rfds)) {
  752. if (SUCCESS != callback(opaque, fd, POLLIN)) {
  753. retval = FAILURE;
  754. }
  755. }
  756. if (PHP_SAFE_FD_ISSET(fd, &poller->active.wfds)) {
  757. if (SUCCESS != callback(opaque, fd, POLLOUT)) {
  758. retval = FAILURE;
  759. }
  760. }
  761. }
  762. #endif
  763. return retval;
  764. } /* }}} */
  765. static size_t php_cli_server_chunk_size(const php_cli_server_chunk *chunk) /* {{{ */
  766. {
  767. switch (chunk->type) {
  768. case PHP_CLI_SERVER_CHUNK_HEAP:
  769. return chunk->data.heap.len;
  770. case PHP_CLI_SERVER_CHUNK_IMMORTAL:
  771. return chunk->data.immortal.len;
  772. }
  773. return 0;
  774. } /* }}} */
  775. static void php_cli_server_chunk_dtor(php_cli_server_chunk *chunk) /* {{{ */
  776. {
  777. switch (chunk->type) {
  778. case PHP_CLI_SERVER_CHUNK_HEAP:
  779. if (chunk->data.heap.block != chunk) {
  780. pefree(chunk->data.heap.block, 1);
  781. }
  782. break;
  783. case PHP_CLI_SERVER_CHUNK_IMMORTAL:
  784. break;
  785. }
  786. } /* }}} */
  787. static void php_cli_server_buffer_dtor(php_cli_server_buffer *buffer) /* {{{ */
  788. {
  789. php_cli_server_chunk *chunk, *next;
  790. for (chunk = buffer->first; chunk; chunk = next) {
  791. next = chunk->next;
  792. php_cli_server_chunk_dtor(chunk);
  793. pefree(chunk, 1);
  794. }
  795. } /* }}} */
  796. static void php_cli_server_buffer_ctor(php_cli_server_buffer *buffer) /* {{{ */
  797. {
  798. buffer->first = NULL;
  799. buffer->last = NULL;
  800. } /* }}} */
  801. static void php_cli_server_buffer_append(php_cli_server_buffer *buffer, php_cli_server_chunk *chunk) /* {{{ */
  802. {
  803. php_cli_server_chunk *last;
  804. for (last = chunk; last->next; last = last->next);
  805. if (!buffer->last) {
  806. buffer->first = chunk;
  807. } else {
  808. buffer->last->next = chunk;
  809. }
  810. buffer->last = last;
  811. } /* }}} */
  812. static void php_cli_server_buffer_prepend(php_cli_server_buffer *buffer, php_cli_server_chunk *chunk) /* {{{ */
  813. {
  814. php_cli_server_chunk *last;
  815. for (last = chunk; last->next; last = last->next);
  816. last->next = buffer->first;
  817. if (!buffer->last) {
  818. buffer->last = last;
  819. }
  820. buffer->first = chunk;
  821. } /* }}} */
  822. static size_t php_cli_server_buffer_size(const php_cli_server_buffer *buffer) /* {{{ */
  823. {
  824. php_cli_server_chunk *chunk;
  825. size_t retval = 0;
  826. for (chunk = buffer->first; chunk; chunk = chunk->next) {
  827. retval += php_cli_server_chunk_size(chunk);
  828. }
  829. return retval;
  830. } /* }}} */
  831. static php_cli_server_chunk *php_cli_server_chunk_immortal_new(const char *buf, size_t len) /* {{{ */
  832. {
  833. php_cli_server_chunk *chunk = pemalloc(sizeof(php_cli_server_chunk), 1);
  834. chunk->type = PHP_CLI_SERVER_CHUNK_IMMORTAL;
  835. chunk->next = NULL;
  836. chunk->data.immortal.p = buf;
  837. chunk->data.immortal.len = len;
  838. return chunk;
  839. } /* }}} */
  840. static php_cli_server_chunk *php_cli_server_chunk_heap_new(void *block, char *buf, size_t len) /* {{{ */
  841. {
  842. php_cli_server_chunk *chunk = pemalloc(sizeof(php_cli_server_chunk), 1);
  843. chunk->type = PHP_CLI_SERVER_CHUNK_HEAP;
  844. chunk->next = NULL;
  845. chunk->data.heap.block = block;
  846. chunk->data.heap.p = buf;
  847. chunk->data.heap.len = len;
  848. return chunk;
  849. } /* }}} */
  850. static php_cli_server_chunk *php_cli_server_chunk_heap_new_self_contained(size_t len) /* {{{ */
  851. {
  852. php_cli_server_chunk *chunk = pemalloc(sizeof(php_cli_server_chunk) + len, 1);
  853. chunk->type = PHP_CLI_SERVER_CHUNK_HEAP;
  854. chunk->next = NULL;
  855. chunk->data.heap.block = chunk;
  856. chunk->data.heap.p = (char *)(chunk + 1);
  857. chunk->data.heap.len = len;
  858. return chunk;
  859. } /* }}} */
  860. static void php_cli_server_content_sender_dtor(php_cli_server_content_sender *sender) /* {{{ */
  861. {
  862. php_cli_server_buffer_dtor(&sender->buffer);
  863. } /* }}} */
  864. static void php_cli_server_content_sender_ctor(php_cli_server_content_sender *sender) /* {{{ */
  865. {
  866. php_cli_server_buffer_ctor(&sender->buffer);
  867. } /* }}} */
  868. static int php_cli_server_content_sender_send(php_cli_server_content_sender *sender, php_socket_t fd, size_t *nbytes_sent_total) /* {{{ */
  869. {
  870. php_cli_server_chunk *chunk, *next;
  871. size_t _nbytes_sent_total = 0;
  872. for (chunk = sender->buffer.first; chunk; chunk = next) {
  873. #ifdef PHP_WIN32
  874. int nbytes_sent;
  875. #else
  876. ssize_t nbytes_sent;
  877. #endif
  878. next = chunk->next;
  879. switch (chunk->type) {
  880. case PHP_CLI_SERVER_CHUNK_HEAP:
  881. #ifdef PHP_WIN32
  882. nbytes_sent = send(fd, chunk->data.heap.p, (int)chunk->data.heap.len, 0);
  883. #else
  884. nbytes_sent = send(fd, chunk->data.heap.p, chunk->data.heap.len, 0);
  885. #endif
  886. if (nbytes_sent < 0) {
  887. *nbytes_sent_total = _nbytes_sent_total;
  888. return php_socket_errno();
  889. #ifdef PHP_WIN32
  890. } else if (nbytes_sent == chunk->data.heap.len) {
  891. #else
  892. } else if (nbytes_sent == (ssize_t)chunk->data.heap.len) {
  893. #endif
  894. php_cli_server_chunk_dtor(chunk);
  895. pefree(chunk, 1);
  896. sender->buffer.first = next;
  897. if (!next) {
  898. sender->buffer.last = NULL;
  899. }
  900. } else {
  901. chunk->data.heap.p += nbytes_sent;
  902. chunk->data.heap.len -= nbytes_sent;
  903. }
  904. _nbytes_sent_total += nbytes_sent;
  905. break;
  906. case PHP_CLI_SERVER_CHUNK_IMMORTAL:
  907. #ifdef PHP_WIN32
  908. nbytes_sent = send(fd, chunk->data.immortal.p, (int)chunk->data.immortal.len, 0);
  909. #else
  910. nbytes_sent = send(fd, chunk->data.immortal.p, chunk->data.immortal.len, 0);
  911. #endif
  912. if (nbytes_sent < 0) {
  913. *nbytes_sent_total = _nbytes_sent_total;
  914. return php_socket_errno();
  915. #ifdef PHP_WIN32
  916. } else if (nbytes_sent == chunk->data.immortal.len) {
  917. #else
  918. } else if (nbytes_sent == (ssize_t)chunk->data.immortal.len) {
  919. #endif
  920. php_cli_server_chunk_dtor(chunk);
  921. pefree(chunk, 1);
  922. sender->buffer.first = next;
  923. if (!next) {
  924. sender->buffer.last = NULL;
  925. }
  926. } else {
  927. chunk->data.immortal.p += nbytes_sent;
  928. chunk->data.immortal.len -= nbytes_sent;
  929. }
  930. _nbytes_sent_total += nbytes_sent;
  931. break;
  932. }
  933. }
  934. *nbytes_sent_total = _nbytes_sent_total;
  935. return 0;
  936. } /* }}} */
  937. static int php_cli_server_content_sender_pull(php_cli_server_content_sender *sender, int fd, size_t *nbytes_read) /* {{{ */
  938. {
  939. #ifdef PHP_WIN32
  940. int _nbytes_read;
  941. #else
  942. ssize_t _nbytes_read;
  943. #endif
  944. php_cli_server_chunk *chunk = php_cli_server_chunk_heap_new_self_contained(131072);
  945. #ifdef PHP_WIN32
  946. _nbytes_read = read(fd, chunk->data.heap.p, (unsigned int)chunk->data.heap.len);
  947. #else
  948. _nbytes_read = read(fd, chunk->data.heap.p, chunk->data.heap.len);
  949. #endif
  950. if (_nbytes_read < 0) {
  951. if (php_cli_server_log_level >= PHP_CLI_SERVER_LOG_ERROR) {
  952. char *errstr = get_last_error();
  953. php_cli_server_logf(PHP_CLI_SERVER_LOG_ERROR, "%s", errstr);
  954. pefree(errstr, 1);
  955. }
  956. php_cli_server_chunk_dtor(chunk);
  957. pefree(chunk, 1);
  958. return 1;
  959. }
  960. chunk->data.heap.len = _nbytes_read;
  961. php_cli_server_buffer_append(&sender->buffer, chunk);
  962. *nbytes_read = _nbytes_read;
  963. return 0;
  964. } /* }}} */
  965. #if HAVE_UNISTD_H
  966. static int php_cli_is_output_tty(void) /* {{{ */
  967. {
  968. if (php_cli_output_is_tty == OUTPUT_NOT_CHECKED) {
  969. php_cli_output_is_tty = isatty(STDOUT_FILENO);
  970. }
  971. return php_cli_output_is_tty;
  972. } /* }}} */
  973. #elif defined(PHP_WIN32)
  974. static int php_cli_is_output_tty() /* {{{ */
  975. {
  976. if (php_cli_output_is_tty == OUTPUT_NOT_CHECKED) {
  977. php_cli_output_is_tty = php_win32_console_fileno_is_console(STDOUT_FILENO) && php_win32_console_fileno_has_vt100(STDOUT_FILENO);
  978. }
  979. return php_cli_output_is_tty;
  980. } /* }}} */
  981. #endif
  982. static void php_cli_server_log_response(php_cli_server_client *client, int status, const char *message) /* {{{ */
  983. {
  984. int color = 0, effective_status = status;
  985. char *basic_buf, *message_buf = "", *error_buf = "";
  986. bool append_error_message = 0;
  987. if (PG(last_error_message)) {
  988. if (PG(last_error_type) & E_FATAL_ERRORS) {
  989. if (status == 200) {
  990. /* the status code isn't changed by a fatal error, so fake it */
  991. effective_status = 500;
  992. }
  993. append_error_message = 1;
  994. }
  995. }
  996. #if HAVE_UNISTD_H || defined(PHP_WIN32)
  997. if (CLI_SERVER_G(color) && php_cli_is_output_tty() == OUTPUT_IS_TTY) {
  998. if (effective_status >= 500) {
  999. /* server error: red */
  1000. color = 1;
  1001. } else if (effective_status >= 400) {
  1002. /* client error: yellow */
  1003. color = 3;
  1004. } else if (effective_status >= 200) {
  1005. /* success: green */
  1006. color = 2;
  1007. }
  1008. }
  1009. #endif
  1010. /* basic */
  1011. spprintf(&basic_buf, 0, "%s [%d]: %s %s", client->addr_str, status, php_http_method_str(client->request.request_method), client->request.request_uri);
  1012. if (!basic_buf) {
  1013. return;
  1014. }
  1015. /* message */
  1016. if (message) {
  1017. spprintf(&message_buf, 0, " - %s", message);
  1018. if (!message_buf) {
  1019. efree(basic_buf);
  1020. return;
  1021. }
  1022. }
  1023. /* error */
  1024. if (append_error_message) {
  1025. spprintf(&error_buf, 0, " - %s in %s on line %d",
  1026. ZSTR_VAL(PG(last_error_message)), ZSTR_VAL(PG(last_error_file)), PG(last_error_lineno));
  1027. if (!error_buf) {
  1028. efree(basic_buf);
  1029. if (message) {
  1030. efree(message_buf);
  1031. }
  1032. return;
  1033. }
  1034. }
  1035. if (color) {
  1036. php_cli_server_logf(PHP_CLI_SERVER_LOG_MESSAGE, "\x1b[3%dm%s%s%s\x1b[0m", color, basic_buf, message_buf, error_buf);
  1037. } else {
  1038. php_cli_server_logf(PHP_CLI_SERVER_LOG_MESSAGE, "%s%s%s", basic_buf, message_buf, error_buf);
  1039. }
  1040. efree(basic_buf);
  1041. if (message) {
  1042. efree(message_buf);
  1043. }
  1044. if (append_error_message) {
  1045. efree(error_buf);
  1046. }
  1047. } /* }}} */
  1048. static void php_cli_server_logf(int type, const char *format, ...) /* {{{ */
  1049. {
  1050. char *buf = NULL;
  1051. va_list ap;
  1052. if (php_cli_server_log_level < type) {
  1053. return;
  1054. }
  1055. va_start(ap, format);
  1056. vspprintf(&buf, 0, format, ap);
  1057. va_end(ap);
  1058. if (!buf) {
  1059. return;
  1060. }
  1061. sapi_cli_server_log_write(type, buf);
  1062. efree(buf);
  1063. } /* }}} */
  1064. static php_socket_t php_network_listen_socket(const char *host, int *port, int socktype, int *af, socklen_t *socklen, zend_string **errstr) /* {{{ */
  1065. {
  1066. php_socket_t retval = SOCK_ERR;
  1067. int err = 0;
  1068. struct sockaddr *sa = NULL, **p, **sal;
  1069. int num_addrs = php_network_getaddresses(host, socktype, &sal, errstr);
  1070. if (num_addrs == 0) {
  1071. return -1;
  1072. }
  1073. for (p = sal; *p; p++) {
  1074. if (sa) {
  1075. pefree(sa, 1);
  1076. sa = NULL;
  1077. }
  1078. retval = socket((*p)->sa_family, socktype, 0);
  1079. if (retval == SOCK_ERR) {
  1080. continue;
  1081. }
  1082. switch ((*p)->sa_family) {
  1083. #if HAVE_GETADDRINFO && HAVE_IPV6
  1084. case AF_INET6:
  1085. sa = pemalloc(sizeof(struct sockaddr_in6), 1);
  1086. *(struct sockaddr_in6 *)sa = *(struct sockaddr_in6 *)*p;
  1087. ((struct sockaddr_in6 *)sa)->sin6_port = htons(*port);
  1088. *socklen = sizeof(struct sockaddr_in6);
  1089. break;
  1090. #endif
  1091. case AF_INET:
  1092. sa = pemalloc(sizeof(struct sockaddr_in), 1);
  1093. *(struct sockaddr_in *)sa = *(struct sockaddr_in *)*p;
  1094. ((struct sockaddr_in *)sa)->sin_port = htons(*port);
  1095. *socklen = sizeof(struct sockaddr_in);
  1096. break;
  1097. default:
  1098. /* Unknown family */
  1099. *socklen = 0;
  1100. closesocket(retval);
  1101. continue;
  1102. }
  1103. #ifdef SO_REUSEADDR
  1104. {
  1105. int val = 1;
  1106. setsockopt(retval, SOL_SOCKET, SO_REUSEADDR, (char*)&val, sizeof(val));
  1107. }
  1108. #endif
  1109. if (bind(retval, sa, *socklen) == SOCK_CONN_ERR) {
  1110. err = php_socket_errno();
  1111. if (err == SOCK_EINVAL || err == SOCK_EADDRINUSE) {
  1112. goto out;
  1113. }
  1114. closesocket(retval);
  1115. retval = SOCK_ERR;
  1116. continue;
  1117. }
  1118. err = 0;
  1119. *af = sa->sa_family;
  1120. if (*port == 0) {
  1121. if (getsockname(retval, sa, socklen)) {
  1122. err = php_socket_errno();
  1123. goto out;
  1124. }
  1125. switch (sa->sa_family) {
  1126. #if HAVE_GETADDRINFO && HAVE_IPV6
  1127. case AF_INET6:
  1128. *port = ntohs(((struct sockaddr_in6 *)sa)->sin6_port);
  1129. break;
  1130. #endif
  1131. case AF_INET:
  1132. *port = ntohs(((struct sockaddr_in *)sa)->sin_port);
  1133. break;
  1134. }
  1135. }
  1136. break;
  1137. }
  1138. if (retval == SOCK_ERR) {
  1139. goto out;
  1140. }
  1141. if (listen(retval, SOMAXCONN)) {
  1142. err = php_socket_errno();
  1143. goto out;
  1144. }
  1145. out:
  1146. if (sa) {
  1147. pefree(sa, 1);
  1148. }
  1149. if (sal) {
  1150. php_network_freeaddresses(sal);
  1151. }
  1152. if (err) {
  1153. if (ZEND_VALID_SOCKET(retval)) {
  1154. closesocket(retval);
  1155. }
  1156. if (errstr) {
  1157. *errstr = php_socket_error_str(err);
  1158. }
  1159. return SOCK_ERR;
  1160. }
  1161. return retval;
  1162. } /* }}} */
  1163. static int php_cli_server_request_ctor(php_cli_server_request *req) /* {{{ */
  1164. {
  1165. req->protocol_version = 0;
  1166. req->request_uri = NULL;
  1167. req->request_uri_len = 0;
  1168. req->vpath = NULL;
  1169. req->vpath_len = 0;
  1170. req->path_translated = NULL;
  1171. req->path_translated_len = 0;
  1172. req->path_info = NULL;
  1173. req->path_info_len = 0;
  1174. req->query_string = NULL;
  1175. req->query_string_len = 0;
  1176. zend_hash_init(&req->headers, 0, NULL, char_ptr_dtor_p, 1);
  1177. zend_hash_init(&req->headers_original_case, 0, NULL, NULL, 1);
  1178. req->content = NULL;
  1179. req->content_len = 0;
  1180. req->ext = NULL;
  1181. req->ext_len = 0;
  1182. return SUCCESS;
  1183. } /* }}} */
  1184. static void php_cli_server_request_dtor(php_cli_server_request *req) /* {{{ */
  1185. {
  1186. if (req->request_uri) {
  1187. pefree(req->request_uri, 1);
  1188. }
  1189. if (req->vpath) {
  1190. pefree(req->vpath, 1);
  1191. }
  1192. if (req->path_translated) {
  1193. pefree(req->path_translated, 1);
  1194. }
  1195. if (req->path_info) {
  1196. pefree(req->path_info, 1);
  1197. }
  1198. if (req->query_string) {
  1199. pefree(req->query_string, 1);
  1200. }
  1201. zend_hash_destroy(&req->headers);
  1202. zend_hash_destroy(&req->headers_original_case);
  1203. if (req->content) {
  1204. pefree(req->content, 1);
  1205. }
  1206. } /* }}} */
  1207. static void php_cli_server_request_translate_vpath(php_cli_server_request *request, const char *document_root, size_t document_root_len) /* {{{ */
  1208. {
  1209. zend_stat_t sb;
  1210. static const char *index_files[] = { "index.php", "index.html", NULL };
  1211. char *buf = safe_pemalloc(1, request->vpath_len, 1 + document_root_len + 1 + sizeof("index.html"), 1);
  1212. char *p = buf, *prev_path = NULL, *q, *vpath;
  1213. size_t prev_path_len = 0;
  1214. int is_static_file = 0;
  1215. memmove(p, document_root, document_root_len);
  1216. p += document_root_len;
  1217. vpath = p;
  1218. if (request->vpath_len > 0 && request->vpath[0] != '/') {
  1219. *p++ = DEFAULT_SLASH;
  1220. }
  1221. q = request->vpath + request->vpath_len;
  1222. while (q > request->vpath) {
  1223. if (*q-- == '.') {
  1224. is_static_file = 1;
  1225. break;
  1226. }
  1227. }
  1228. memmove(p, request->vpath, request->vpath_len);
  1229. #ifdef PHP_WIN32
  1230. q = p + request->vpath_len;
  1231. do {
  1232. if (*q == '/') {
  1233. *q = '\\';
  1234. }
  1235. } while (q-- > p);
  1236. #endif
  1237. p += request->vpath_len;
  1238. *p = '\0';
  1239. q = p;
  1240. while (q > buf) {
  1241. if (!php_sys_stat(buf, &sb)) {
  1242. if (sb.st_mode & S_IFDIR) {
  1243. const char **file = index_files;
  1244. if (q[-1] != DEFAULT_SLASH) {
  1245. *q++ = DEFAULT_SLASH;
  1246. }
  1247. while (*file) {
  1248. size_t l = strlen(*file);
  1249. memmove(q, *file, l + 1);
  1250. if (!php_sys_stat(buf, &sb) && (sb.st_mode & S_IFREG)) {
  1251. q += l;
  1252. break;
  1253. }
  1254. file++;
  1255. }
  1256. if (!*file || is_static_file) {
  1257. if (prev_path) {
  1258. pefree(prev_path, 1);
  1259. }
  1260. pefree(buf, 1);
  1261. return;
  1262. }
  1263. }
  1264. break; /* regular file */
  1265. }
  1266. if (prev_path) {
  1267. pefree(prev_path, 1);
  1268. *q = DEFAULT_SLASH;
  1269. }
  1270. while (q > buf && *(--q) != DEFAULT_SLASH);
  1271. prev_path_len = p - q;
  1272. prev_path = pestrndup(q, prev_path_len, 1);
  1273. *q = '\0';
  1274. }
  1275. if (prev_path) {
  1276. request->path_info_len = prev_path_len;
  1277. #ifdef PHP_WIN32
  1278. while (prev_path_len--) {
  1279. if (prev_path[prev_path_len] == '\\') {
  1280. prev_path[prev_path_len] = '/';
  1281. }
  1282. }
  1283. #endif
  1284. request->path_info = prev_path;
  1285. pefree(request->vpath, 1);
  1286. request->vpath = pestrndup(vpath, q - vpath, 1);
  1287. request->vpath_len = q - vpath;
  1288. request->path_translated = buf;
  1289. request->path_translated_len = q - buf;
  1290. } else {
  1291. pefree(request->vpath, 1);
  1292. request->vpath = pestrndup(vpath, q - vpath, 1);
  1293. request->vpath_len = q - vpath;
  1294. request->path_translated = buf;
  1295. request->path_translated_len = q - buf;
  1296. }
  1297. #ifdef PHP_WIN32
  1298. {
  1299. uint32_t i = 0;
  1300. for (;i<request->vpath_len;i++) {
  1301. if (request->vpath[i] == '\\') {
  1302. request->vpath[i] = '/';
  1303. }
  1304. }
  1305. }
  1306. #endif
  1307. request->sb = sb;
  1308. } /* }}} */
  1309. static void normalize_vpath(char **retval, size_t *retval_len, const char *vpath, size_t vpath_len, int persistent) /* {{{ */
  1310. {
  1311. char *decoded_vpath = NULL;
  1312. char *decoded_vpath_end;
  1313. char *p;
  1314. *retval = NULL;
  1315. *retval_len = 0;
  1316. decoded_vpath = pestrndup(vpath, vpath_len, persistent);
  1317. if (!decoded_vpath) {
  1318. return;
  1319. }
  1320. decoded_vpath_end = decoded_vpath + php_raw_url_decode(decoded_vpath, (int)vpath_len);
  1321. #ifdef PHP_WIN32
  1322. {
  1323. char *p = decoded_vpath;
  1324. do {
  1325. if (*p == '\\') {
  1326. *p = '/';
  1327. }
  1328. } while (*p++);
  1329. }
  1330. #endif
  1331. p = decoded_vpath;
  1332. if (p < decoded_vpath_end && *p == '/') {
  1333. char *n = p;
  1334. while (n < decoded_vpath_end && *n == '/') n++;
  1335. memmove(++p, n, decoded_vpath_end - n);
  1336. decoded_vpath_end -= n - p;
  1337. }
  1338. while (p < decoded_vpath_end) {
  1339. char *n = p;
  1340. while (n < decoded_vpath_end && *n != '/') n++;
  1341. if (n - p == 2 && p[0] == '.' && p[1] == '.') {
  1342. if (p > decoded_vpath) {
  1343. --p;
  1344. for (;;) {
  1345. if (p == decoded_vpath) {
  1346. if (*p == '/') {
  1347. p++;
  1348. }
  1349. break;
  1350. }
  1351. if (*(--p) == '/') {
  1352. p++;
  1353. break;
  1354. }
  1355. }
  1356. }
  1357. while (n < decoded_vpath_end && *n == '/') n++;
  1358. memmove(p, n, decoded_vpath_end - n);
  1359. decoded_vpath_end -= n - p;
  1360. } else if (n - p == 1 && p[0] == '.') {
  1361. while (n < decoded_vpath_end && *n == '/') n++;
  1362. memmove(p, n, decoded_vpath_end - n);
  1363. decoded_vpath_end -= n - p;
  1364. } else {
  1365. if (n < decoded_vpath_end) {
  1366. char *nn = n;
  1367. while (nn < decoded_vpath_end && *nn == '/') nn++;
  1368. p = n + 1;
  1369. memmove(p, nn, decoded_vpath_end - nn);
  1370. decoded_vpath_end -= nn - p;
  1371. } else {
  1372. p = n;
  1373. }
  1374. }
  1375. }
  1376. *decoded_vpath_end = '\0';
  1377. *retval = decoded_vpath;
  1378. *retval_len = decoded_vpath_end - decoded_vpath;
  1379. } /* }}} */
  1380. /* {{{ php_cli_server_client_read_request */
  1381. static int php_cli_server_client_read_request_on_message_begin(php_http_parser *parser)
  1382. {
  1383. return 0;
  1384. }
  1385. static int php_cli_server_client_read_request_on_path(php_http_parser *parser, const char *at, size_t length)
  1386. {
  1387. php_cli_server_client *client = parser->data;
  1388. {
  1389. char *vpath;
  1390. size_t vpath_len;
  1391. if (UNEXPECTED(client->request.vpath != NULL)) {
  1392. return 1;
  1393. }
  1394. normalize_vpath(&vpath, &vpath_len, at, length, 1);
  1395. client->request.vpath = vpath;
  1396. client->request.vpath_len = vpath_len;
  1397. }
  1398. return 0;
  1399. }
  1400. static int php_cli_server_client_read_request_on_query_string(php_http_parser *parser, const char *at, size_t length)
  1401. {
  1402. php_cli_server_client *client = parser->data;
  1403. if (EXPECTED(client->request.query_string == NULL)) {
  1404. client->request.query_string = pestrndup(at, length, 1);
  1405. client->request.query_string_len = length;
  1406. } else {
  1407. ZEND_ASSERT(length <= PHP_HTTP_MAX_HEADER_SIZE && PHP_HTTP_MAX_HEADER_SIZE - length >= client->request.query_string_len);
  1408. client->request.query_string = perealloc(client->request.query_string, client->request.query_string_len + length + 1, 1);
  1409. memcpy(client->request.query_string + client->request.query_string_len, at, length);
  1410. client->request.query_string_len += length;
  1411. client->request.query_string[client->request.query_string_len] = '\0';
  1412. }
  1413. return 0;
  1414. }
  1415. static int php_cli_server_client_read_request_on_url(php_http_parser *parser, const char *at, size_t length)
  1416. {
  1417. php_cli_server_client *client = parser->data;
  1418. if (EXPECTED(client->request.request_uri == NULL)) {
  1419. client->request.request_method = parser->method;
  1420. client->request.request_uri = pestrndup(at, length, 1);
  1421. client->request.request_uri_len = length;
  1422. } else {
  1423. ZEND_ASSERT(client->request.request_method == parser->method);
  1424. ZEND_ASSERT(length <= PHP_HTTP_MAX_HEADER_SIZE && PHP_HTTP_MAX_HEADER_SIZE - length >= client->request.query_string_len);
  1425. client->request.request_uri = perealloc(client->request.request_uri, client->request.request_uri_len + length + 1, 1);
  1426. memcpy(client->request.request_uri + client->request.request_uri_len, at, length);
  1427. client->request.request_uri_len += length;
  1428. client->request.request_uri[client->request.request_uri_len] = '\0';
  1429. }
  1430. return 0;
  1431. }
  1432. static int php_cli_server_client_read_request_on_fragment(php_http_parser *parser, const char *at, size_t length)
  1433. {
  1434. return 0;
  1435. }
  1436. static void php_cli_server_client_save_header(php_cli_server_client *client)
  1437. {
  1438. /* strip off the colon */
  1439. zend_string *orig_header_name = zend_string_init(client->current_header_name, client->current_header_name_len, 1);
  1440. zend_string *lc_header_name = zend_string_alloc(client->current_header_name_len, 1);
  1441. zend_str_tolower_copy(ZSTR_VAL(lc_header_name), client->current_header_name, client->current_header_name_len);
  1442. GC_MAKE_PERSISTENT_LOCAL(orig_header_name);
  1443. GC_MAKE_PERSISTENT_LOCAL(lc_header_name);
  1444. zend_hash_add_ptr(&client->request.headers, lc_header_name, client->current_header_value);
  1445. zend_hash_add_ptr(&client->request.headers_original_case, orig_header_name, client->current_header_value);
  1446. zend_string_release_ex(lc_header_name, 1);
  1447. zend_string_release_ex(orig_header_name, 1);
  1448. if (client->current_header_name_allocated) {
  1449. pefree(client->current_header_name, 1);
  1450. client->current_header_name_allocated = 0;
  1451. }
  1452. client->current_header_name = NULL;
  1453. client->current_header_name_len = 0;
  1454. client->current_header_value = NULL;
  1455. client->current_header_value_len = 0;
  1456. }
  1457. static int php_cli_server_client_read_request_on_header_field(php_http_parser *parser, const char *at, size_t length)
  1458. {
  1459. php_cli_server_client *client = parser->data;
  1460. switch (client->last_header_element) {
  1461. case HEADER_VALUE:
  1462. php_cli_server_client_save_header(client);
  1463. ZEND_FALLTHROUGH;
  1464. case HEADER_NONE:
  1465. client->current_header_name = (char *)at;
  1466. client->current_header_name_len = length;
  1467. break;
  1468. case HEADER_FIELD:
  1469. if (client->current_header_name_allocated) {
  1470. size_t new_length = client->current_header_name_len + length;
  1471. client->current_header_name = perealloc(client->current_header_name, new_length + 1, 1);
  1472. memcpy(client->current_header_name + client->current_header_name_len, at, length);
  1473. client->current_header_name[new_length] = '\0';
  1474. client->current_header_name_len = new_length;
  1475. } else {
  1476. size_t new_length = client->current_header_name_len + length;
  1477. char* field = pemalloc(new_length + 1, 1);
  1478. memcpy(field, client->current_header_name, client->current_header_name_len);
  1479. memcpy(field + client->current_header_name_len, at, length);
  1480. field[new_length] = '\0';
  1481. client->current_header_name = field;
  1482. client->current_header_name_len = new_length;
  1483. client->current_header_name_allocated = 1;
  1484. }
  1485. break;
  1486. }
  1487. client->last_header_element = HEADER_FIELD;
  1488. return 0;
  1489. }
  1490. static int php_cli_server_client_read_request_on_header_value(php_http_parser *parser, const char *at, size_t length)
  1491. {
  1492. php_cli_server_client *client = parser->data;
  1493. switch (client->last_header_element) {
  1494. case HEADER_FIELD:
  1495. client->current_header_value = pestrndup(at, length, 1);
  1496. client->current_header_value_len = length;
  1497. break;
  1498. case HEADER_VALUE:
  1499. {
  1500. size_t new_length = client->current_header_value_len + length;
  1501. client->current_header_value = perealloc(client->current_header_value, new_length + 1, 1);
  1502. memcpy(client->current_header_value + client->current_header_value_len, at, length);
  1503. client->current_header_value[new_length] = '\0';
  1504. client->current_header_value_len = new_length;
  1505. }
  1506. break;
  1507. case HEADER_NONE:
  1508. // can't happen
  1509. assert(0);
  1510. break;
  1511. }
  1512. client->last_header_element = HEADER_VALUE;
  1513. return 0;
  1514. }
  1515. static int php_cli_server_client_read_request_on_headers_complete(php_http_parser *parser)
  1516. {
  1517. php_cli_server_client *client = parser->data;
  1518. switch (client->last_header_element) {
  1519. case HEADER_NONE:
  1520. break;
  1521. case HEADER_FIELD:
  1522. client->current_header_value = pemalloc(1, 1);
  1523. *client->current_header_value = '\0';
  1524. client->current_header_value_len = 0;
  1525. ZEND_FALLTHROUGH;
  1526. case HEADER_VALUE:
  1527. php_cli_server_client_save_header(client);
  1528. break;
  1529. }
  1530. client->last_header_element = HEADER_NONE;
  1531. return 0;
  1532. }
  1533. static int php_cli_server_client_read_request_on_body(php_http_parser *parser, const char *at, size_t length)
  1534. {
  1535. php_cli_server_client *client = parser->data;
  1536. if (!client->request.content) {
  1537. client->request.content = pemalloc(parser->content_length, 1);
  1538. client->request.content_len = 0;
  1539. }
  1540. client->request.content = perealloc(client->request.content, client->request.content_len + length, 1);
  1541. memmove(client->request.content + client->request.content_len, at, length);
  1542. client->request.content_len += length;
  1543. return 0;
  1544. }
  1545. static int php_cli_server_client_read_request_on_message_complete(php_http_parser *parser)
  1546. {
  1547. php_cli_server_client *client = parser->data;
  1548. client->request.protocol_version = parser->http_major * 100 + parser->http_minor;
  1549. php_cli_server_request_translate_vpath(&client->request, client->server->document_root, client->server->document_root_len);
  1550. {
  1551. const char *vpath = client->request.vpath, *end = vpath + client->request.vpath_len, *p = end;
  1552. client->request.ext = end;
  1553. client->request.ext_len = 0;
  1554. while (p > vpath) {
  1555. --p;
  1556. if (*p == '.') {
  1557. ++p;
  1558. client->request.ext = p;
  1559. client->request.ext_len = end - p;
  1560. break;
  1561. }
  1562. }
  1563. }
  1564. client->request_read = 1;
  1565. return 0;
  1566. }
  1567. static int php_cli_server_client_read_request(php_cli_server_client *client, char **errstr)
  1568. {
  1569. char buf[16384];
  1570. static const php_http_parser_settings settings = {
  1571. php_cli_server_client_read_request_on_message_begin,
  1572. php_cli_server_client_read_request_on_path,
  1573. php_cli_server_client_read_request_on_query_string,
  1574. php_cli_server_client_read_request_on_url,
  1575. php_cli_server_client_read_request_on_fragment,
  1576. php_cli_server_client_read_request_on_header_field,
  1577. php_cli_server_client_read_request_on_header_value,
  1578. php_cli_server_client_read_request_on_headers_complete,
  1579. php_cli_server_client_read_request_on_body,
  1580. php_cli_server_client_read_request_on_message_complete
  1581. };
  1582. size_t nbytes_consumed;
  1583. int nbytes_read;
  1584. if (client->request_read) {
  1585. return 1;
  1586. }
  1587. nbytes_read = recv(client->sock, buf, sizeof(buf) - 1, 0);
  1588. if (nbytes_read < 0) {
  1589. int err = php_socket_errno();
  1590. if (err == SOCK_EAGAIN) {
  1591. return 0;
  1592. }
  1593. if (php_cli_server_log_level >= PHP_CLI_SERVER_LOG_ERROR) {
  1594. *errstr = php_socket_strerror(err, NULL, 0);
  1595. }
  1596. return -1;
  1597. } else if (nbytes_read == 0) {
  1598. if (php_cli_server_log_level >= PHP_CLI_SERVER_LOG_ERROR) {
  1599. *errstr = estrdup(php_cli_server_request_error_unexpected_eof);
  1600. }
  1601. return -1;
  1602. }
  1603. client->parser.data = client;
  1604. nbytes_consumed = php_http_parser_execute(&client->parser, &settings, buf, nbytes_read);
  1605. if (nbytes_consumed != (size_t)nbytes_read) {
  1606. if (php_cli_server_log_level >= PHP_CLI_SERVER_LOG_ERROR) {
  1607. if ((buf[0] & 0x80) /* SSLv2 */ || buf[0] == 0x16 /* SSLv3/TLSv1 */) {
  1608. *errstr = estrdup("Unsupported SSL request");
  1609. } else {
  1610. *errstr = estrdup("Malformed HTTP request");
  1611. }
  1612. }
  1613. return -1;
  1614. }
  1615. if (client->current_header_name) {
  1616. char *header_name = safe_pemalloc(client->current_header_name_len, 1, 1, 1);
  1617. memmove(header_name, client->current_header_name, client->current_header_name_len);
  1618. client->current_header_name = header_name;
  1619. client->current_header_name_allocated = 1;
  1620. }
  1621. return client->request_read ? 1: 0;
  1622. }
  1623. /* }}} */
  1624. static size_t php_cli_server_client_send_through(php_cli_server_client *client, const char *str, size_t str_len) /* {{{ */
  1625. {
  1626. struct timeval tv = { 10, 0 };
  1627. #ifdef PHP_WIN32
  1628. int nbytes_left = (int)str_len;
  1629. #else
  1630. ssize_t nbytes_left = (ssize_t)str_len;
  1631. #endif
  1632. do {
  1633. #ifdef PHP_WIN32
  1634. int nbytes_sent;
  1635. #else
  1636. ssize_t nbytes_sent;
  1637. #endif
  1638. nbytes_sent = send(client->sock, str + str_len - nbytes_left, nbytes_left, 0);
  1639. if (nbytes_sent < 0) {
  1640. int err = php_socket_errno();
  1641. if (err == SOCK_EAGAIN) {
  1642. int nfds = php_pollfd_for(client->sock, POLLOUT, &tv);
  1643. if (nfds > 0) {
  1644. continue;
  1645. } else {
  1646. /* error or timeout */
  1647. php_handle_aborted_connection();
  1648. return nbytes_left;
  1649. }
  1650. } else {
  1651. php_handle_aborted_connection();
  1652. return nbytes_left;
  1653. }
  1654. }
  1655. nbytes_left -= nbytes_sent;
  1656. } while (nbytes_left > 0);
  1657. return str_len;
  1658. } /* }}} */
  1659. static void php_cli_server_client_populate_request_info(const php_cli_server_client *client, sapi_request_info *request_info) /* {{{ */
  1660. {
  1661. char *val;
  1662. request_info->request_method = php_http_method_str(client->request.request_method);
  1663. request_info->proto_num = client->request.protocol_version;
  1664. request_info->request_uri = client->request.request_uri;
  1665. request_info->path_translated = client->request.path_translated;
  1666. request_info->query_string = client->request.query_string;
  1667. request_info->content_length = client->request.content_len;
  1668. request_info->auth_user = request_info->auth_password = request_info->auth_digest = NULL;
  1669. if (NULL != (val = zend_hash_str_find_ptr(&client->request.headers, "content-type", sizeof("content-type")-1))) {
  1670. request_info->content_type = val;
  1671. }
  1672. } /* }}} */
  1673. static void destroy_request_info(sapi_request_info *request_info) /* {{{ */
  1674. {
  1675. } /* }}} */
  1676. static int php_cli_server_client_ctor(php_cli_server_client *client, php_cli_server *server, php_socket_t client_sock, struct sockaddr *addr, socklen_t addr_len) /* {{{ */
  1677. {
  1678. client->server = server;
  1679. client->sock = client_sock;
  1680. client->addr = addr;
  1681. client->addr_len = addr_len;
  1682. {
  1683. zend_string *addr_str = 0;
  1684. php_network_populate_name_from_sockaddr(addr, addr_len, &addr_str, NULL, 0);
  1685. client->addr_str = pestrndup(ZSTR_VAL(addr_str), ZSTR_LEN(addr_str), 1);
  1686. client->addr_str_len = ZSTR_LEN(addr_str);
  1687. zend_string_release_ex(addr_str, 0);
  1688. }
  1689. php_http_parser_init(&client->parser, PHP_HTTP_REQUEST);
  1690. client->request_read = 0;
  1691. client->last_header_element = HEADER_NONE;
  1692. client->current_header_name = NULL;
  1693. client->current_header_name_len = 0;
  1694. client->current_header_name_allocated = 0;
  1695. client->current_header_value = NULL;
  1696. client->current_header_value_len = 0;
  1697. client->post_read_offset = 0;
  1698. if (FAILURE == php_cli_server_request_ctor(&client->request)) {
  1699. return FAILURE;
  1700. }
  1701. client->content_sender_initialized = 0;
  1702. client->file_fd = -1;
  1703. return SUCCESS;
  1704. } /* }}} */
  1705. static void php_cli_server_client_dtor(php_cli_server_client *client) /* {{{ */
  1706. {
  1707. php_cli_server_request_dtor(&client->request);
  1708. if (client->file_fd >= 0) {
  1709. close(client->file_fd);
  1710. client->file_fd = -1;
  1711. }
  1712. pefree(client->addr, 1);
  1713. pefree(client->addr_str, 1);
  1714. if (client->content_sender_initialized) {
  1715. php_cli_server_content_sender_dtor(&client->content_sender);
  1716. }
  1717. } /* }}} */
  1718. static void php_cli_server_close_connection(php_cli_server *server, php_cli_server_client *client) /* {{{ */
  1719. {
  1720. php_cli_server_logf(PHP_CLI_SERVER_LOG_MESSAGE, "%s Closing", client->addr_str);
  1721. zend_hash_index_del(&server->clients, client->sock);
  1722. } /* }}} */
  1723. static int php_cli_server_send_error_page(php_cli_server *server, php_cli_server_client *client, int status) /* {{{ */
  1724. {
  1725. zend_string *escaped_request_uri = NULL;
  1726. const char *status_string = get_status_string(status);
  1727. const char *content_template = get_template_string(status);
  1728. char *errstr = get_last_error();
  1729. assert(status_string && content_template);
  1730. php_cli_server_content_sender_ctor(&client->content_sender);
  1731. client->content_sender_initialized = 1;
  1732. escaped_request_uri = php_escape_html_entities_ex((const unsigned char *) client->request.request_uri, client->request.request_uri_len, 0, ENT_QUOTES, NULL, /* double_encode */ 0, /* quiet */ 0);
  1733. {
  1734. static const char prologue_template[] = "<!doctype html><html><head><title>%d %s</title>";
  1735. php_cli_server_chunk *chunk = php_cli_server_chunk_heap_new_self_contained(strlen(prologue_template) + 3 + strlen(status_string) + 1);
  1736. if (!chunk) {
  1737. goto fail;
  1738. }
  1739. snprintf(chunk->data.heap.p, chunk->data.heap.len, prologue_template, status, status_string);
  1740. chunk->data.heap.len = strlen(chunk->data.heap.p);
  1741. php_cli_server_buffer_append(&client->content_sender.buffer, chunk);
  1742. }
  1743. {
  1744. php_cli_server_chunk *chunk = php_cli_server_chunk_immortal_new(php_cli_server_css, sizeof(php_cli_server_css) - 1);
  1745. if (!chunk) {
  1746. goto fail;
  1747. }
  1748. php_cli_server_buffer_append(&client->content_sender.buffer, chunk);
  1749. }
  1750. {
  1751. static const char template[] = "</head><body>";
  1752. php_cli_server_chunk *chunk = php_cli_server_chunk_immortal_new(template, sizeof(template) - 1);
  1753. if (!chunk) {
  1754. goto fail;
  1755. }
  1756. php_cli_server_buffer_append(&client->content_sender.buffer, chunk);
  1757. }
  1758. {
  1759. php_cli_server_chunk *chunk = php_cli_server_chunk_heap_new_self_contained(strlen(content_template) + ZSTR_LEN(escaped_request_uri) + 3 + strlen(status_string) + 1);
  1760. if (!chunk) {
  1761. goto fail;
  1762. }
  1763. snprintf(chunk->data.heap.p, chunk->data.heap.len, content_template, status_string, ZSTR_VAL(escaped_request_uri));
  1764. chunk->data.heap.len = strlen(chunk->data.heap.p);
  1765. php_cli_server_buffer_append(&client->content_sender.buffer, chunk);
  1766. }
  1767. {
  1768. static const char epilogue_template[] = "</body></html>";
  1769. php_cli_server_chunk *chunk = php_cli_server_chunk_immortal_new(epilogue_template, sizeof(epilogue_template) - 1);
  1770. if (!chunk) {
  1771. goto fail;
  1772. }
  1773. php_cli_server_buffer_append(&client->content_sender.buffer, chunk);
  1774. }
  1775. {
  1776. php_cli_server_chunk *chunk;
  1777. smart_str buffer = { 0 };
  1778. append_http_status_line(&buffer, client->request.protocol_version, status, 1);
  1779. if (!buffer.s) {
  1780. /* out of memory */
  1781. goto fail;
  1782. }
  1783. append_essential_headers(&buffer, client, 1);
  1784. smart_str_appends_ex(&buffer, "Content-Type: text/html; charset=UTF-8\r\n", 1);
  1785. smart_str_appends_ex(&buffer, "Content-Length: ", 1);
  1786. smart_str_append_unsigned_ex(&buffer, php_cli_server_buffer_size(&client->content_sender.buffer), 1);
  1787. smart_str_appendl_ex(&buffer, "\r\n", 2, 1);
  1788. smart_str_appendl_ex(&buffer, "\r\n", 2, 1);
  1789. chunk = php_cli_server_chunk_heap_new(buffer.s, ZSTR_VAL(buffer.s), ZSTR_LEN(buffer.s));
  1790. if (!chunk) {
  1791. smart_str_free_ex(&buffer, 1);
  1792. goto fail;
  1793. }
  1794. php_cli_server_buffer_prepend(&client->content_sender.buffer, chunk);
  1795. }
  1796. php_cli_server_log_response(client, status, errstr ? errstr : "?");
  1797. php_cli_server_poller_add(&server->poller, POLLOUT, client->sock);
  1798. if (errstr) {
  1799. pefree(errstr, 1);
  1800. }
  1801. zend_string_free(escaped_request_uri);
  1802. return SUCCESS;
  1803. fail:
  1804. if (errstr) {
  1805. pefree(errstr, 1);
  1806. }
  1807. zend_string_free(escaped_request_uri);
  1808. return FAILURE;
  1809. } /* }}} */
  1810. static int php_cli_server_dispatch_script(php_cli_server *server, php_cli_server_client *client) /* {{{ */
  1811. {
  1812. if (strlen(client->request.path_translated) != client->request.path_translated_len) {
  1813. /* can't handle paths that contain nul bytes */
  1814. return php_cli_server_send_error_page(server, client, 400);
  1815. }
  1816. {
  1817. zend_file_handle zfd;
  1818. zend_stream_init_filename(&zfd, SG(request_info).path_translated);
  1819. zfd.primary_script = 1;
  1820. zend_try {
  1821. php_execute_script(&zfd);
  1822. } zend_end_try();
  1823. zend_destroy_file_handle(&zfd);
  1824. }
  1825. php_cli_server_log_response(client, SG(sapi_headers).http_response_code, NULL);
  1826. return SUCCESS;
  1827. } /* }}} */
  1828. static int php_cli_server_begin_send_static(php_cli_server *server, php_cli_server_client *client) /* {{{ */
  1829. {
  1830. int fd;
  1831. int status = 200;
  1832. if (client->request.path_translated && strlen(client->request.path_translated) != client->request.path_translated_len) {
  1833. /* can't handle paths that contain nul bytes */
  1834. return php_cli_server_send_error_page(server, client, 400);
  1835. }
  1836. #ifdef PHP_WIN32
  1837. /* The win32 namespace will cut off trailing dots and spaces. Since the
  1838. VCWD functionality isn't used here, a sophisticated functionality
  1839. would have to be reimplemented to know ahead there are no files
  1840. with invalid names there. The simplest is just to forbid invalid
  1841. filenames, which is done here. */
  1842. if (client->request.path_translated &&
  1843. ('.' == client->request.path_translated[client->request.path_translated_len-1] ||
  1844. ' ' == client->request.path_translated[client->request.path_translated_len-1])) {
  1845. return php_cli_server_send_error_page(server, client, 500);
  1846. }
  1847. fd = client->request.path_translated ? php_win32_ioutil_open(client->request.path_translated, O_RDONLY): -1;
  1848. #else
  1849. fd = client->request.path_translated ? open(client->request.path_translated, O_RDONLY): -1;
  1850. #endif
  1851. if (fd < 0) {
  1852. return php_cli_server_send_error_page(server, client, 404);
  1853. }
  1854. php_cli_server_content_sender_ctor(&client->content_sender);
  1855. client->content_sender_initialized = 1;
  1856. client->file_fd = fd;
  1857. {
  1858. php_cli_server_chunk *chunk;
  1859. smart_str buffer = { 0 };
  1860. const char *mime_type = get_mime_type(server, client->request.ext, client->request.ext_len);
  1861. append_http_status_line(&buffer, client->request.protocol_version, status, 1);
  1862. if (!buffer.s) {
  1863. /* out of memory */
  1864. php_cli_server_log_response(client, 500, NULL);
  1865. return FAILURE;
  1866. }
  1867. append_essential_headers(&buffer, client, 1);
  1868. if (mime_type) {
  1869. smart_str_appendl_ex(&buffer, "Content-Type: ", sizeof("Content-Type: ") - 1, 1);
  1870. smart_str_appends_ex(&buffer, mime_type, 1);
  1871. if (strncmp(mime_type, "text/", 5) == 0) {
  1872. smart_str_appends_ex(&buffer, "; charset=UTF-8", 1);
  1873. }
  1874. smart_str_appendl_ex(&buffer, "\r\n", 2, 1);
  1875. }
  1876. smart_str_appends_ex(&buffer, "Content-Length: ", 1);
  1877. smart_str_append_unsigned_ex(&buffer, client->request.sb.st_size, 1);
  1878. smart_str_appendl_ex(&buffer, "\r\n", 2, 1);
  1879. smart_str_appendl_ex(&buffer, "\r\n", 2, 1);
  1880. chunk = php_cli_server_chunk_heap_new(buffer.s, ZSTR_VAL(buffer.s), ZSTR_LEN(buffer.s));
  1881. if (!chunk) {
  1882. smart_str_free_ex(&buffer, 1);
  1883. php_cli_server_log_response(client, 500, NULL);
  1884. return FAILURE;
  1885. }
  1886. php_cli_server_buffer_append(&client->content_sender.buffer, chunk);
  1887. }
  1888. php_cli_server_log_response(client, 200, NULL);
  1889. php_cli_server_poller_add(&server->poller, POLLOUT, client->sock);
  1890. return SUCCESS;
  1891. }
  1892. /* }}} */
  1893. static int php_cli_server_request_startup(php_cli_server *server, php_cli_server_client *client) { /* {{{ */
  1894. char *auth;
  1895. php_cli_server_client_populate_request_info(client, &SG(request_info));
  1896. if (NULL != (auth = zend_hash_str_find_ptr(&client->request.headers, "authorization", sizeof("authorization")-1))) {
  1897. php_handle_auth_data(auth);
  1898. }
  1899. SG(sapi_headers).http_response_code = 200;
  1900. if (FAILURE == php_request_startup()) {
  1901. return FAILURE;
  1902. }
  1903. PG(during_request_startup) = 0;
  1904. return SUCCESS;
  1905. }
  1906. /* }}} */
  1907. static int php_cli_server_request_shutdown(php_cli_server *server, php_cli_server_client *client) { /* {{{ */
  1908. php_request_shutdown(0);
  1909. php_cli_server_close_connection(server, client);
  1910. destroy_request_info(&SG(request_info));
  1911. SG(server_context) = NULL;
  1912. SG(rfc1867_uploaded_files) = NULL;
  1913. return SUCCESS;
  1914. }
  1915. /* }}} */
  1916. static int php_cli_server_dispatch_router(php_cli_server *server, php_cli_server_client *client) /* {{{ */
  1917. {
  1918. int decline = 0;
  1919. zend_file_handle zfd;
  1920. char *old_cwd;
  1921. ALLOCA_FLAG(use_heap)
  1922. old_cwd = do_alloca(MAXPATHLEN, use_heap);
  1923. old_cwd[0] = '\0';
  1924. php_ignore_value(VCWD_GETCWD(old_cwd, MAXPATHLEN - 1));
  1925. zend_stream_init_filename(&zfd, server->router);
  1926. zfd.primary_script = 1;
  1927. zend_try {
  1928. zval retval;
  1929. ZVAL_UNDEF(&retval);
  1930. if (SUCCESS == zend_execute_scripts(ZEND_REQUIRE, &retval, 1, &zfd)) {
  1931. if (Z_TYPE(retval) != IS_UNDEF) {
  1932. decline = Z_TYPE(retval) == IS_FALSE;
  1933. zval_ptr_dtor(&retval);
  1934. }
  1935. }
  1936. } zend_end_try();
  1937. zend_destroy_file_handle(&zfd);
  1938. if (old_cwd[0] != '\0') {
  1939. php_ignore_value(VCWD_CHDIR(old_cwd));
  1940. }
  1941. free_alloca(old_cwd, use_heap);
  1942. return decline;
  1943. }
  1944. /* }}} */
  1945. static int php_cli_server_dispatch(php_cli_server *server, php_cli_server_client *client) /* {{{ */
  1946. {
  1947. int is_static_file = 0;
  1948. const char *ext = client->request.ext;
  1949. SG(server_context) = client;
  1950. if (client->request.ext_len != 3
  1951. || (ext[0] != 'p' && ext[0] != 'P') || (ext[1] != 'h' && ext[1] != 'H') || (ext[2] != 'p' && ext[2] != 'P')
  1952. || !client->request.path_translated) {
  1953. is_static_file = 1;
  1954. }
  1955. if (server->router || !is_static_file) {
  1956. if (FAILURE == php_cli_server_request_startup(server, client)) {
  1957. php_cli_server_request_shutdown(server, client);
  1958. return SUCCESS;
  1959. }
  1960. }
  1961. if (server->router) {
  1962. if (!php_cli_server_dispatch_router(server, client)) {
  1963. php_cli_server_request_shutdown(server, client);
  1964. return SUCCESS;
  1965. }
  1966. }
  1967. if (!is_static_file) {
  1968. if (SUCCESS == php_cli_server_dispatch_script(server, client)
  1969. || SUCCESS != php_cli_server_send_error_page(server, client, 500)) {
  1970. if (SG(sapi_headers).http_response_code == 304) {
  1971. SG(sapi_headers).send_default_content_type = 0;
  1972. }
  1973. php_cli_server_request_shutdown(server, client);
  1974. return SUCCESS;
  1975. }
  1976. } else {
  1977. if (server->router) {
  1978. static int (*send_header_func)(sapi_headers_struct *);
  1979. send_header_func = sapi_module.send_headers;
  1980. /* do not generate default content type header */
  1981. SG(sapi_headers).send_default_content_type = 0;
  1982. /* we don't want headers to be sent */
  1983. sapi_module.send_headers = sapi_cli_server_discard_headers;
  1984. php_request_shutdown(0);
  1985. sapi_module.send_headers = send_header_func;
  1986. SG(sapi_headers).send_default_content_type = 1;
  1987. SG(rfc1867_uploaded_files) = NULL;
  1988. }
  1989. if (SUCCESS != php_cli_server_begin_send_static(server, client)) {
  1990. php_cli_server_close_connection(server, client);
  1991. }
  1992. SG(server_context) = NULL;
  1993. return SUCCESS;
  1994. }
  1995. SG(server_context) = NULL;
  1996. destroy_request_info(&SG(request_info));
  1997. return SUCCESS;
  1998. }
  1999. /* }}} */
  2000. static int php_cli_server_mime_type_ctor(php_cli_server *server, const php_cli_server_ext_mime_type_pair *mime_type_map) /* {{{ */
  2001. {
  2002. const php_cli_server_ext_mime_type_pair *pair;
  2003. zend_hash_init(&server->extension_mime_types, 0, NULL, NULL, 1);
  2004. for (pair = mime_type_map; pair->ext; pair++) {
  2005. size_t ext_len = strlen(pair->ext);
  2006. zend_hash_str_add_ptr(&server->extension_mime_types, pair->ext, ext_len, (void*)pair->mime_type);
  2007. }
  2008. return SUCCESS;
  2009. } /* }}} */
  2010. static void php_cli_server_dtor(php_cli_server *server) /* {{{ */
  2011. {
  2012. zend_hash_destroy(&server->clients);
  2013. zend_hash_destroy(&server->extension_mime_types);
  2014. if (ZEND_VALID_SOCKET(server->server_sock)) {
  2015. closesocket(server->server_sock);
  2016. }
  2017. if (server->host) {
  2018. pefree(server->host, 1);
  2019. }
  2020. if (server->document_root) {
  2021. pefree(server->document_root, 1);
  2022. }
  2023. if (server->router) {
  2024. pefree(server->router, 1);
  2025. }
  2026. #if HAVE_FORK
  2027. if (php_cli_server_workers_max > 1 &&
  2028. php_cli_server_workers &&
  2029. getpid() == php_cli_server_master) {
  2030. zend_long php_cli_server_worker;
  2031. for (php_cli_server_worker = 0;
  2032. php_cli_server_worker < php_cli_server_workers_max;
  2033. php_cli_server_worker++) {
  2034. int php_cli_server_worker_status;
  2035. do {
  2036. if (waitpid(php_cli_server_workers[php_cli_server_worker],
  2037. &php_cli_server_worker_status,
  2038. 0) == FAILURE) {
  2039. /* an extremely bad thing happened */
  2040. break;
  2041. }
  2042. } while (!WIFEXITED(php_cli_server_worker_status) &&
  2043. !WIFSIGNALED(php_cli_server_worker_status));
  2044. }
  2045. pefree(php_cli_server_workers, 1);
  2046. }
  2047. #endif
  2048. } /* }}} */
  2049. static void php_cli_server_client_dtor_wrapper(zval *zv) /* {{{ */
  2050. {
  2051. php_cli_server_client *p = Z_PTR_P(zv);
  2052. shutdown(p->sock, SHUT_RDWR);
  2053. closesocket(p->sock);
  2054. php_cli_server_poller_remove(&p->server->poller, POLLIN | POLLOUT, p->sock);
  2055. php_cli_server_client_dtor(p);
  2056. pefree(p, 1);
  2057. } /* }}} */
  2058. /**
  2059. * Parse the host and port portions of an address specifier in
  2060. * one of the following forms:
  2061. * - hostOrIP:port
  2062. * - [hostOrIP]:port
  2063. */
  2064. static char *php_cli_server_parse_addr(const char *addr, int *pport) {
  2065. const char *p, *end;
  2066. long port;
  2067. if (addr[0] == '[') {
  2068. /* Encapsulated [hostOrIP]:port */
  2069. const char *start = addr + 1;
  2070. end = strchr(start, ']');
  2071. if (!end) {
  2072. /* No ending ] delimiter to match [ */
  2073. return NULL;
  2074. }
  2075. p = end + 1;
  2076. if (*p != ':') {
  2077. /* Invalid char following address/missing port */
  2078. return NULL;
  2079. }
  2080. port = strtol(p + 1, (char**)&p, 10);
  2081. if (p && *p) {
  2082. /* Non-numeric in port */
  2083. return NULL;
  2084. }
  2085. if (port < 0 || port > 65535) {
  2086. /* Invalid port */
  2087. return NULL;
  2088. }
  2089. /* Full [hostOrIP]:port provided */
  2090. *pport = (int)port;
  2091. return pestrndup(start, end - start, 1);
  2092. }
  2093. end = strchr(addr, ':');
  2094. if (!end) {
  2095. /* Missing port */
  2096. return NULL;
  2097. }
  2098. port = strtol(end + 1, (char**)&p, 10);
  2099. if (p && *p) {
  2100. /* Non-numeric port */
  2101. return NULL;
  2102. }
  2103. if (port < 0 || port > 65535) {
  2104. /* Invalid port */
  2105. return NULL;
  2106. }
  2107. *pport = (int)port;
  2108. return pestrndup(addr, end - addr, 1);
  2109. }
  2110. static void php_cli_server_startup_workers(void) {
  2111. char *workers = getenv("PHP_CLI_SERVER_WORKERS");
  2112. if (!workers) {
  2113. return;
  2114. }
  2115. #if HAVE_FORK
  2116. php_cli_server_workers_max = ZEND_ATOL(workers);
  2117. if (php_cli_server_workers_max > 1) {
  2118. zend_long php_cli_server_worker;
  2119. php_cli_server_workers = pecalloc(
  2120. php_cli_server_workers_max, sizeof(pid_t), 1);
  2121. php_cli_server_master = getpid();
  2122. for (php_cli_server_worker = 0;
  2123. php_cli_server_worker < php_cli_server_workers_max;
  2124. php_cli_server_worker++) {
  2125. pid_t pid = fork();
  2126. if (pid == FAILURE) {
  2127. /* no more forks allowed, work with what we have ... */
  2128. php_cli_server_workers_max =
  2129. php_cli_server_worker + 1;
  2130. return;
  2131. } else if (pid == SUCCESS) {
  2132. return;
  2133. } else {
  2134. php_cli_server_workers[php_cli_server_worker] = pid;
  2135. }
  2136. }
  2137. } else {
  2138. fprintf(stderr, "number of workers must be larger than 1\n");
  2139. }
  2140. #else
  2141. fprintf(stderr, "forking is not supported on this platform\n");
  2142. #endif
  2143. }
  2144. static int php_cli_server_ctor(php_cli_server *server, const char *addr, const char *document_root, const char *router) /* {{{ */
  2145. {
  2146. int retval = SUCCESS;
  2147. char *host = NULL;
  2148. zend_string *errstr = NULL;
  2149. char *_document_root = NULL;
  2150. char *_router = NULL;
  2151. int err = 0;
  2152. int port = 3000;
  2153. php_socket_t server_sock = SOCK_ERR;
  2154. host = php_cli_server_parse_addr(addr, &port);
  2155. if (!host) {
  2156. fprintf(stderr, "Invalid address: %s\n", addr);
  2157. retval = FAILURE;
  2158. goto out;
  2159. }
  2160. server_sock = php_network_listen_socket(host, &port, SOCK_STREAM, &server->address_family, &server->socklen, &errstr);
  2161. if (server_sock == SOCK_ERR) {
  2162. php_cli_server_logf(PHP_CLI_SERVER_LOG_ERROR, "Failed to listen on %s:%d (reason: %s)", host, port, errstr ? ZSTR_VAL(errstr) : "?");
  2163. if (errstr) {
  2164. zend_string_release_ex(errstr, 0);
  2165. }
  2166. retval = FAILURE;
  2167. goto out;
  2168. }
  2169. server->server_sock = server_sock;
  2170. php_cli_server_startup_workers();
  2171. err = php_cli_server_poller_ctor(&server->poller);
  2172. if (SUCCESS != err) {
  2173. goto out;
  2174. }
  2175. php_cli_server_poller_add(&server->poller, POLLIN, server_sock);
  2176. server->host = host;
  2177. server->port = port;
  2178. zend_hash_init(&server->clients, 0, NULL, php_cli_server_client_dtor_wrapper, 1);
  2179. {
  2180. size_t document_root_len = strlen(document_root);
  2181. _document_root = pestrndup(document_root, document_root_len, 1);
  2182. if (!_document_root) {
  2183. retval = FAILURE;
  2184. goto out;
  2185. }
  2186. server->document_root = _document_root;
  2187. server->document_root_len = document_root_len;
  2188. }
  2189. if (router) {
  2190. size_t router_len = strlen(router);
  2191. _router = pestrndup(router, router_len, 1);
  2192. if (!_router) {
  2193. retval = FAILURE;
  2194. goto out;
  2195. }
  2196. server->router = _router;
  2197. server->router_len = router_len;
  2198. } else {
  2199. server->router = NULL;
  2200. server->router_len = 0;
  2201. }
  2202. if (php_cli_server_mime_type_ctor(server, mime_type_map) == FAILURE) {
  2203. retval = FAILURE;
  2204. goto out;
  2205. }
  2206. server->is_running = 1;
  2207. out:
  2208. if (retval != SUCCESS) {
  2209. if (host) {
  2210. pefree(host, 1);
  2211. }
  2212. if (_document_root) {
  2213. pefree(_document_root, 1);
  2214. }
  2215. if (_router) {
  2216. pefree(_router, 1);
  2217. }
  2218. if (server_sock > -1) {
  2219. closesocket(server_sock);
  2220. }
  2221. }
  2222. return retval;
  2223. } /* }}} */
  2224. static int php_cli_server_recv_event_read_request(php_cli_server *server, php_cli_server_client *client) /* {{{ */
  2225. {
  2226. char *errstr = NULL;
  2227. int status = php_cli_server_client_read_request(client, &errstr);
  2228. if (status < 0) {
  2229. if (errstr) {
  2230. if (strcmp(errstr, php_cli_server_request_error_unexpected_eof) == 0 && client->parser.state == s_start_req) {
  2231. php_cli_server_logf(PHP_CLI_SERVER_LOG_MESSAGE,
  2232. "%s Closed without sending a request; it was probably just an unused speculative preconnection", client->addr_str);
  2233. } else {
  2234. php_cli_server_logf(PHP_CLI_SERVER_LOG_ERROR, "%s Invalid request (%s)", client->addr_str, errstr);
  2235. }
  2236. efree(errstr);
  2237. }
  2238. php_cli_server_close_connection(server, client);
  2239. return FAILURE;
  2240. } else if (status == 1 && client->request.request_method == PHP_HTTP_NOT_IMPLEMENTED) {
  2241. return php_cli_server_send_error_page(server, client, 501);
  2242. } else if (status == 1) {
  2243. php_cli_server_poller_remove(&server->poller, POLLIN, client->sock);
  2244. php_cli_server_dispatch(server, client);
  2245. } else {
  2246. php_cli_server_poller_add(&server->poller, POLLIN, client->sock);
  2247. }
  2248. return SUCCESS;
  2249. } /* }}} */
  2250. static int php_cli_server_send_event(php_cli_server *server, php_cli_server_client *client) /* {{{ */
  2251. {
  2252. if (client->content_sender_initialized) {
  2253. if (client->file_fd >= 0 && !client->content_sender.buffer.first) {
  2254. size_t nbytes_read;
  2255. if (php_cli_server_content_sender_pull(&client->content_sender, client->file_fd, &nbytes_read)) {
  2256. php_cli_server_close_connection(server, client);
  2257. return FAILURE;
  2258. }
  2259. if (nbytes_read == 0) {
  2260. close(client->file_fd);
  2261. client->file_fd = -1;
  2262. }
  2263. }
  2264. {
  2265. size_t nbytes_sent;
  2266. int err = php_cli_server_content_sender_send(&client->content_sender, client->sock, &nbytes_sent);
  2267. if (err && err != SOCK_EAGAIN) {
  2268. php_cli_server_close_connection(server, client);
  2269. return FAILURE;
  2270. }
  2271. }
  2272. if (!client->content_sender.buffer.first && client->file_fd < 0) {
  2273. php_cli_server_close_connection(server, client);
  2274. }
  2275. }
  2276. return SUCCESS;
  2277. }
  2278. /* }}} */
  2279. typedef struct php_cli_server_do_event_for_each_fd_callback_params {
  2280. php_cli_server *server;
  2281. int(*rhandler)(php_cli_server*, php_cli_server_client*);
  2282. int(*whandler)(php_cli_server*, php_cli_server_client*);
  2283. } php_cli_server_do_event_for_each_fd_callback_params;
  2284. static int php_cli_server_do_event_for_each_fd_callback(void *_params, php_socket_t fd, int event) /* {{{ */
  2285. {
  2286. php_cli_server_do_event_for_each_fd_callback_params *params = _params;
  2287. php_cli_server *server = params->server;
  2288. if (server->server_sock == fd) {
  2289. php_cli_server_client *client = NULL;
  2290. php_socket_t client_sock;
  2291. socklen_t socklen = server->socklen;
  2292. struct sockaddr *sa = pemalloc(server->socklen, 1);
  2293. client_sock = accept(server->server_sock, sa, &socklen);
  2294. if (!ZEND_VALID_SOCKET(client_sock)) {
  2295. if (php_cli_server_log_level >= PHP_CLI_SERVER_LOG_ERROR) {
  2296. char *errstr = php_socket_strerror(php_socket_errno(), NULL, 0);
  2297. php_cli_server_logf(PHP_CLI_SERVER_LOG_ERROR,
  2298. "Failed to accept a client (reason: %s)", errstr);
  2299. efree(errstr);
  2300. }
  2301. pefree(sa, 1);
  2302. return SUCCESS;
  2303. }
  2304. if (SUCCESS != php_set_sock_blocking(client_sock, 0)) {
  2305. pefree(sa, 1);
  2306. closesocket(client_sock);
  2307. return SUCCESS;
  2308. }
  2309. client = pemalloc(sizeof(php_cli_server_client), 1);
  2310. if (FAILURE == php_cli_server_client_ctor(client, server, client_sock, sa, socklen)) {
  2311. php_cli_server_logf(PHP_CLI_SERVER_LOG_ERROR, "Failed to create a new request object");
  2312. pefree(sa, 1);
  2313. closesocket(client_sock);
  2314. return SUCCESS;
  2315. }
  2316. php_cli_server_logf(PHP_CLI_SERVER_LOG_MESSAGE, "%s Accepted", client->addr_str);
  2317. zend_hash_index_update_ptr(&server->clients, client_sock, client);
  2318. php_cli_server_poller_add(&server->poller, POLLIN, client->sock);
  2319. } else {
  2320. php_cli_server_client *client;
  2321. if (NULL != (client = zend_hash_index_find_ptr(&server->clients, fd))) {
  2322. if (event & POLLIN) {
  2323. params->rhandler(server, client);
  2324. }
  2325. if (event & POLLOUT) {
  2326. params->whandler(server, client);
  2327. }
  2328. }
  2329. }
  2330. return SUCCESS;
  2331. } /* }}} */
  2332. static void php_cli_server_do_event_for_each_fd(php_cli_server *server, int(*rhandler)(php_cli_server*, php_cli_server_client*), int(*whandler)(php_cli_server*, php_cli_server_client*)) /* {{{ */
  2333. {
  2334. php_cli_server_do_event_for_each_fd_callback_params params = {
  2335. server,
  2336. rhandler,
  2337. whandler
  2338. };
  2339. php_cli_server_poller_iter_on_active(&server->poller, &params, php_cli_server_do_event_for_each_fd_callback);
  2340. } /* }}} */
  2341. static int php_cli_server_do_event_loop(php_cli_server *server) /* {{{ */
  2342. {
  2343. int retval = SUCCESS;
  2344. while (server->is_running) {
  2345. struct timeval tv = { 1, 0 };
  2346. int n = php_cli_server_poller_poll(&server->poller, &tv);
  2347. if (n > 0) {
  2348. php_cli_server_do_event_for_each_fd(server,
  2349. php_cli_server_recv_event_read_request,
  2350. php_cli_server_send_event);
  2351. } else if (n == 0) {
  2352. /* do nothing */
  2353. } else {
  2354. int err = php_socket_errno();
  2355. if (err != SOCK_EINTR) {
  2356. if (php_cli_server_log_level >= PHP_CLI_SERVER_LOG_ERROR) {
  2357. char *errstr = php_socket_strerror(err, NULL, 0);
  2358. php_cli_server_logf(PHP_CLI_SERVER_LOG_ERROR, "%s", errstr);
  2359. efree(errstr);
  2360. }
  2361. retval = FAILURE;
  2362. goto out;
  2363. }
  2364. }
  2365. }
  2366. out:
  2367. return retval;
  2368. } /* }}} */
  2369. static php_cli_server server;
  2370. static void php_cli_server_sigint_handler(int sig) /* {{{ */
  2371. {
  2372. server.is_running = 0;
  2373. }
  2374. /* }}} */
  2375. int do_cli_server(int argc, char **argv) /* {{{ */
  2376. {
  2377. char *php_optarg = NULL;
  2378. int php_optind = 1;
  2379. int c;
  2380. const char *server_bind_address = NULL;
  2381. extern const opt_struct OPTIONS[];
  2382. const char *document_root = NULL;
  2383. #ifdef PHP_WIN32
  2384. char document_root_tmp[MAXPATHLEN];
  2385. size_t k;
  2386. #endif
  2387. const char *router = NULL;
  2388. char document_root_buf[MAXPATHLEN];
  2389. while ((c = php_getopt(argc, argv, OPTIONS, &php_optarg, &php_optind, 0, 2))!=-1) {
  2390. switch (c) {
  2391. case 'S':
  2392. server_bind_address = php_optarg;
  2393. break;
  2394. case 't':
  2395. #ifndef PHP_WIN32
  2396. document_root = php_optarg;
  2397. #else
  2398. k = strlen(php_optarg);
  2399. if (k + 1 > MAXPATHLEN) {
  2400. fprintf(stderr, "Document root path is too long.\n");
  2401. return 1;
  2402. }
  2403. memmove(document_root_tmp, php_optarg, k + 1);
  2404. /* Clean out any trailing garbage that might have been passed
  2405. from a batch script. */
  2406. do {
  2407. document_root_tmp[k] = '\0';
  2408. k--;
  2409. } while ('"' == document_root_tmp[k] || ' ' == document_root_tmp[k]);
  2410. document_root = document_root_tmp;
  2411. #endif
  2412. break;
  2413. case 'q':
  2414. if (php_cli_server_log_level > 1) {
  2415. php_cli_server_log_level--;
  2416. }
  2417. break;
  2418. }
  2419. }
  2420. if (document_root) {
  2421. zend_stat_t sb;
  2422. if (php_sys_stat(document_root, &sb)) {
  2423. fprintf(stderr, "Directory %s does not exist.\n", document_root);
  2424. return 1;
  2425. }
  2426. if (!S_ISDIR(sb.st_mode)) {
  2427. fprintf(stderr, "%s is not a directory.\n", document_root);
  2428. return 1;
  2429. }
  2430. if (VCWD_REALPATH(document_root, document_root_buf)) {
  2431. document_root = document_root_buf;
  2432. }
  2433. } else {
  2434. char *ret = NULL;
  2435. #if HAVE_GETCWD
  2436. ret = VCWD_GETCWD(document_root_buf, MAXPATHLEN);
  2437. #elif HAVE_GETWD
  2438. ret = VCWD_GETWD(document_root_buf);
  2439. #endif
  2440. document_root = ret ? document_root_buf: ".";
  2441. }
  2442. if (argc > php_optind) {
  2443. router = argv[php_optind];
  2444. }
  2445. if (FAILURE == php_cli_server_ctor(&server, server_bind_address, document_root, router)) {
  2446. return 1;
  2447. }
  2448. sapi_module.phpinfo_as_text = 0;
  2449. {
  2450. bool ipv6 = strchr(server.host, ':');
  2451. php_cli_server_logf(
  2452. PHP_CLI_SERVER_LOG_PROCESS,
  2453. "PHP %s Development Server (http://%s%s%s:%d) started",
  2454. PHP_VERSION, ipv6 ? "[" : "", server.host,
  2455. ipv6 ? "]" : "", server.port);
  2456. }
  2457. #if defined(SIGINT)
  2458. signal(SIGINT, php_cli_server_sigint_handler);
  2459. #endif
  2460. #if defined(SIGPIPE)
  2461. signal(SIGPIPE, SIG_IGN);
  2462. #endif
  2463. zend_signal_init();
  2464. php_cli_server_do_event_loop(&server);
  2465. php_cli_server_dtor(&server);
  2466. return 0;
  2467. } /* }}} */