Example #1
0
static int
ts_lua_schedule_handler(TSCont contp, TSEvent ev, void *edata)
{
  lua_State *L;
  ts_lua_cont_info *ci;
  ts_lua_coroutine *crt;
  int event, n, ret;
  ts_lua_http_ctx *actx;
  ts_lua_main_ctx *main_ctx;

  event = (int)ev;
  TSDebug(TS_LUA_DEBUG_TAG, "getting actx and other info");
  actx = (ts_lua_http_ctx *)TSContDataGet(contp);

  TSDebug(TS_LUA_DEBUG_TAG, "getting http_Ctx");
  ci = &actx->cinfo;
  crt = &ci->routine;

  main_ctx = crt->mctx;
  L = crt->lua;

  ret = 0;

  TSMutexLock(main_ctx->mutexp);
  ts_lua_set_cont_info(L, ci);

  if (event == TS_LUA_EVENT_COROUTINE_CONT) {
    TSDebug(TS_LUA_DEBUG_TAG, "event is coroutine_cont");
    n = (intptr_t)edata;
    ret = lua_resume(L, n);
  } else {
    TSDebug(TS_LUA_DEBUG_TAG, "event is not coroutine_cont");
    n = lua_gettop(L);
    ret = lua_resume(L, n - 1);
  }

  if (ret == LUA_YIELD) {
    TSMutexUnlock(main_ctx->mutexp);
    goto done;
  }

  if (ret != 0) {
    TSError("[ts_lua] lua_resume failed: %s", lua_tostring(L, -1));
  }

  lua_pop(L, lua_gettop(L));
  TSMutexUnlock(main_ctx->mutexp);
  ts_lua_destroy_async_ctx(actx);

done:
  return 0;
}
static int
ts_lua_transform_handler(TSCont contp, ts_lua_http_transform_ctx *transform_ctx, TSEvent event, int n)
{
  TSVConn output_conn;
  TSVIO input_vio;
  TSIOBufferReader input_reader;
  TSIOBufferBlock blk;
  int64_t toread, towrite, blk_len, upstream_done, input_avail, input_wm_bytes, l;
  const char *start;
  const char *res;
  size_t res_len;
  int ret, eos, write_down, rc, top, empty_input;
  ts_lua_coroutine *crt;
  ts_lua_cont_info *ci;

  lua_State *L;
  TSMutex mtxp;

  ci  = &transform_ctx->cinfo;
  crt = &ci->routine;

  mtxp = crt->mctx->mutexp;
  L    = crt->lua;

  output_conn = TSTransformOutputVConnGet(contp);
  input_vio   = TSVConnWriteVIOGet(contp);

  empty_input = 0;
  if (!TSVIOBufferGet(input_vio)) {
    if (transform_ctx->output.vio) {
      TSDebug(TS_LUA_DEBUG_TAG, "[%s] reenabling output VIO after input VIO does not exist", __FUNCTION__);
      TSVIONBytesSet(transform_ctx->output.vio, transform_ctx->total);
      TSVIOReenable(transform_ctx->output.vio);
      return 0;
    } else {
      TSDebug(TS_LUA_DEBUG_TAG, "[%s] no input VIO and output VIO", __FUNCTION__);
      empty_input = 1;
    }
  } else { // input VIO exists
    input_wm_bytes = TSIOBufferWaterMarkGet(TSVIOBufferGet(input_vio));
    if (transform_ctx->upstream_watermark_bytes >= 0 && transform_ctx->upstream_watermark_bytes != input_wm_bytes) {
      TSDebug(TS_LUA_DEBUG_TAG, "[%s] Setting input_vio watermark to %" PRId64 " bytes", __FUNCTION__,
              transform_ctx->upstream_watermark_bytes);
      TSIOBufferWaterMarkSet(TSVIOBufferGet(input_vio), transform_ctx->upstream_watermark_bytes);
    }
  }

  if (empty_input == 0) {
    input_reader = TSVIOReaderGet(input_vio);
  }

  if (!transform_ctx->output.buffer) {
    transform_ctx->output.buffer = TSIOBufferCreate();
    transform_ctx->output.reader = TSIOBufferReaderAlloc(transform_ctx->output.buffer);

    transform_ctx->reserved.buffer = TSIOBufferCreate();
    transform_ctx->reserved.reader = TSIOBufferReaderAlloc(transform_ctx->reserved.buffer);

    if (empty_input == 0) {
      transform_ctx->upstream_bytes = TSVIONBytesGet(input_vio);
    } else {
      transform_ctx->upstream_bytes = 0;
    }

    transform_ctx->downstream_bytes = INT64_MAX;
  }

  if (empty_input == 0) {
    input_avail   = TSIOBufferReaderAvail(input_reader);
    upstream_done = TSVIONDoneGet(input_vio);
    toread        = TSVIONTodoGet(input_vio);

    if (toread <= input_avail) { // upstream finished
      eos = 1;
    } else {
      eos = 0;
    }
  } else {
    input_avail = 0;
    toread      = 0;
    eos         = 1;
  }

  if (input_avail > 0) {
    // move to the reserved.buffer
    TSIOBufferCopy(transform_ctx->reserved.buffer, input_reader, input_avail, 0);

    // reset input
    TSIOBufferReaderConsume(input_reader, input_avail);
    TSVIONDoneSet(input_vio, upstream_done + input_avail);
  }

  write_down = 0;
  if (empty_input == 0) {
    towrite = TSIOBufferReaderAvail(transform_ctx->reserved.reader);
  } else {
    towrite = 0;
  }

  TSMutexLock(mtxp);
  ts_lua_set_cont_info(L, ci);

  do {
    if (event == TS_LUA_EVENT_COROUTINE_CONT) {
      event = 0;
      goto launch;
    } else {
      n = 2;
    }

    if (towrite == 0 && empty_input == 0) {
      break;
    }

    if (empty_input == 0) {
      blk   = TSIOBufferReaderStart(transform_ctx->reserved.reader);
      start = TSIOBufferBlockReadStart(blk, transform_ctx->reserved.reader, &blk_len);

      lua_pushlightuserdata(L, transform_ctx);
      lua_rawget(L, LUA_GLOBALSINDEX); /* push function */

      if (towrite > blk_len) {
        lua_pushlstring(L, start, (size_t)blk_len);
        towrite -= blk_len;
        TSIOBufferReaderConsume(transform_ctx->reserved.reader, blk_len);
      } else {
        lua_pushlstring(L, start, (size_t)towrite);
        TSIOBufferReaderConsume(transform_ctx->reserved.reader, towrite);
        towrite = 0;
      }

      if (!towrite && eos) {
        lua_pushinteger(L, 1); /* second param, data finished */
      } else {
        lua_pushinteger(L, 0); /* second param, data not finish */
      }
    } else {
      lua_pushlightuserdata(L, transform_ctx);
      lua_rawget(L, LUA_GLOBALSINDEX); /* push function */

      lua_pushlstring(L, "", 0);
      lua_pushinteger(L, 1); /* second param, data finished */
    }

  launch:
    rc  = lua_resume(L, n);
    top = lua_gettop(L);

    switch (rc) {
    case LUA_YIELD: // coroutine yield
      TSMutexUnlock(mtxp);
      return 0;

    case 0: // coroutine success
      if (top == 2) {
        ret = lua_tointeger(L, -1); /* 0 is not finished, 1 is finished */
        res = lua_tolstring(L, -2, &res_len);
      } else { // what hells code are you writing ?
        ret     = 0;
        res     = NULL;
        res_len = 0;
      }
      break;

    default: // coroutine failed
      TSError("[ts_lua] lua_resume failed: %s", lua_tostring(L, -1));
      ret     = 1;
      res     = NULL;
      res_len = 0;
      break;
    }

    if (res && res_len > 0) {
      if (!transform_ctx->output.vio) {
        l = transform_ctx->downstream_bytes;
        if (ret) {
          l = res_len;
        }

        transform_ctx->output.vio = TSVConnWrite(output_conn, contp, transform_ctx->output.reader, l); // HttpSM go on
      }

      TSIOBufferWrite(transform_ctx->output.buffer, res, res_len);
      transform_ctx->total += res_len;
      write_down = 1;
    }

    lua_pop(L, top);

    if (ret || (eos && !towrite)) { // EOS
      eos = 1;
      break;
    }

  } while (towrite > 0);

  TSMutexUnlock(mtxp);

  if (eos && !transform_ctx->output.vio) {
    transform_ctx->output.vio = TSVConnWrite(output_conn, contp, transform_ctx->output.reader, 0);
  }

  if (write_down || eos) {
    TSVIOReenable(transform_ctx->output.vio);
  }

  if (toread > input_avail) { // upstream not finished.
    if (eos) {
      TSVIONBytesSet(transform_ctx->output.vio, transform_ctx->total);
      if (empty_input == 0) {
        TSContCall(TSVIOContGet(input_vio), TS_EVENT_VCONN_EOS, input_vio);
      }
    } else {
      if (empty_input == 0) {
        TSContCall(TSVIOContGet(input_vio), TS_EVENT_VCONN_WRITE_READY, input_vio);
      }
    }
  } else { // upstream is finished.
    TSVIONBytesSet(transform_ctx->output.vio, transform_ctx->total);
    if (empty_input == 0) {
      TSContCall(TSVIOContGet(input_vio), TS_EVENT_VCONN_WRITE_COMPLETE, input_vio);
    }
  }

  return 0;
}
Example #3
0
static int
globalHookHandler(TSCont contp, TSEvent event ATS_UNUSED, void *edata)
{
    TSHttpTxn txnp = (TSHttpTxn)edata;

    TSMBuffer bufp;
    TSMLoc hdr_loc;
    TSMLoc url_loc;

    int ret;
    uint64_t req_id;
    TSCont txn_contp;

    lua_State *l;

    ts_lua_main_ctx *main_ctx;
    ts_lua_http_ctx *http_ctx;
    ts_lua_cont_info *ci;

    ts_lua_instance_conf *conf = (ts_lua_instance_conf *)TSContDataGet(contp);

    req_id = __sync_fetch_and_add(&ts_lua_g_http_next_id, 1);

    main_ctx = &ts_lua_g_main_ctx_array[req_id % TS_LUA_MAX_STATE_COUNT];

    TSDebug(TS_LUA_DEBUG_TAG, "[%s] req_id: %" PRId64, __FUNCTION__, req_id);
    TSMutexLock(main_ctx->mutexp);

    http_ctx           = ts_lua_create_http_ctx(main_ctx, conf);
    http_ctx->txnp     = txnp;
    http_ctx->rri      = NULL;
    http_ctx->has_hook = 0;

    if (!http_ctx->client_request_bufp) {
        if (TSHttpTxnClientReqGet(txnp, &bufp, &hdr_loc) == TS_SUCCESS) {
            http_ctx->client_request_bufp = bufp;
            http_ctx->client_request_hdrp = hdr_loc;

            if (TSHttpHdrUrlGet(bufp, hdr_loc, &url_loc) == TS_SUCCESS) {
                http_ctx->client_request_url = url_loc;
            }
        }
    }

    if (!http_ctx->client_request_hdrp) {
        ts_lua_destroy_http_ctx(http_ctx);
        TSMutexUnlock(main_ctx->mutexp);

        TSHttpTxnReenable(txnp, TS_EVENT_HTTP_CONTINUE);
        return 0;
    }

    txn_contp = TSContCreate(ts_lua_http_cont_handler, NULL);
    TSContDataSet(txn_contp, http_ctx);

    ci        = &http_ctx->cinfo;
    ci->contp = txn_contp;
    ci->mutex = TSContMutexGet((TSCont)txnp);

    l = ci->routine.lua;

    switch (event) {
    case TS_EVENT_HTTP_READ_REQUEST_HDR:
        lua_getglobal(l, TS_LUA_FUNCTION_G_READ_REQUEST);
        break;

    case TS_EVENT_HTTP_SEND_REQUEST_HDR:
        lua_getglobal(l, TS_LUA_FUNCTION_G_SEND_REQUEST);
        break;

    case TS_EVENT_HTTP_READ_RESPONSE_HDR:
        lua_getglobal(l, TS_LUA_FUNCTION_G_READ_RESPONSE);
        break;

    case TS_EVENT_HTTP_SEND_RESPONSE_HDR:
        // client response can be changed within a transaction
        // (e.g. due to the follow redirect feature). So, clearing the pointers
        // to allow API(s) to fetch the pointers again when it re-enters the hook
        if (http_ctx->client_response_hdrp != NULL) {
            TSHandleMLocRelease(http_ctx->client_response_bufp, TS_NULL_MLOC, http_ctx->client_response_hdrp);
            http_ctx->client_response_hdrp = NULL;
        }
        lua_getglobal(l, TS_LUA_FUNCTION_G_SEND_RESPONSE);
        break;

    case TS_EVENT_HTTP_CACHE_LOOKUP_COMPLETE:
        lua_getglobal(l, TS_LUA_FUNCTION_G_CACHE_LOOKUP_COMPLETE);
        break;

    case TS_EVENT_HTTP_TXN_START:
        lua_getglobal(l, TS_LUA_FUNCTION_G_TXN_START);
        break;

    case TS_EVENT_HTTP_PRE_REMAP:
        lua_getglobal(l, TS_LUA_FUNCTION_G_PRE_REMAP);
        break;

    case TS_EVENT_HTTP_POST_REMAP:
        lua_getglobal(l, TS_LUA_FUNCTION_G_POST_REMAP);
        break;

    case TS_EVENT_HTTP_SELECT_ALT:
        lua_getglobal(l, TS_LUA_FUNCTION_G_SELECT_ALT);
        break;

    case TS_EVENT_HTTP_OS_DNS:
        lua_getglobal(l, TS_LUA_FUNCTION_G_OS_DNS);
        break;

    case TS_EVENT_HTTP_READ_CACHE_HDR:
        lua_getglobal(l, TS_LUA_FUNCTION_G_READ_CACHE);
        break;

    case TS_EVENT_HTTP_TXN_CLOSE:
        lua_getglobal(l, TS_LUA_FUNCTION_G_TXN_CLOSE);
        break;

    default:
        ts_lua_destroy_http_ctx(http_ctx);
        TSMutexUnlock(main_ctx->mutexp);
        TSHttpTxnReenable(txnp, TS_EVENT_HTTP_CONTINUE);
        return 0;
    }

    if (lua_type(l, -1) != LUA_TFUNCTION) {
        lua_pop(l, 1);
        ts_lua_destroy_http_ctx(http_ctx);
        TSMutexUnlock(main_ctx->mutexp);

        TSHttpTxnReenable(txnp, TS_EVENT_HTTP_CONTINUE);
        return 0;
    }

    ts_lua_set_cont_info(l, NULL);

    if (lua_pcall(l, 0, 1, 0) != 0) {
        TSError("[ts_lua] lua_pcall failed: %s", lua_tostring(l, -1));
    }

    ret = lua_tointeger(l, -1);
    lua_pop(l, 1);

    if (http_ctx->has_hook) {
        // add a hook to release resources for context
        TSDebug(TS_LUA_DEBUG_TAG, "[%s] has txn hook -> adding txn close hook handler to release resources", __FUNCTION__);
        TSHttpTxnHookAdd(txnp, TS_HTTP_TXN_CLOSE_HOOK, txn_contp);
    } else {
        TSDebug(TS_LUA_DEBUG_TAG, "[%s] no txn hook -> release resources now", __FUNCTION__);
        ts_lua_destroy_http_ctx(http_ctx);
    }

    TSMutexUnlock(main_ctx->mutexp);

    if (ret) {
        TSHttpTxnReenable(txnp, TS_EVENT_HTTP_ERROR);
    } else {
        TSHttpTxnReenable(txnp, TS_EVENT_HTTP_CONTINUE);
    }

    return 0;
}
Example #4
0
static TSRemapStatus
ts_lua_remap_plugin_init(void *ih, TSHttpTxn rh, TSRemapRequestInfo *rri)
{
    int ret;
    uint64_t req_id;

    TSCont contp;
    lua_State *L;

    ts_lua_main_ctx *main_ctx;
    ts_lua_http_ctx *http_ctx;
    ts_lua_cont_info *ci;

    ts_lua_instance_conf *instance_conf;

    int remap     = (rri == NULL ? 0 : 1);
    instance_conf = (ts_lua_instance_conf *)ih;
    req_id        = __sync_fetch_and_add(&ts_lua_http_next_id, 1);

    main_ctx = &ts_lua_main_ctx_array[req_id % TS_LUA_MAX_STATE_COUNT];

    TSMutexLock(main_ctx->mutexp);

    http_ctx = ts_lua_create_http_ctx(main_ctx, instance_conf);

    http_ctx->txnp     = rh;
    http_ctx->has_hook = 0;
    http_ctx->rri      = rri;
    if (rri != NULL) {
        http_ctx->client_request_bufp = rri->requestBufp;
        http_ctx->client_request_hdrp = rri->requestHdrp;
        http_ctx->client_request_url  = rri->requestUrl;
    }

    ci = &http_ctx->cinfo;
    L  = ci->routine.lua;

    contp = TSContCreate(ts_lua_http_cont_handler, NULL);
    TSContDataSet(contp, http_ctx);

    ci->contp = contp;
    ci->mutex = TSContMutexGet((TSCont)rh);

    lua_getglobal(L, (remap ? TS_LUA_FUNCTION_REMAP : TS_LUA_FUNCTION_OS_RESPONSE));
    if (lua_type(L, -1) != LUA_TFUNCTION) {
        TSMutexUnlock(main_ctx->mutexp);
        return TSREMAP_NO_REMAP;
    }

    ts_lua_set_cont_info(L, NULL);
    if (lua_pcall(L, 0, 1, 0) != 0) {
        TSError("[ts_lua] lua_pcall failed: %s", lua_tostring(L, -1));
        ret = TSREMAP_NO_REMAP;

    } else {
        ret = lua_tointeger(L, -1);
    }

    lua_pop(L, 1);

    if (http_ctx->has_hook) {
        TSDebug(TS_LUA_DEBUG_TAG, "[%s] has txn hook -> adding txn close hook handler to release resources", __FUNCTION__);
        TSHttpTxnHookAdd(rh, TS_HTTP_TXN_CLOSE_HOOK, contp);
    } else {
        TSDebug(TS_LUA_DEBUG_TAG, "[%s] no txn hook -> release resources now", __FUNCTION__);
        ts_lua_destroy_http_ctx(http_ctx);
    }

    TSMutexUnlock(main_ctx->mutexp);

    return ret;
}