libafcgi is a libev based asynchronous FastCGI library https://redmine.lighttpd.net/projects/libafcgi
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

875 lines
25 KiB

  1. #include "libafcgi.h"
  2. #include <arpa/inet.h>
  3. #include <errno.h>
  4. #include <sys/socket.h>
  5. #include <fcntl.h>
  6. #include <unistd.h>
  7. #include <string.h>
  8. typedef struct fastcgi_queue_link {
  9. GList queue_link;
  10. enum { FASTCGI_QUEUE_STRING, FASTCGI_QUEUE_BYTEARRAY } elem_type;
  11. } fastcgi_queue_link;
  12. /* some util functions */
  13. #define GSTR_LEN(x) ((x) ? (x)->str : ""), ((x) ? (x)->len : 0)
  14. #define GBARR_LEN(x) ((x)->data), ((x)->len)
  15. #define UNUSED(x) ((void)(x))
  16. #define ERROR(...) g_printerr("libafcgi.c:" G_STRINGIFY(__LINE__) ": " __VA_ARGS__)
  17. static void fd_init(int fd) {
  18. #ifdef _WIN32
  19. int i = 1;
  20. #endif
  21. #ifdef FD_CLOEXEC
  22. /* close fd on exec (cgi) */
  23. fcntl(fd, F_SETFD, FD_CLOEXEC);
  24. #endif
  25. #ifdef O_NONBLOCK
  26. fcntl(fd, F_SETFL, O_NONBLOCK | O_RDWR);
  27. #elif defined _WIN32
  28. ioctlsocket(fd, FIONBIO, &i);
  29. #endif
  30. }
  31. static fastcgi_queue_link* fastcgi_queue_link_new_string(GString *s) {
  32. fastcgi_queue_link *l = g_slice_new0(fastcgi_queue_link);
  33. l->queue_link.data = s;
  34. l->elem_type = FASTCGI_QUEUE_STRING;
  35. return l;
  36. }
  37. static fastcgi_queue_link* fastcgi_queue_link_new_bytearray(GByteArray *a) {
  38. fastcgi_queue_link *l = g_slice_new0(fastcgi_queue_link);
  39. l->queue_link.data = a;
  40. l->elem_type = FASTCGI_QUEUE_BYTEARRAY ;
  41. return l;
  42. }
  43. static void fastcgi_queue_link_free(fastcgi_queue *queue, fastcgi_queue_link *l) {
  44. switch (l->elem_type) {
  45. case FASTCGI_QUEUE_STRING:
  46. if (queue) queue->length -= ((GString*)l->queue_link.data)->len;
  47. g_string_free(l->queue_link.data, TRUE);
  48. break;
  49. case FASTCGI_QUEUE_BYTEARRAY:
  50. if (queue) queue->length -= ((GByteArray*)l->queue_link.data)->len;
  51. g_byte_array_free(l->queue_link.data, TRUE);
  52. break;
  53. }
  54. g_slice_free(fastcgi_queue_link, l);
  55. }
  56. static fastcgi_queue_link *fastcgi_queue_peek_head(fastcgi_queue *queue) {
  57. return (fastcgi_queue_link*) g_queue_peek_head_link(&queue->queue);
  58. }
  59. static fastcgi_queue_link *fastcgi_queue_pop_head(fastcgi_queue *queue) {
  60. return (fastcgi_queue_link*) g_queue_pop_head_link(&queue->queue);
  61. }
  62. void fastcgi_queue_clear(fastcgi_queue *queue) {
  63. fastcgi_queue_link *l;
  64. queue->offset = 0;
  65. while (NULL != (l = fastcgi_queue_pop_head(queue))) {
  66. fastcgi_queue_link_free(queue, l);
  67. }
  68. g_assert(0 == queue->length);
  69. }
  70. void fastcgi_queue_append_string(fastcgi_queue *queue, GString *buf) {
  71. fastcgi_queue_link *l;
  72. if (!buf) return;
  73. if (!buf->len) { g_string_free(buf, TRUE); return; }
  74. l = fastcgi_queue_link_new_string(buf);
  75. g_queue_push_tail_link(&queue->queue, (GList*) l);
  76. queue->length += buf->len;
  77. }
  78. void fastcgi_queue_append_bytearray(fastcgi_queue *queue, GByteArray *buf) {
  79. fastcgi_queue_link *l;
  80. if (!buf) return;
  81. if (!buf->len) { g_byte_array_free(buf, TRUE); return; }
  82. l = fastcgi_queue_link_new_bytearray(buf);
  83. g_queue_push_tail_link(&queue->queue, (GList*) l);
  84. queue->length += buf->len;
  85. }
  86. /* return values: 0 ok, -1 error, -2 con closed */
  87. gint fastcgi_queue_write(int fd, fastcgi_queue *queue, gsize max_write) {
  88. gsize rem_write = max_write;
  89. g_assert(rem_write <= G_MAXSSIZE);
  90. #ifdef TCP_CORK
  91. int corked = 0;
  92. #endif
  93. #ifdef TCP_CORK
  94. /* Linux: put a cork into the socket as we want to combine the write() calls
  95. * but only if we really have multiple chunks
  96. */
  97. if (queue->queue.length > 1) {
  98. corked = 1;
  99. setsockopt(fd, IPPROTO_TCP, TCP_CORK, &corked, sizeof(corked));
  100. }
  101. #endif
  102. while (rem_write > 0 && queue->length > 0) {
  103. fastcgi_queue_link *l = fastcgi_queue_peek_head(queue);
  104. gsize towrite, datalen;
  105. gssize res;
  106. gchar *data;
  107. switch (l->elem_type) {
  108. case FASTCGI_QUEUE_STRING:
  109. data = ((GString*) l->queue_link.data)->str;
  110. datalen = towrite = ((GString*) l->queue_link.data)->len;
  111. break;
  112. case FASTCGI_QUEUE_BYTEARRAY:
  113. data = (gchar*) ((GByteArray*) l->queue_link.data)->data;
  114. datalen = towrite = ((GByteArray*) l->queue_link.data)->len;
  115. break;
  116. default:
  117. g_error("invalid fastcgi_queue_link type\n");
  118. }
  119. towrite -= queue->offset; data += queue->offset;
  120. if (towrite > rem_write) towrite = rem_write;
  121. res = write(fd, data, towrite);
  122. if (-1 == res) {
  123. #ifdef TCP_CORK
  124. if (corked) {
  125. corked = 0;
  126. setsockopt(fd, IPPROTO_TCP, TCP_CORK, &corked, sizeof(corked));
  127. }
  128. #endif
  129. switch (errno) {
  130. case EINTR:
  131. case EAGAIN:
  132. #if EWOULDBLOCK != EAGAIN
  133. case EWOULDBLOCK:
  134. #endif
  135. return 0; /* try again later */
  136. case ECONNRESET:
  137. case EPIPE:
  138. return -2;
  139. default:
  140. ERROR("write to fd=%d failed, %s\n", fd, g_strerror(errno) );
  141. return -1;
  142. }
  143. } else {
  144. queue->offset += res;
  145. rem_write -= res;
  146. if (queue->offset == datalen) {
  147. queue->offset = 0;
  148. fastcgi_queue_link_free(queue, fastcgi_queue_pop_head(queue));
  149. }
  150. }
  151. }
  152. #ifdef TCP_CORK
  153. if (corked) {
  154. corked = 0;
  155. setsockopt(fd, IPPROTO_TCP, TCP_CORK, &corked, sizeof(corked));
  156. }
  157. #endif
  158. return 0;
  159. }
  160. static void ev_io_add_events(struct ev_loop *loop, ev_io *watcher, int events) {
  161. if ((watcher->events & events) == events) return;
  162. ev_io_stop(loop, watcher);
  163. ev_io_set(watcher, watcher->fd, watcher->events | events);
  164. ev_io_start(loop, watcher);
  165. }
  166. static void ev_io_rem_events(struct ev_loop *loop, ev_io *watcher, int events) {
  167. if (0 == (watcher->events & events)) return;
  168. ev_io_stop(loop, watcher);
  169. ev_io_set(watcher, watcher->fd, watcher->events & ~events);
  170. ev_io_start(loop, watcher);
  171. }
  172. /* end: some util functions */
  173. static const guint8 __padding[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };
  174. static void append_padding_str(GString *s, guint8 padlen) {
  175. g_string_append_len(s, (const gchar*) __padding, padlen);
  176. }
  177. static void append_padding_bytearray(GByteArray *a, guint8 padlen) {
  178. g_byte_array_append(a, __padding, padlen);
  179. }
  180. /* returns padding length */
  181. static guint8 stream_build_fcgi_record(GByteArray *buf, guint8 type, guint16 requestid, guint16 datalen) {
  182. guint8 padlen = (8 - (datalen & 0x7)) % 8; /* padding must be < 8 */
  183. /* alloc enough space */
  184. g_byte_array_set_size(buf, FCGI_HEADER_LEN);
  185. buf->len = 0;
  186. buf->data[buf->len++] = FCGI_VERSION_1;
  187. buf->data[buf->len++] = type;
  188. buf->data[buf->len++] = (guint8) (requestid >> 8);
  189. buf->data[buf->len++] = (guint8) (requestid);
  190. buf->data[buf->len++] = (guint8) (datalen >> 8);
  191. buf->data[buf->len++] = (guint8) (datalen);
  192. buf->data[buf->len++] = padlen;
  193. buf->data[buf->len++] = 0;
  194. return padlen;
  195. }
  196. /* returns padding length */
  197. static guint8 stream_send_fcgi_record(fastcgi_queue *out, guint8 type, guint16 requestid, guint16 datalen) {
  198. GByteArray *record = g_byte_array_sized_new(FCGI_HEADER_LEN);
  199. guint8 padlen = stream_build_fcgi_record(record, type, requestid, datalen);
  200. fastcgi_queue_append_bytearray(out, record);
  201. return padlen;
  202. }
  203. static void stream_send_data(fastcgi_queue *out, guint8 type, guint16 requestid, const guint8 *data, size_t datalen) {
  204. while (datalen > 0) {
  205. guint16 tosend = (datalen > G_MAXUINT16) ? G_MAXUINT16 : datalen;
  206. guint8 padlen = stream_send_fcgi_record(out, type, requestid, tosend);
  207. GByteArray *buf = g_byte_array_sized_new(tosend + padlen);
  208. g_byte_array_append(buf, data, tosend);
  209. append_padding_bytearray(buf, padlen);
  210. fastcgi_queue_append_bytearray(out, buf);
  211. data += tosend;
  212. datalen -= tosend;
  213. }
  214. }
  215. /* kills string */
  216. static void stream_send_string(fastcgi_queue *out, guint8 type, guint16 requestid, GString *data) {
  217. if (data->len > G_MAXUINT16) {
  218. stream_send_data(out, type, requestid, (const guint8*) GSTR_LEN(data));
  219. g_string_free(data, TRUE);
  220. } else {
  221. guint8 padlen = stream_send_fcgi_record(out, type, requestid, data->len);
  222. append_padding_str(data, padlen);
  223. fastcgi_queue_append_string(out, data);
  224. }
  225. }
  226. /* kills bytearray */
  227. static void stream_send_bytearray(fastcgi_queue *out, guint8 type, guint16 requestid, GByteArray *data) {
  228. if (data->len > G_MAXUINT16) {
  229. stream_send_data(out, type, requestid, GBARR_LEN(data));
  230. g_byte_array_free(data, TRUE);
  231. } else {
  232. guint8 padlen = stream_send_fcgi_record(out, type, requestid, data->len);
  233. append_padding_bytearray(data, padlen);
  234. fastcgi_queue_append_bytearray(out, data);
  235. }
  236. }
  237. static void stream_send_end_request(fastcgi_queue *out, guint16 requestID, gint32 appStatus, enum FCGI_ProtocolStatus status) {
  238. GByteArray *record;
  239. record = g_byte_array_sized_new(16);
  240. stream_build_fcgi_record(record, FCGI_END_REQUEST, requestID, 8);
  241. /* alloc enough space */
  242. g_byte_array_set_size(record, 16);
  243. record->len = 8;
  244. appStatus = htonl(appStatus);
  245. g_byte_array_append(record, (const guchar*) &appStatus, sizeof(appStatus));
  246. record->data[record->len++] = status;
  247. g_byte_array_append(record, __padding, 3);
  248. fastcgi_queue_append_bytearray(out, record);
  249. }
  250. static void write_queue(fastcgi_connection *fcon) {
  251. if (fcon->closing) return;
  252. if (fastcgi_queue_write(fcon->fd, &fcon->write_queue, 256*1024) < 0) {
  253. fastcgi_connection_close(fcon);
  254. return;
  255. }
  256. if (fcon->fsrv->callbacks->cb_wrote_data) {
  257. fcon->fsrv->callbacks->cb_wrote_data(fcon);
  258. }
  259. if (!fcon->closing) {
  260. if (fcon->write_queue.length > 0) {
  261. ev_io_add_events(fcon->fsrv->loop, &fcon->fd_watcher, EV_WRITE);
  262. } else {
  263. ev_io_rem_events(fcon->fsrv->loop, &fcon->fd_watcher, EV_WRITE);
  264. if (0 == fcon->requestID) {
  265. if (!(fcon->flags & FCGI_KEEP_CONN)) {
  266. fastcgi_connection_close(fcon);
  267. }
  268. }
  269. }
  270. }
  271. }
  272. static GByteArray* read_chunk(fastcgi_connection *fcon, guint maxlen) {
  273. gssize res;
  274. GByteArray *buf;
  275. int tmp_errno;
  276. buf = g_byte_array_sized_new(maxlen);
  277. g_byte_array_set_size(buf, maxlen);
  278. if (0 == maxlen) return buf;
  279. res = read(fcon->fd, buf->data, maxlen);
  280. if (res == -1) {
  281. tmp_errno = errno;
  282. g_byte_array_free(buf, TRUE);
  283. errno = tmp_errno;
  284. return NULL;
  285. } else if (res == 0) {
  286. g_byte_array_free(buf, TRUE);
  287. errno = ECONNRESET;
  288. return NULL;
  289. } else {
  290. g_byte_array_set_size(buf, res);
  291. return buf;
  292. }
  293. }
  294. /* read content + padding, but only returns content data. decrements counters */
  295. static GByteArray *read_content(fastcgi_connection *fcon) {
  296. GByteArray *buf;
  297. buf = read_chunk(fcon, fcon->content_remaining + fcon->padding_remaining);
  298. if (!buf) return NULL;
  299. if (buf->len > fcon->content_remaining) {
  300. fcon->padding_remaining -= (buf->len - fcon->content_remaining);
  301. g_byte_array_set_size(buf, fcon->content_remaining);
  302. fcon->content_remaining = 0;
  303. } else {
  304. fcon->content_remaining -= buf->len;
  305. }
  306. return buf;
  307. }
  308. static gboolean read_append_chunk(fastcgi_connection *fcon, GByteArray *buf) {
  309. gssize res;
  310. int tmp_errno;
  311. guint curlen = buf->len;
  312. const guint maxlen = fcon->content_remaining + fcon->padding_remaining;
  313. if (0 == maxlen) return TRUE;
  314. g_byte_array_set_size(buf, curlen + maxlen);
  315. res = read(fcon->fd, buf->data + curlen, maxlen);
  316. if (res == -1) {
  317. tmp_errno = errno;
  318. g_byte_array_set_size(buf, curlen);
  319. errno = tmp_errno;
  320. return FALSE;
  321. } else if (res == 0) {
  322. g_byte_array_set_size(buf, curlen);
  323. errno = ECONNRESET;
  324. return FALSE;
  325. } else {
  326. /* remove padding data */
  327. if (res > fcon->content_remaining) {
  328. fcon->padding_remaining -= res - fcon->content_remaining;
  329. res = fcon->content_remaining;
  330. }
  331. g_byte_array_set_size(buf, curlen + res);
  332. fcon->content_remaining -= res;
  333. return TRUE;
  334. }
  335. }
  336. static gboolean read_key_value(fastcgi_connection *fcon, GByteArray *buf, guint *pos, gchar **key, guint *keylen, gchar **value, guint *valuelen) {
  337. const unsigned char *data = (const unsigned char*) buf->data;
  338. guint32 klen, vlen;
  339. guint p = *pos, len = buf->len;
  340. if (len - p < 2) return FALSE;
  341. klen = data[p++];
  342. if (klen & 0x80) {
  343. if (len - p < 100) return FALSE;
  344. klen = ((klen & 0x7f) << 24) | (data[p] << 16) | (data[p+1] << 8) | data[p+2];
  345. p += 3;
  346. }
  347. vlen = data[p++];
  348. if (vlen & 0x80) {
  349. if (len - p < 100) return FALSE;
  350. vlen = ((vlen & 0x7f) << 24) | (data[p] << 16) | (data[p+1] << 8) | data[p+2];
  351. p += 3;
  352. }
  353. if (klen > FASTCGI_MAX_KEYLEN || vlen > FASTCGI_MAX_VALUELEN) {
  354. fastcgi_connection_close(fcon);
  355. return FALSE;
  356. }
  357. if (len - p < klen + vlen) return FALSE;
  358. *key = (gchar*) &buf->data[p];
  359. *keylen = klen;
  360. p += klen;
  361. *value = (gchar*) &buf->data[p];
  362. *valuelen = vlen;
  363. p += vlen;
  364. *pos = p;
  365. return TRUE;
  366. }
  367. static void parse_params(const fastcgi_callbacks *fcbs, fastcgi_connection *fcon) {
  368. if (!fcon->current_header.contentLength) {
  369. fcbs->cb_new_request(fcon);
  370. g_byte_array_set_size(fcon->parambuf, 0);
  371. } else {
  372. guint pos = 0, keylen = 0, valuelen = 0;
  373. gchar *key = NULL, *value = NULL;
  374. while (read_key_value(fcon, fcon->parambuf, &pos, &key, &keylen, &value, &valuelen)) {
  375. GString *gkey = g_string_new_len(key, keylen);
  376. GString *gvalue = g_string_new_len(value, valuelen);
  377. g_hash_table_insert(fcon->environ, gkey, gvalue);
  378. }
  379. if (!fcon->closing)
  380. g_byte_array_remove_range(fcon->parambuf, 0, pos);
  381. }
  382. }
  383. static void parse_get_values(fastcgi_connection *fcon) {
  384. /* just send the request back and don't insert results */
  385. GByteArray *tmp = g_byte_array_sized_new(0);
  386. stream_send_bytearray(&fcon->write_queue, FCGI_GET_VALUES_RESULT, 0, fcon->buffer);
  387. *fcon->buffer = *tmp;
  388. /* TODO: provide get-values result */
  389. }
  390. static void read_queue(fastcgi_connection *fcon) {
  391. gssize res;
  392. GByteArray *buf;
  393. const fastcgi_callbacks *fcbs = fcon->fsrv->callbacks;
  394. for (;;) {
  395. if (fcon->closing || fcon->read_suspended) return;
  396. if (fcon->headerbuf_used < 8) {
  397. const unsigned char *data = fcon->headerbuf;
  398. res = read(fcon->fd, fcon->headerbuf + fcon->headerbuf_used, 8 - fcon->headerbuf_used);
  399. if (0 == res) { errno = ECONNRESET; goto handle_error; }
  400. if (-1 == res) goto handle_error;
  401. fcon->headerbuf_used += res;
  402. if (fcon->headerbuf_used < 8) return; /* need more data */
  403. fcon->current_header.version = data[0];
  404. fcon->current_header.type = data[1];
  405. fcon->current_header.requestID = (data[2] << 8) | (data[3]);
  406. fcon->current_header.contentLength = (data[4] << 8) | (data[5]);
  407. fcon->current_header.paddingLength = data[6];
  408. fcon->content_remaining = fcon->current_header.contentLength;
  409. fcon->padding_remaining = fcon->current_header.paddingLength;
  410. fcon->first = TRUE;
  411. g_byte_array_set_size(fcon->buffer, 0);
  412. if (fcon->current_header.version != FCGI_VERSION_1) {
  413. fastcgi_connection_close(fcon);
  414. return;
  415. }
  416. }
  417. if (fcon->current_header.type != FCGI_BEGIN_REQUEST &&
  418. (0 != fcon->current_header.requestID) && fcon->current_header.requestID != fcon->requestID) {
  419. /* ignore packet data */
  420. if (0 != fcon->content_remaining + fcon->padding_remaining) {
  421. if (NULL == (buf = read_content(fcon))) goto handle_error;
  422. g_byte_array_free(buf, TRUE);
  423. }
  424. if (0 == fcon->content_remaining + fcon->padding_remaining) {
  425. fcon->headerbuf_used = 0;
  426. }
  427. continue;
  428. }
  429. if (fcon->first || fcon->content_remaining) {
  430. fcon->first = FALSE;
  431. switch (fcon->current_header.type) {
  432. case FCGI_BEGIN_REQUEST:
  433. if (8 != fcon->current_header.contentLength || 0 == fcon->current_header.requestID) goto error;
  434. if (!read_append_chunk(fcon, fcon->buffer)) goto handle_error;
  435. if (0 == fcon->content_remaining) {
  436. if (fcon->requestID) {
  437. stream_send_end_request(&fcon->write_queue, fcon->current_header.requestID, 0, FCGI_CANT_MPX_CONN);
  438. } else {
  439. unsigned char *data = (unsigned char*) fcon->buffer->data;
  440. fcon->requestID = fcon->current_header.requestID;
  441. fcon->role = (data[0] << 8) | (data[1]);
  442. fcon->flags = data[2];
  443. g_byte_array_set_size(fcon->parambuf, 0);
  444. }
  445. }
  446. break;
  447. case FCGI_ABORT_REQUEST:
  448. if (0 != fcon->current_header.contentLength || 0 == fcon->current_header.requestID) goto error;
  449. fcbs->cb_request_aborted(fcon);
  450. break;
  451. case FCGI_END_REQUEST:
  452. goto error; /* invalid type */
  453. case FCGI_PARAMS:
  454. if (0 == fcon->current_header.requestID) goto error;
  455. if (!read_append_chunk(fcon, fcon->parambuf)) goto handle_error;
  456. parse_params(fcbs, fcon);
  457. break;
  458. case FCGI_STDIN:
  459. if (0 == fcon->current_header.requestID) goto error;
  460. buf = NULL;
  461. if (0 != fcon->content_remaining &&
  462. NULL == (buf = read_content(fcon))) goto handle_error;
  463. if (fcbs->cb_received_stdin) {
  464. fcbs->cb_received_stdin(fcon, buf);
  465. } else {
  466. g_byte_array_free(buf, TRUE);
  467. }
  468. break;
  469. case FCGI_STDOUT:
  470. goto error; /* invalid type */
  471. case FCGI_STDERR:
  472. goto error; /* invalid type */
  473. case FCGI_DATA:
  474. if (0 == fcon->current_header.requestID) goto error;
  475. buf = NULL;
  476. if (0 != fcon->content_remaining &&
  477. NULL == (buf = read_content(fcon))) goto handle_error;
  478. if (fcbs->cb_received_data) {
  479. fcbs->cb_received_data(fcon, buf);
  480. } else {
  481. g_byte_array_free(buf, TRUE);
  482. }
  483. break;
  484. case FCGI_GET_VALUES:
  485. if (0 != fcon->current_header.requestID) goto error;
  486. if (!read_append_chunk(fcon, fcon->buffer)) goto handle_error;
  487. if (0 == fcon->content_remaining)
  488. parse_get_values(fcon);
  489. break;
  490. case FCGI_GET_VALUES_RESULT:
  491. goto error; /* invalid type */
  492. break;
  493. case FCGI_UNKNOWN_TYPE:
  494. /* we didn't send anything fancy, so this is not expected */
  495. goto error; /* invalid type */
  496. default:
  497. break;
  498. }
  499. }
  500. if (0 == fcon->content_remaining) {
  501. if (0 == fcon->padding_remaining) {
  502. fcon->headerbuf_used = 0;
  503. } else {
  504. if (NULL == (buf = read_chunk(fcon, fcon->padding_remaining))) goto handle_error;
  505. fcon->padding_remaining -= buf->len;
  506. if (0 == fcon->padding_remaining) {
  507. fcon->headerbuf_used = 0;
  508. }
  509. g_byte_array_free(buf, TRUE);
  510. }
  511. }
  512. }
  513. return;
  514. handle_error:
  515. switch (errno) {
  516. case EINTR:
  517. case EAGAIN:
  518. #if EWOULDBLOCK != EAGAIN
  519. case EWOULDBLOCK:
  520. #endif
  521. return; /* try again later */
  522. case ECONNRESET:
  523. break;
  524. default:
  525. ERROR("read from fd=%d failed, %s\n", fcon->fd, g_strerror(errno) );
  526. break;
  527. }
  528. error:
  529. if (0 != fcon->requestID)
  530. fcbs->cb_request_aborted(fcon);
  531. fastcgi_connection_close(fcon);
  532. }
  533. static void fastcgi_connection_fd_cb(struct ev_loop *loop, ev_io *w, int revents) {
  534. fastcgi_connection *fcon = (fastcgi_connection*) w->data;
  535. UNUSED(loop);
  536. if (revents & EV_READ) {
  537. read_queue(fcon);
  538. }
  539. if (revents & EV_WRITE) {
  540. write_queue(fcon);
  541. }
  542. }
  543. static void _g_string_destroy(gpointer data) {
  544. g_string_free(data, TRUE);
  545. }
  546. static fastcgi_connection *fastcgi_connecion_create(fastcgi_server *fsrv, gint fd, guint id) {
  547. fastcgi_connection *fcon = g_slice_new0(fastcgi_connection);
  548. fcon->fsrv = fsrv;
  549. fcon->fcon_id = id;
  550. fcon->buffer = g_byte_array_sized_new(0);
  551. fcon->parambuf = g_byte_array_sized_new(0);
  552. fcon->environ = g_hash_table_new_full((GHashFunc) g_string_hash, (GEqualFunc) g_string_equal, _g_string_destroy, _g_string_destroy);
  553. fcon->fd = fd;
  554. fd_init(fcon->fd);
  555. ev_io_init(&fcon->fd_watcher, fastcgi_connection_fd_cb, fcon->fd, EV_READ);
  556. fcon->fd_watcher.data = fcon;
  557. ev_io_start(fcon->fsrv->loop, &fcon->fd_watcher);
  558. return fcon;
  559. }
  560. static void fastcgi_connection_free(fastcgi_connection *fcon) {
  561. fcon->fsrv->callbacks->cb_reset_connection(fcon);
  562. if (fcon->fd != -1) {
  563. ev_io_stop(fcon->fsrv->loop, &fcon->fd_watcher);
  564. close(fcon->fd);
  565. fcon->fd = -1;
  566. }
  567. fastcgi_queue_clear(&fcon->write_queue);
  568. g_hash_table_destroy(fcon->environ);
  569. g_byte_array_free(fcon->buffer, TRUE);
  570. g_byte_array_free(fcon->parambuf, TRUE);
  571. g_slice_free(fastcgi_connection, fcon);
  572. }
  573. void fastcgi_connection_close(fastcgi_connection *fcon) {
  574. fcon->closing = TRUE;
  575. if (fcon->fd != -1) {
  576. ev_io_stop(fcon->fsrv->loop, &fcon->fd_watcher);
  577. close(fcon->fd);
  578. fcon->fd = -1;
  579. }
  580. fastcgi_queue_clear(&fcon->write_queue);
  581. g_byte_array_set_size(fcon->buffer, 0);
  582. g_byte_array_set_size(fcon->parambuf, 0);
  583. g_hash_table_remove_all(fcon->environ);
  584. ev_prepare_start(fcon->fsrv->loop, &fcon->fsrv->closing_watcher);
  585. }
  586. static void fastcgi_server_fd_cb(struct ev_loop *loop, ev_io *w, int revents) {
  587. fastcgi_server *fsrv = (fastcgi_server*) w->data;
  588. fastcgi_connection *fcon;
  589. void (*cb_new_connection)(fastcgi_connection *fcon) = fsrv->callbacks->cb_new_connection;
  590. g_assert(revents & EV_READ);
  591. for (;;) {
  592. gint fd = accept(fsrv->fd, NULL, NULL);
  593. if (-1 == fd) {
  594. switch (errno) {
  595. case EAGAIN:
  596. #if EWOULDBLOCK != EAGAIN
  597. case EWOULDBLOCK:
  598. #endif
  599. case EINTR:
  600. /* we were stopped _before_ we had a connection */
  601. case ECONNABORTED: /* this is a FreeBSD thingy */
  602. /* we were stopped _after_ we had a connection */
  603. return;
  604. case EMFILE:
  605. if (0 == fsrv->max_connections) {
  606. fsrv->max_connections = fsrv->connections->len / 2;
  607. } else {
  608. fsrv->max_connections = fsrv->max_connections / 2;
  609. }
  610. ERROR("dropped connection limit to %u as we got EMFILE\n", fsrv->max_connections);
  611. ev_io_rem_events(loop, w, EV_READ);
  612. return;
  613. default:
  614. ERROR("accept failed on fd=%d with error: %s\nshutting down\n", fsrv->fd, g_strerror(errno));
  615. fastcgi_server_stop(fsrv);
  616. return;
  617. }
  618. }
  619. fcon = fastcgi_connecion_create(fsrv, fd, fsrv->connections->len);
  620. g_ptr_array_add(fsrv->connections, fcon);
  621. if (cb_new_connection) {
  622. cb_new_connection(fcon);
  623. }
  624. if (fsrv->connections->len >= fsrv->max_connections) {
  625. ev_io_rem_events(loop, w, EV_READ);
  626. return;
  627. }
  628. if (fsrv->do_shutdown) return;
  629. }
  630. }
  631. static void fastcgi_cleanup_connections(fastcgi_server *fsrv) {
  632. guint i;
  633. for (i = 0; i < fsrv->connections->len; ) {
  634. fastcgi_connection *fcon = g_ptr_array_index(fsrv->connections, i);
  635. if (fcon->closing) {
  636. fastcgi_connection *t_fcon;
  637. guint l = fsrv->connections->len-1;
  638. t_fcon = g_ptr_array_index(fsrv->connections, i) = g_ptr_array_index(fsrv->connections, l);
  639. g_ptr_array_set_size(fsrv->connections, l);
  640. t_fcon->fcon_id = i;
  641. fastcgi_connection_free(fcon);
  642. } else {
  643. i++;
  644. }
  645. }
  646. }
  647. static void fastcgi_closing_cb(struct ev_loop *loop, ev_prepare *w, int revents) {
  648. UNUSED(revents);
  649. ev_prepare_stop(loop, w);
  650. fastcgi_cleanup_connections((fastcgi_server*) w->data);
  651. }
  652. fastcgi_server *fastcgi_server_create(struct ev_loop *loop, gint socketfd, const fastcgi_callbacks *callbacks, guint max_connections) {
  653. fastcgi_server *fsrv = g_slice_new0(fastcgi_server);
  654. fsrv->callbacks = callbacks;
  655. fsrv->max_connections = max_connections;
  656. fsrv->connections = g_ptr_array_sized_new(fsrv->max_connections);
  657. fsrv->loop = loop;
  658. fsrv->fd = socketfd;
  659. fd_init(fsrv->fd);
  660. ev_io_init(&fsrv->fd_watcher, fastcgi_server_fd_cb, fsrv->fd, EV_READ);
  661. fsrv->fd_watcher.data = fsrv;
  662. ev_io_start(fsrv->loop, &fsrv->fd_watcher);
  663. ev_prepare_init(&fsrv->closing_watcher, fastcgi_closing_cb);
  664. fsrv->closing_watcher.data = fsrv;
  665. return fsrv;
  666. }
  667. void fastcgi_server_stop(fastcgi_server *fsrv) {
  668. if (fsrv->do_shutdown) return;
  669. fsrv->do_shutdown = TRUE;
  670. ev_io_stop(fsrv->loop, &fsrv->fd_watcher);
  671. close(fsrv->fd);
  672. fsrv->fd = -1;
  673. }
  674. void fastcgi_server_free(fastcgi_server *fsrv) {
  675. guint i;
  676. void (*cb_request_aborted)(fastcgi_connection *fcon) = fsrv->callbacks->cb_request_aborted;
  677. if (!fsrv->do_shutdown) fastcgi_server_stop(fsrv);
  678. ev_prepare_stop(fsrv->loop, &fsrv->closing_watcher);
  679. for (i = 0; i < fsrv->connections->len; i++) {
  680. fastcgi_connection *fcon = g_ptr_array_index(fsrv->connections, i);
  681. cb_request_aborted(fcon);
  682. fcon->closing = TRUE;
  683. }
  684. fastcgi_cleanup_connections(fsrv);
  685. g_ptr_array_free(fsrv->connections, TRUE);
  686. g_slice_free(fastcgi_server, fsrv);
  687. }
  688. void fastcgi_end_request(fastcgi_connection *fcon, gint32 appStatus, enum FCGI_ProtocolStatus status) {
  689. gboolean had_data = (fcon->write_queue.length > 0);
  690. if (0 == fcon->requestID) return;
  691. stream_send_end_request(&fcon->write_queue, fcon->requestID, appStatus, status);
  692. fcon->requestID = 0;
  693. if (!had_data) write_queue(fcon);
  694. }
  695. void fastcgi_suspend_read(fastcgi_connection *fcon) {
  696. fcon->read_suspended = TRUE;
  697. ev_io_rem_events(fcon->fsrv->loop, &fcon->fd_watcher, EV_READ);
  698. }
  699. void fastcgi_resume_read(fastcgi_connection *fcon) {
  700. fcon->read_suspended = FALSE;
  701. ev_io_add_events(fcon->fsrv->loop, &fcon->fd_watcher, EV_READ);
  702. }
  703. void fastcgi_send_out(fastcgi_connection *fcon, GString *data) {
  704. gboolean had_data = (fcon->write_queue.length > 0);
  705. if (!data) {
  706. stream_send_fcgi_record(&fcon->write_queue, FCGI_STDOUT, fcon->requestID, 0);
  707. } else {
  708. stream_send_string(&fcon->write_queue, FCGI_STDOUT, fcon->requestID, data);
  709. }
  710. if (!had_data) write_queue(fcon);
  711. }
  712. void fastcgi_send_err(fastcgi_connection *fcon, GString *data) {
  713. gboolean had_data = (fcon->write_queue.length > 0);
  714. if (!data) {
  715. stream_send_fcgi_record(&fcon->write_queue, FCGI_STDERR, fcon->requestID, 0);
  716. } else {
  717. stream_send_string(&fcon->write_queue, FCGI_STDERR, fcon->requestID, data);
  718. }
  719. if (!had_data) write_queue(fcon);
  720. }
  721. void fastcgi_send_out_bytearray(fastcgi_connection *fcon, GByteArray *data) {
  722. gboolean had_data = (fcon->write_queue.length > 0);
  723. if (!data) {
  724. stream_send_fcgi_record(&fcon->write_queue, FCGI_STDOUT, fcon->requestID, 0);
  725. } else {
  726. stream_send_bytearray(&fcon->write_queue, FCGI_STDOUT, fcon->requestID, data);
  727. }
  728. if (!had_data) write_queue(fcon);
  729. }
  730. void fastcgi_send_err_bytearray(fastcgi_connection *fcon, GByteArray *data) {
  731. gboolean had_data = (fcon->write_queue.length > 0);
  732. if (!data) {
  733. stream_send_fcgi_record(&fcon->write_queue, FCGI_STDERR, fcon->requestID, 0);
  734. } else {
  735. stream_send_bytearray(&fcon->write_queue, FCGI_STDERR, fcon->requestID, data);
  736. }
  737. if (!had_data) write_queue(fcon);
  738. }
  739. char** fastcgi_build_env(fastcgi_connection *con) {
  740. GPtrArray *env = g_ptr_array_new();
  741. GHashTableIter iter;
  742. gpointer pkey, pvalue;
  743. g_hash_table_iter_init(&iter, con->environ);
  744. while (g_hash_table_iter_next(&iter, &pkey, &pvalue)) {
  745. GString *key = pkey, *value = pvalue;
  746. char *s = g_malloc(key->len + value->len + 2);
  747. memcpy(s, key->str, key->len);
  748. memcpy(s + key->len + 1, value->str, value->len);
  749. s[key->len] = '=';
  750. s[key->len + value->len + 1] = '\0';
  751. g_ptr_array_add(env, s);
  752. }
  753. g_ptr_array_add(env, NULL);
  754. return (char**) g_ptr_array_free(env, FALSE);
  755. }
  756. const gchar* fastcgi_connection_environ_lookup(fastcgi_connection *fcon, const gchar* key, gsize keylen) {
  757. GString s = { (gchar*) key, keylen, 0 };
  758. GString *value = g_hash_table_lookup(fcon->environ, &s);
  759. return (NULL != value) ? value->str : NULL;
  760. }