1 /* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2 
3 #include "proxy.h"
4 
mcplib_response_elapsed(lua_State * L)5 int mcplib_response_elapsed(lua_State *L) {
6     mcp_resp_t *r = luaL_checkudata(L, -1, "mcp.response");
7     lua_pushinteger(L, r->elapsed);
8     return 1;
9 }
10 
11 // resp:ok()
mcplib_response_ok(lua_State * L)12 int mcplib_response_ok(lua_State *L) {
13     mcp_resp_t *r = luaL_checkudata(L, -1, "mcp.response");
14 
15     if (r->status == MCMC_OK) {
16         lua_pushboolean(L, 1);
17     } else {
18         lua_pushboolean(L, 0);
19     }
20 
21     return 1;
22 }
23 
mcplib_response_hit(lua_State * L)24 int mcplib_response_hit(lua_State *L) {
25     mcp_resp_t *r = luaL_checkudata(L, -1, "mcp.response");
26 
27     if (r->status == MCMC_OK && r->resp.code != MCMC_CODE_END) {
28         lua_pushboolean(L, 1);
29     } else {
30         lua_pushboolean(L, 0);
31     }
32 
33     return 1;
34 }
35 
36 // Caller needs to discern if a vlen is 0 because of a failed response or an
37 // OK response that was actually zero. So we always return an integer value
38 // here.
mcplib_response_vlen(lua_State * L)39 int mcplib_response_vlen(lua_State *L) {
40     mcp_resp_t *r = luaL_checkudata(L, -1, "mcp.response");
41 
42     // We do remove the "\r\n" from the value length, so if you're actually
43     // processing the value nothing breaks.
44     if (r->resp.vlen >= 2) {
45         lua_pushinteger(L, r->resp.vlen-2);
46     } else {
47         lua_pushinteger(L, 0);
48     }
49 
50     return 1;
51 }
52 
53 // Refer to MCMC_CODE_* defines.
mcplib_response_code(lua_State * L)54 int mcplib_response_code(lua_State *L) {
55     mcp_resp_t *r = luaL_checkudata(L, -1, "mcp.response");
56 
57     lua_pushinteger(L, r->resp.code);
58 
59     return 1;
60 }
61 
62 // Get the unparsed response line for handling in lua.
mcplib_response_line(lua_State * L)63 int mcplib_response_line(lua_State *L) {
64     mcp_resp_t *r = luaL_checkudata(L, -1, "mcp.response");
65 
66     if (r->resp.rline != NULL) {
67         lua_pushlstring(L, r->resp.rline, r->resp.rlen);
68     } else {
69         lua_pushnil(L);
70     }
71 
72     return 1;
73 }
74 
mcplib_response_flag_blank(lua_State * L)75 int mcplib_response_flag_blank(lua_State *L) {
76     mcp_resp_t *r = luaL_checkudata(L, 1, "mcp.response");
77     mcmc_resp_t reresp;
78     size_t len = 0;
79     const char *flagstr = luaL_checklstring(L, 2, &len);
80 
81     if (len != 1) {
82         proxy_lua_error(L, "request: meta flag must be a single character");
83         return 0;
84     }
85     if (flagstr[0] < 65 || flagstr[0] > 122) {
86         proxy_lua_error(L, "request: invalid flag, must be A-Z,a-z");
87         return 0;
88     }
89 
90     mcmc_parse_buf(r->buf, r->blen, &reresp);
91 
92     if (reresp.type == MCMC_RESP_META) {
93         // r->resp.rline is the start of the meta line... rlen is the length.
94         // we cast away the const and do evil here.
95         char *pos = (char *) reresp.rline;
96         size_t rlen = reresp.rlen;
97 
98         char *end = pos + rlen;
99         char flag = flagstr[0];
100 
101         while (pos != end) {
102             // either flag is at the start of the line or it has a space
103             // immediately before it.
104             if (*pos == flag && (pos == reresp.rline || *(pos-1) == ' ')) {
105                 while (pos != end && !isspace(*pos)) {
106                     *pos = ' ';
107                     pos++;
108                 }
109                 lua_pushboolean(L, 1); // found and blanked.
110                 return 1;
111             } else {
112                 pos++;
113             }
114         }
115 
116         // TODO: blank out r->tok?
117     }
118     lua_pushboolean(L, 0); // not found or not done.
119     return 1;
120 }
121 
mcp_response_cleanup(LIBEVENT_THREAD * t,mcp_resp_t * r)122 void mcp_response_cleanup(LIBEVENT_THREAD *t, mcp_resp_t *r) {
123     // On error/similar we might be holding the read buffer.
124     // If the buf is handed off to mc_resp for return, this pointer is NULL
125     if (r->buf != NULL) {
126         pthread_mutex_lock(&t->proxy_limit_lock);
127         t->proxy_buffer_memory_used -= r->blen + r->extra;
128         pthread_mutex_unlock(&t->proxy_limit_lock);
129 
130         free(r->buf);
131         r->buf = NULL;
132     }
133     r->tok.ntokens = 0;
134 
135     // release our temporary mc_resp sub-object.
136     if (r->cresp != NULL) {
137         mc_resp *cresp = r->cresp;
138         assert(r->thread != NULL);
139         if (cresp->item) {
140             item_remove(cresp->item);
141             cresp->item = NULL;
142         }
143         resp_free(r->thread, cresp);
144         r->cresp = NULL;
145     }
146 }
147 
mcplib_response_gc(lua_State * L)148 int mcplib_response_gc(lua_State *L) {
149     LIBEVENT_THREAD *t = PROXY_GET_THR(L);
150     mcp_resp_t *r = luaL_checkudata(L, -1, "mcp.response");
151     mcp_response_cleanup(t, r);
152 
153     return 0;
154 }
155 
156 // Note that this can be called multiple times for a single object, as opposed
157 // to _gc. The cleanup routine is armored against repeat accesses by NULL'ing
158 // th efields it checks.
mcplib_response_close(lua_State * L)159 int mcplib_response_close(lua_State *L) {
160     LIBEVENT_THREAD *t = PROXY_GET_THR(L);
161     mcp_resp_t *r = luaL_checkudata(L, 1, "mcp.response");
162     mcp_response_cleanup(t, r);
163 
164     return 0;
165 }
166 
167 
168