
/*
 * Copyright (C) OpenResty Inc.
 */


#ifndef DDEBUG
#define DDEBUG 0
#endif
#include "ddebug.h"


#include <nginx.h>
#include "ngx_stream_lua_prereadby.h"
#include "ngx_stream_lua_util.h"
#include "ngx_stream_lua_exception.h"
#include "ngx_stream_lua_cache.h"


static ngx_int_t ngx_stream_lua_preread_by_chunk(lua_State *L,
    ngx_stream_lua_request_t *r);


ngx_int_t
ngx_stream_lua_preread_handler(ngx_stream_session_t *s)
{
    ngx_int_t                     rc;
    ngx_stream_lua_ctx_t         *ctx;
    ngx_stream_lua_srv_conf_t    *lscf;
    ngx_stream_lua_main_conf_t   *lmcf;
    ngx_stream_lua_request_t     *r;

    ngx_log_debug0(NGX_LOG_DEBUG_STREAM, s->connection->log, 0,
                   "lua preread handler");

    lmcf = ngx_stream_get_module_main_conf(s, ngx_stream_lua_module);

    if (!lmcf->postponed_to_preread_phase_end) {
        ngx_stream_core_main_conf_t       *cmcf;
        ngx_stream_phase_handler_t         tmp;
        ngx_stream_phase_handler_t        *ph;
        ngx_stream_phase_handler_t        *cur_ph;
        ngx_stream_phase_handler_t        *last_ph;

        lmcf->postponed_to_preread_phase_end = 1;

        cmcf = ngx_stream_get_module_main_conf(s, ngx_stream_core_module);

        ph = cmcf->phase_engine.handlers;
        cur_ph = &ph[s->phase_handler];
        last_ph = &ph[cur_ph->next - 1];

        if (cur_ph < last_ph) {
            tmp      = *cur_ph;

            ngx_memmove(cur_ph, cur_ph + 1, (last_ph - cur_ph)
                        * sizeof (ngx_stream_phase_handler_t));

            *last_ph = tmp;

            s->phase_handler--; /* redo the current ph */

            return NGX_DECLINED;
        }
    }

    lscf = ngx_stream_get_module_srv_conf(s, ngx_stream_lua_module);

    if (lscf->preread_handler == NULL) {
        ngx_log_debug0(NGX_LOG_DEBUG_STREAM, s->connection->log, 0,
                       "no preread handler found");
        return NGX_DECLINED;
    }

    ctx = ngx_stream_get_module_ctx(s, ngx_stream_lua_module);

    dd("ctx = %p", ctx);

    if (ctx == NULL) {
        ctx = ngx_stream_lua_create_ctx(s);
        if (ctx == NULL) {
            return NGX_STREAM_INTERNAL_SERVER_ERROR;
        }
    }

    r = ctx->request;

    dd("entered? %d", (int) ctx->entered_preread_phase);

    if (ctx->entered_preread_phase) {
        dd("calling wev handler");
        rc = ctx->resume_handler(r);
        dd("wev handler returns %d", (int) rc);

        if (rc == NGX_ERROR || rc > NGX_OK) {
            ngx_stream_lua_finalize_request(ctx->request, rc);
            return NGX_DONE;
        }

        if (rc == NGX_DONE && ctx->peek_needs_more_data) {
            return NGX_AGAIN;
        }

        if (rc == NGX_OK || rc == NGX_DONE) {
            return rc;
        }

        return NGX_DECLINED;
    }

    r->connection->read->handler = ngx_stream_lua_request_handler;
    r->connection->write->handler = ngx_stream_lua_request_handler;

    dd("calling preread handler");
    rc = lscf->preread_handler(r);

    if (rc == NGX_ERROR || rc > NGX_OK) {
        ngx_stream_lua_finalize_request(ctx->request, rc);
        return NGX_DONE;
    }

    return rc;
}


ngx_int_t
ngx_stream_lua_preread_handler_inline(ngx_stream_lua_request_t *r)
{
    ngx_int_t                    rc;
    lua_State                   *L;
    ngx_stream_lua_srv_conf_t   *lscf;

    lscf = ngx_stream_lua_get_module_srv_conf(r, ngx_stream_lua_module);

    L = ngx_stream_lua_get_lua_vm(r, NULL);

    /*  load Lua inline script (w/ cache) sp = 1 */
    rc = ngx_stream_lua_cache_loadbuffer(r->connection->log, L,
                                         lscf->preread_src.value.data,
                                         lscf->preread_src.value.len,
                                         lscf->preread_src_key,
                                         (const char *)
                                         lscf->preread_chunkname);

    if (rc != NGX_OK) {
        return NGX_STREAM_INTERNAL_SERVER_ERROR;
    }

    return ngx_stream_lua_preread_by_chunk(L, r);
}


ngx_int_t
ngx_stream_lua_preread_handler_file(ngx_stream_lua_request_t *r)
{
    u_char                      *script_path;
    ngx_int_t                    rc;
    ngx_str_t                    eval_src;
    lua_State                   *L;
    ngx_stream_lua_srv_conf_t   *lscf;

    lscf = ngx_stream_lua_get_module_srv_conf(r, ngx_stream_lua_module);

    /* Eval nginx variables in code path string first */
    if (ngx_stream_complex_value(r->session, &lscf->preread_src, &eval_src)
        != NGX_OK)
    {
        return NGX_ERROR;
    }

    script_path = ngx_stream_lua_rebase_path(r->pool, eval_src.data,
                                             eval_src.len);

    if (script_path == NULL) {
        return NGX_ERROR;
    }

    L = ngx_stream_lua_get_lua_vm(r, NULL);

    /*  load Lua script file (w/ cache)        sp = 1 */
    rc = ngx_stream_lua_cache_loadfile(r->connection->log, L, script_path,
                                       lscf->preread_src_key);
    if (rc != NGX_OK) {
        return rc;
    }

    /*  make sure we have a valid code chunk */
    ngx_stream_lua_assert(lua_isfunction(L, -1));

    return ngx_stream_lua_preread_by_chunk(L, r);
}


static ngx_int_t
ngx_stream_lua_preread_by_chunk(lua_State *L, ngx_stream_lua_request_t *r)
{
    int                          co_ref;
    ngx_int_t                    rc;
    lua_State                   *co;
    ngx_event_t                 *rev;
    ngx_connection_t            *c;
    ngx_stream_lua_ctx_t        *ctx;
    ngx_stream_lua_cleanup_t    *cln;

    ngx_stream_lua_srv_conf_t     *lscf;

    /*  {{{ new coroutine to handle request */
    co = ngx_stream_lua_new_thread(r, L, &co_ref);

    if (co == NULL) {
        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
                      "lua: failed to create new coroutine "
                      "to handle request");

        return NGX_STREAM_INTERNAL_SERVER_ERROR;
    }

    /*  move code closure to new coroutine */
    lua_xmove(L, co, 1);

#ifndef OPENRESTY_LUAJIT
    /*  set closure's env table to new coroutine's globals table */
    ngx_stream_lua_get_globals_table(co);
    lua_setfenv(co, -2);
#endif

    /*  save nginx request in coroutine globals table */
    ngx_stream_lua_set_req(co, r);

    /*  {{{ initialize request context */
    ctx = ngx_stream_lua_get_module_ctx(r, ngx_stream_lua_module);

    dd("ctx = %p", ctx);

    if (ctx == NULL) {
        return NGX_ERROR;
    }

    ngx_stream_lua_reset_ctx(r, L, ctx);

    ctx->entered_preread_phase = 1;

    ctx->cur_co_ctx = &ctx->entry_co_ctx;
    ctx->cur_co_ctx->co = co;
    ctx->cur_co_ctx->co_ref = co_ref;
#ifdef NGX_LUA_USE_ASSERT
    ctx->cur_co_ctx->co_top = 1;
#endif

    ngx_stream_lua_attach_co_ctx_to_L(co, ctx->cur_co_ctx);

    /*  }}} */

    /*  {{{ register request cleanup hooks */
    if (ctx->cleanup == NULL) {
        cln = ngx_stream_lua_cleanup_add(r, 0);
        if (cln == NULL) {
            return NGX_STREAM_INTERNAL_SERVER_ERROR;
        }

        cln->handler = ngx_stream_lua_request_cleanup_handler;
        cln->data = ctx;
        ctx->cleanup = &cln->handler;
    }
    /*  }}} */

    ctx->context = NGX_STREAM_LUA_CONTEXT_PREREAD;

    lscf = ngx_stream_lua_get_module_srv_conf(r, ngx_stream_lua_module);

    if (lscf->check_client_abort) {
        r->read_event_handler = ngx_stream_lua_rd_check_broken_connection;

        rev = r->connection->read;

        if (!rev->active) {
            if (ngx_add_event(rev, NGX_READ_EVENT, 0) != NGX_OK) {
                return NGX_ERROR;
            }
        }

    } else {
        r->read_event_handler = ngx_stream_lua_block_reading;
    }

    rc = ngx_stream_lua_run_thread(L, r, ctx, 0);

    ngx_log_debug1(NGX_LOG_DEBUG_STREAM, r->connection->log, 0,
                   "preread run thread returned %d", (int) rc);

    if (rc == NGX_ERROR || rc > NGX_OK) {
        return rc;
    }

    c = r->connection;

    if (rc == NGX_AGAIN) {
        rc = ngx_stream_lua_run_posted_threads(c, L, r, ctx, 0);

        if (rc == NGX_DONE && ctx->peek_needs_more_data) {
            return NGX_AGAIN;
        }

        if (rc == NGX_ERROR || rc == NGX_DONE || rc > NGX_OK) {
            return rc;
        }

        if (rc != NGX_OK) {
            return NGX_DECLINED;
        }

    } else if (rc == NGX_DONE) {
        ngx_stream_lua_finalize_request(r, NGX_DONE);

        rc = ngx_stream_lua_run_posted_threads(c, L, r, ctx, 0);

        if (rc == NGX_ERROR || rc == NGX_DONE || rc > NGX_OK) {
            return rc;
        }

        if (rc != NGX_OK) {
            return NGX_DECLINED;
        }
    }

#if 1
    if (rc == NGX_OK) {
        return NGX_OK;
    }
#endif

    return NGX_DECLINED;
}

/* vi:set ft=c ts=4 sw=4 et fdm=marker: */
