text: /* vim:set ft=c ts=4 sw=4 et fdm=marker: */
  
  #ifndef DDEBUG
  #define DDEBUG 0
  #endif
  #include "ddebug.h"
  
  #include <nginx.h>
  #include "ngx_http_lua_contentby.h"
  #include "ngx_http_lua_util.h"
  #include "ngx_http_lua_exception.h"
  #include "ngx_http_lua_cache.h"
  
  
  static void ngx_http_lua_content_phase_post_read(ngx_http_request_t *r);
  
  
  ngx_int_t
  ngx_http_lua_content_by_chunk(lua_State *L, ngx_http_request_t *r)
  {
      int                      cc_ref;
      lua_State               *cc;
      ngx_http_lua_ctx_t      *ctx;
      ngx_http_cleanup_t      *cln;
  
      dd("content by chunk");
  
      ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module);
  
      if (ctx == NULL) {
          ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_lua_ctx_t));
          if (ctx == NULL) {
              return NGX_HTTP_INTERNAL_SERVER_ERROR;
          }
  
          dd("setting new ctx, ctx = %p", ctx);
  
          ctx->cc_ref = LUA_NOREF;
          ctx->ctx_ref = LUA_NOREF;
  
          ngx_http_set_ctx(r, ctx, ngx_http_lua_module);
  
      } else {
          dd("reset ctx");
          ngx_http_lua_reset_ctx(r, L, ctx);
      }
  
      ctx->entered_content_phase = 1;
  
      /*  {{{ new coroutine to handle request */
      cc = ngx_http_lua_new_thread(r, L, &cc_ref);
  
      if (cc == NULL) {
          ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
                  "(lua-content-by-chunk) failed to create new coroutine "
                  "to handle request");
  
          return NGX_HTTP_INTERNAL_SERVER_ERROR;
      }
  
      /*  move code closure to new coroutine */
      lua_xmove(L, cc, 1);
  
      /*  set closure's env table to new coroutine's globals table */
      lua_pushvalue(cc, LUA_GLOBALSINDEX);
      lua_setfenv(cc, -2);
  
      /*  save reference of code to ease forcing stopping */
      lua_pushvalue(cc, -1);
      lua_setglobal(cc, GLOBALS_SYMBOL_RUNCODE);
  
      /*  save nginx request in coroutine globals table */
      lua_pushlightuserdata(cc, r);
      lua_setglobal(cc, GLOBALS_SYMBOL_REQUEST);
      /*  }}} */
  
      ctx->cc = cc;
      ctx->cc_ref = cc_ref;
  
      /*  {{{ register request cleanup hooks */
      if (ctx->cleanup == NULL) {
          cln = ngx_http_cleanup_add(r, 0);
          if (cln == NULL) {
              return NGX_HTTP_INTERNAL_SERVER_ERROR;
          }
  
          cln->handler = ngx_http_lua_request_cleanup;
          cln->data = r;
          ctx->cleanup = &cln->handler;
      }
      /*  }}} */
  
      return ngx_http_lua_run_thread(L, r, ctx, 0);
  }
  
  
  void
  ngx_http_lua_content_wev_handler(ngx_http_request_t *r)
  {
      (void) ngx_http_lua_wev_handler(r);
  }
  
  
  ngx_int_t
  ngx_http_lua_content_handler(ngx_http_request_t *r)
  {
      ngx_http_lua_loc_conf_t     *llcf;
      ngx_http_lua_ctx_t          *ctx;
      ngx_int_t                    rc;
  
      llcf = ngx_http_get_module_loc_conf(r, ngx_http_lua_module);
  
      if (llcf->content_handler == NULL) {
          dd("no content handler found");
          return NGX_DECLINED;
      }
  
      ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module);
  
      dd("ctx = %p", ctx);
  
      if (ctx == NULL) {
          ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_lua_ctx_t));
          if (ctx == NULL) {
              return NGX_HTTP_INTERNAL_SERVER_ERROR;
          }
  
          dd("setting new ctx: ctx = %p", ctx);
  
          ctx->cc_ref = LUA_NOREF;
          ctx->ctx_ref = LUA_NOREF;
  
          ngx_http_set_ctx(r, ctx, ngx_http_lua_module);
      }
  
      dd("entered? %d", (int) ctx->entered_content_phase);
  
      if (ctx->waiting_more_body) {
          return NGX_DONE;
      }
  
      if (ctx->entered_content_phase) {
          dd("calling wev handler");
          rc = ngx_http_lua_wev_handler(r);
          dd("wev handler returns %d", (int) rc);
          return rc;
      }
  
      if (llcf->force_read_body && !ctx->read_body_done) {
          r->request_body_in_single_buf = 1;
          r->request_body_in_persistent_file = 1;
          r->request_body_in_clean_file = 1;
  
          rc = ngx_http_read_client_request_body(r,
                  ngx_http_lua_content_phase_post_read);
  
          if (rc == NGX_ERROR || rc >= NGX_HTTP_SPECIAL_RESPONSE) {
              return rc;
          }
  
          if (rc == NGX_AGAIN) {
              ctx->waiting_more_body = 1;
  
              return NGX_DONE;
          }
      }
  
      dd("setting entered");
  
      ctx->entered_content_phase = 1;
  
      dd("calling content handler");
      return llcf->content_handler(r);
  }
  
  
  /* post read callback for the content phase */
  static void
  ngx_http_lua_content_phase_post_read(ngx_http_request_t *r)
  {
      ngx_http_lua_ctx_t  *ctx;
  
      ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module);
  
      ctx->read_body_done = 1;
  
      if (ctx->waiting_more_body) {
          ctx->waiting_more_body = 0;
          ngx_http_finalize_request(r, ngx_http_lua_content_handler(r));
  
      } else {
  #if defined(nginx_version) && nginx_version >= 8011
          r->main->count--;
  #endif
      }
  }
  
  
  ngx_int_t
  ngx_http_lua_content_handler_file(ngx_http_request_t *r)
  {
      lua_State                       *L;
      ngx_int_t                        rc;
      u_char                          *script_path;
      ngx_http_lua_main_conf_t        *lmcf;
      ngx_http_lua_loc_conf_t         *llcf;
      char                            *err;
      ngx_str_t                        eval_src;
  
      llcf = ngx_http_get_module_loc_conf(r, ngx_http_lua_module);
  
      if (ngx_http_complex_value(r, &llcf->content_src, &eval_src) != NGX_OK) {
          return NGX_ERROR;
      }
  
      script_path = ngx_http_lua_rebase_path(r->pool, eval_src.data,
              eval_src.len);
  
      if (script_path == NULL) {
          return NGX_ERROR;
      }
  
      lmcf = ngx_http_get_module_main_conf(r, ngx_http_lua_module);
      L = lmcf->lua;
  
      /*  load Lua script file (w/ cache)        sp = 1 */
      rc = ngx_http_lua_cache_loadfile(L, script_path, llcf->content_src_key,
              &err, llcf->enable_code_cache ? 1 : 0);
  
      if (rc != NGX_OK) {
          if (err == NULL) {
              err = "unknown error";
          }
  
          ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
                  "Failed to load Lua inlined code: %s", err);
  
          return NGX_HTTP_INTERNAL_SERVER_ERROR;
      }
  
      /*  make sure we have a valid code chunk */
      assert(lua_isfunction(L, -1));
  
      rc = ngx_http_lua_content_by_chunk(L, r);
  
      if (rc == NGX_ERROR || rc >= NGX_HTTP_SPECIAL_RESPONSE) {
          return rc;
      }
  
      if (rc == NGX_DONE) {
          return NGX_DONE;
      }
  
      if (rc == NGX_AGAIN) {
  #if defined(nginx_version) && nginx_version >= 8011
          r->main->count++;
  #endif
          return NGX_DONE;
      }
  
      return NGX_OK;
  }
  
  
  ngx_int_t
  ngx_http_lua_content_handler_inline(ngx_http_request_t *r)
  {
      lua_State                   *L;
      ngx_int_t                    rc;
      ngx_http_lua_main_conf_t    *lmcf;
      ngx_http_lua_loc_conf_t     *llcf;
      char                        *err;
  
      llcf = ngx_http_get_module_loc_conf(r, ngx_http_lua_module);
      lmcf = ngx_http_get_module_main_conf(r, ngx_http_lua_module);
  
      L = lmcf->lua;
  
      /*  load Lua inline script (w/ cache) sp = 1 */
      rc = ngx_http_lua_cache_loadbuffer(L, llcf->content_src.value.data,
              llcf->content_src.value.len, llcf->content_src_key,
              "content_by_lua", &err, llcf->enable_code_cache ? 1 : 0);
  
      if (rc != NGX_OK) {
          if (err == NULL) {
              err = "unknown error";
          }
  
          ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
                  "Failed to load Lua inlined code: %s", err);
  
          return NGX_HTTP_INTERNAL_SERVER_ERROR;
      }
  
      rc = ngx_http_lua_content_by_chunk(L, r);
  
      if (rc == NGX_ERROR || rc >= NGX_HTTP_SPECIAL_RESPONSE) {
          return rc;
      }
  
      if (rc == NGX_DONE) {
          return NGX_DONE;
      }
  
      if (rc == NGX_AGAIN) {
  #if defined(nginx_version) && nginx_version >= 8011
          r->main->count++;
  #endif
          return NGX_DONE;
      }
  
      return NGX_OK;
  }
  
  