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.
 
 
 
 
 
 

310 lines
8.0 KiB

  1. /*
  2. * mod_scgi - connect to SCGI backends for generating response content
  3. *
  4. * Author:
  5. * Copyright (c) 2013 Stefan Bühler
  6. */
  7. #include <lighttpd/base.h>
  8. #include <lighttpd/plugin_core.h>
  9. #include <lighttpd/backends.h>
  10. #include <lighttpd/stream_http_response.h>
  11. LI_API gboolean mod_scgi_init(liModules *mods, liModule *mod);
  12. LI_API gboolean mod_scgi_free(liModules *mods, liModule *mod);
  13. typedef struct scgi_connection scgi_connection;
  14. typedef struct scgi_context scgi_context;
  15. struct scgi_context {
  16. gint refcount;
  17. liBackendPool *pool;
  18. GString *socket_str;
  19. };
  20. struct scgi_connection {
  21. scgi_context *ctx;
  22. liBackendConnection *bcon;
  23. gpointer simple_socket_data;
  24. };
  25. /**********************************************************************************/
  26. static gboolean append_key_value_pair(GByteArray *a, const gchar *key, size_t keylen, const gchar *val, size_t valuelen) {
  27. const guint8 z = 0;
  28. g_byte_array_append(a, (const guint8*) key, keylen);
  29. g_byte_array_append(a, &z, 1);
  30. g_byte_array_append(a, (const guint8*) val, valuelen);
  31. g_byte_array_append(a, &z, 1);
  32. return TRUE;
  33. }
  34. static void cgi_add_cb(gpointer param, const gchar *key, size_t keylen, const gchar *val, size_t valuelen) {
  35. GByteArray *a = (GByteArray*) param;
  36. append_key_value_pair(a, key, keylen, val, valuelen);
  37. }
  38. static void scgi_send_env(liVRequest *vr, liChunkQueue *out) {
  39. GByteArray *buf = g_byte_array_sized_new(0);
  40. liEnvironmentDup *envdup;
  41. GString *tmp = vr->wrk->tmp_str;
  42. GString *env_scgi_value;
  43. g_assert(vr->request.content_length >= 0);
  44. envdup = li_environment_make_dup(&vr->env);
  45. env_scgi_value = li_environment_dup_pop(envdup, CONST_STR_LEN("SCGI"));
  46. li_environment_dup2cgi(vr, envdup, cgi_add_cb, buf);
  47. if (NULL != env_scgi_value) {
  48. append_key_value_pair(buf, CONST_STR_LEN("SCGI"), GSTR_LEN(env_scgi_value));
  49. } else {
  50. append_key_value_pair(buf, CONST_STR_LEN("SCGI"), CONST_STR_LEN("1"));
  51. }
  52. g_string_printf(tmp, "%u:", buf->len);
  53. li_chunkqueue_append_mem(out, GSTR_LEN(tmp));
  54. {
  55. const guint8 c = ',';
  56. g_byte_array_append(buf, &c, 1);
  57. }
  58. li_chunkqueue_append_bytearr(out, buf);
  59. }
  60. /**********************************************************************************/
  61. static void scgi_backend_free(liBackendPool *bpool) {
  62. liBackendConfig *config = (liBackendConfig*) bpool->config;
  63. li_sockaddr_clear(&config->sock_addr);
  64. g_slice_free(liBackendConfig, config);
  65. }
  66. static liBackendCallbacks scgi_backend_cbs = {
  67. /* backend_detach_thread */ NULL,
  68. /* backend_attach_thread */ NULL,
  69. /* backend_new */ NULL,
  70. /* backend_close */ NULL,
  71. scgi_backend_free
  72. };
  73. static scgi_context* scgi_context_new(liServer *srv, GString *dest_socket) {
  74. liSocketAddress saddr;
  75. scgi_context* ctx;
  76. liBackendConfig *config;
  77. saddr = li_sockaddr_from_string(dest_socket, 0);
  78. if (NULL == saddr.addr) {
  79. ERROR(srv, "Invalid socket address '%s'", dest_socket->str);
  80. return NULL;
  81. }
  82. config = g_slice_new0(liBackendConfig);
  83. config->callbacks = &scgi_backend_cbs;
  84. config->sock_addr = saddr;
  85. config->max_connections = 0;
  86. config->idle_timeout = 5;
  87. config->connect_timeout = 5;
  88. config->wait_timeout = 5;
  89. config->disable_time = 0;
  90. config->max_requests = 1;
  91. config->watch_for_close = TRUE;
  92. ctx = g_slice_new0(scgi_context);
  93. ctx->refcount = 1;
  94. ctx->pool = li_backend_pool_new(config);
  95. ctx->socket_str = g_string_new_len(GSTR_LEN(dest_socket));
  96. return ctx;
  97. }
  98. static void scgi_context_release(scgi_context *ctx) {
  99. if (!ctx) return;
  100. LI_FORCE_ASSERT(g_atomic_int_get(&ctx->refcount) > 0);
  101. if (g_atomic_int_dec_and_test(&ctx->refcount)) {
  102. li_backend_pool_free(ctx->pool);
  103. g_string_free(ctx->socket_str, TRUE);
  104. g_slice_free(scgi_context, ctx);
  105. }
  106. }
  107. static void scgi_context_acquire(scgi_context *ctx) {
  108. LI_FORCE_ASSERT(g_atomic_int_get(&ctx->refcount) > 0);
  109. g_atomic_int_inc(&ctx->refcount);
  110. }
  111. static void scgi_io_cb(liIOStream *stream, liIOStreamEvent event) {
  112. scgi_connection *con = stream->data;
  113. liWorker *wrk = li_worker_from_iostream(stream);
  114. li_stream_simple_socket_io_cb_with_context(stream, event, &con->simple_socket_data);
  115. switch (event) {
  116. case LI_IOSTREAM_DESTROY:
  117. li_stream_simple_socket_close(stream, FALSE);
  118. li_event_io_set_fd(&con->bcon->watcher, -1);
  119. li_backend_put(wrk, con->ctx->pool, con->bcon, TRUE);
  120. con->bcon = NULL;
  121. scgi_context_release(con->ctx);
  122. g_slice_free(scgi_connection, con);
  123. stream->data = NULL;
  124. return;
  125. default:
  126. break;
  127. }
  128. if ((NULL == stream->stream_in.out || stream->stream_in.out->is_closed) &&
  129. !(NULL == stream->stream_out.out || stream->stream_out.out->is_closed)) {
  130. stream->stream_out.out->is_closed = TRUE;
  131. li_stream_again_later(&stream->stream_out);
  132. }
  133. }
  134. static void scgi_connection_new(liVRequest *vr, liBackendConnection *bcon, scgi_context *ctx) {
  135. scgi_connection* scon = g_slice_new0(scgi_connection);
  136. liIOStream *iostream;
  137. liStream *outplug;
  138. liStream *http_out;
  139. scgi_context_acquire(ctx);
  140. scon->ctx = ctx;
  141. scon->bcon = bcon;
  142. iostream = li_iostream_new(vr->wrk, li_event_io_fd(&bcon->watcher), scgi_io_cb, scon);
  143. /* insert scgi header before actual data */
  144. outplug = li_stream_plug_new(&vr->wrk->loop);
  145. li_stream_connect(outplug, &iostream->stream_out);
  146. scgi_send_env(vr, outplug->out);
  147. li_stream_notify_later(outplug);
  148. http_out = li_stream_http_response_handle(&iostream->stream_in, vr, TRUE, FALSE, FALSE);
  149. li_vrequest_handle_indirect(vr, NULL);
  150. li_vrequest_indirect_connect(vr, outplug, http_out);
  151. li_iostream_release(iostream);
  152. li_stream_release(outplug);
  153. li_stream_release(http_out);
  154. }
  155. /**********************************************************************************/
  156. static liHandlerResult scgi_handle_abort(liVRequest *vr, gpointer param, gpointer context) {
  157. scgi_context *ctx = (scgi_context*) param;
  158. liBackendWait *bwait = context;
  159. if (bwait != NULL) {
  160. li_backend_wait_stop(vr, ctx->pool, &bwait);
  161. }
  162. return LI_HANDLER_GO_ON;
  163. }
  164. static liHandlerResult scgi_handle(liVRequest *vr, gpointer param, gpointer *context) {
  165. liBackendWait *bwait = (liBackendWait*) *context;
  166. liBackendConnection *bcon = NULL;
  167. scgi_context *ctx = (scgi_context*) param;
  168. liBackendResult bres;
  169. if (li_vrequest_is_handled(vr)) return LI_HANDLER_GO_ON;
  170. LI_VREQUEST_WAIT_FOR_REQUEST_BODY(vr);
  171. if (vr->request.content_length < 0) {
  172. VR_ERROR(vr, "%s", "scgi can't handle progressive uploads. enable request body buffering!");
  173. return LI_HANDLER_ERROR;
  174. }
  175. bres = li_backend_get(vr, ctx->pool, &bcon, &bwait);
  176. *context = bwait;
  177. switch (bres) {
  178. case LI_BACKEND_SUCCESS:
  179. LI_FORCE_ASSERT(NULL == bwait);
  180. LI_FORCE_ASSERT(NULL != bcon);
  181. break;
  182. case LI_BACKEND_WAIT:
  183. LI_FORCE_ASSERT(NULL != bwait);
  184. return LI_HANDLER_WAIT_FOR_EVENT;
  185. case LI_BACKEND_TIMEOUT:
  186. li_vrequest_backend_dead(vr);
  187. return LI_HANDLER_GO_ON;
  188. }
  189. scgi_connection_new(vr, bcon, ctx);
  190. return LI_HANDLER_GO_ON;
  191. }
  192. static void scgi_free(liServer *srv, gpointer param) {
  193. scgi_context *ctx = (scgi_context*) param;
  194. UNUSED(srv);
  195. scgi_context_release(ctx);
  196. }
  197. static liAction* scgi_create(liServer *srv, liWorker *wrk, liPlugin* p, liValue *val, gpointer userdata) {
  198. scgi_context *ctx;
  199. UNUSED(wrk); UNUSED(userdata); UNUSED(p);
  200. val = li_value_get_single_argument(val);
  201. if (LI_VALUE_STRING != li_value_type(val)) {
  202. ERROR(srv, "%s", "scgi expects a string as parameter");
  203. return FALSE;
  204. }
  205. ctx = scgi_context_new(srv, val->data.string);
  206. if (NULL == ctx) return NULL;
  207. return li_action_new_function(scgi_handle, scgi_handle_abort, scgi_free, ctx);
  208. }
  209. static const liPluginOption options[] = {
  210. { NULL, 0, 0, NULL }
  211. };
  212. static const liPluginAction actions[] = {
  213. { "scgi", scgi_create, NULL },
  214. { NULL, NULL, NULL }
  215. };
  216. static const liPluginSetup setups[] = {
  217. { NULL, NULL, NULL }
  218. };
  219. static void plugin_init(liServer *srv, liPlugin *p, gpointer userdata) {
  220. UNUSED(srv); UNUSED(userdata);
  221. p->options = options;
  222. p->actions = actions;
  223. p->setups = setups;
  224. }
  225. gboolean mod_scgi_init(liModules *mods, liModule *mod) {
  226. MODULE_VERSION_CHECK(mods);
  227. mod->config = li_plugin_register(mods->main, "mod_scgi", plugin_init, NULL);
  228. return mod->config != NULL;
  229. }
  230. gboolean mod_scgi_free(liModules *mods, liModule *mod) {
  231. if (mod->config)
  232. li_plugin_free(mods->main, mod->config);
  233. return TRUE;
  234. }