1 /*=========================================================================*\
2 * Input/Output interface for Lua programs
3 * LuaSocket toolkit
4 \*=========================================================================*/
5 #include "lua.h"
6 #include "lauxlib.h"
7 #include "compat.h"
8 
9 #include "buffer.h"
10 
11 /*=========================================================================*\
12 * Internal function prototypes
13 \*=========================================================================*/
14 static int recvraw(p_buffer buf, size_t wanted, luaL_Buffer *b);
15 static int recvline(p_buffer buf, luaL_Buffer *b);
16 static int recvall(p_buffer buf, luaL_Buffer *b);
17 static int buffer_get(p_buffer buf, const char **data, size_t *count);
18 static void buffer_skip(p_buffer buf, size_t count);
19 static int sendraw(p_buffer buf, const char *data, size_t count, size_t *sent);
20 
21 /* min and max macros */
22 #ifndef MIN
23 #define MIN(x, y) ((x) < (y) ? x : y)
24 #endif
25 #ifndef MAX
26 #define MAX(x, y) ((x) > (y) ? x : y)
27 #endif
28 
29 /*=========================================================================*\
30 * Exported functions
31 \*=========================================================================*/
32 /*-------------------------------------------------------------------------*\
33 * Initializes module
34 \*-------------------------------------------------------------------------*/
buffer_open(lua_State * L)35 int buffer_open(lua_State *L) {
36     (void) L;
37     return 0;
38 }
39 
40 /*-------------------------------------------------------------------------*\
41 * Initializes C structure
42 \*-------------------------------------------------------------------------*/
buffer_init(p_buffer buf,p_io io,p_timeout tm)43 void buffer_init(p_buffer buf, p_io io, p_timeout tm) {
44     buf->first = buf->last = 0;
45     buf->io = io;
46     buf->tm = tm;
47     buf->received = buf->sent = 0;
48     buf->birthday = timeout_gettime();
49 }
50 
51 /*-------------------------------------------------------------------------*\
52 * object:getstats() interface
53 \*-------------------------------------------------------------------------*/
buffer_meth_getstats(lua_State * L,p_buffer buf)54 int buffer_meth_getstats(lua_State *L, p_buffer buf) {
55     lua_pushnumber(L, (lua_Number) buf->received);
56     lua_pushnumber(L, (lua_Number) buf->sent);
57     lua_pushnumber(L, timeout_gettime() - buf->birthday);
58     return 3;
59 }
60 
61 /*-------------------------------------------------------------------------*\
62 * object:setstats() interface
63 \*-------------------------------------------------------------------------*/
buffer_meth_setstats(lua_State * L,p_buffer buf)64 int buffer_meth_setstats(lua_State *L, p_buffer buf) {
65     buf->received = (long) luaL_optnumber(L, 2, (lua_Number) buf->received);
66     buf->sent = (long) luaL_optnumber(L, 3, (lua_Number) buf->sent);
67     if (lua_isnumber(L, 4)) buf->birthday = timeout_gettime() - lua_tonumber(L, 4);
68     lua_pushnumber(L, 1);
69     return 1;
70 }
71 
72 /*-------------------------------------------------------------------------*\
73 * object:send() interface
74 \*-------------------------------------------------------------------------*/
buffer_meth_send(lua_State * L,p_buffer buf)75 int buffer_meth_send(lua_State *L, p_buffer buf) {
76     int top = lua_gettop(L);
77     int err = IO_DONE;
78     size_t size = 0, sent = 0;
79     const char *data = luaL_checklstring(L, 2, &size);
80     long start = (long) luaL_optnumber(L, 3, 1);
81     long end = (long) luaL_optnumber(L, 4, -1);
82     timeout_markstart(buf->tm);
83     if (start < 0) start = (long) (size+start+1);
84     if (end < 0) end = (long) (size+end+1);
85     if (start < 1) start = (long) 1;
86     if (end > (long) size) end = (long) size;
87     if (start <= end) err = sendraw(buf, data+start-1, end-start+1, &sent);
88     /* check if there was an error */
89     if (err != IO_DONE) {
90         lua_pushnil(L);
91         lua_pushstring(L, buf->io->error(buf->io->ctx, err));
92         lua_pushnumber(L, (lua_Number) (sent+start-1));
93     } else {
94         lua_pushnumber(L, (lua_Number) (sent+start-1));
95         lua_pushnil(L);
96         lua_pushnil(L);
97     }
98 #ifdef LUASOCKET_DEBUG
99     /* push time elapsed during operation as the last return value */
100     lua_pushnumber(L, timeout_gettime() - timeout_getstart(buf->tm));
101 #endif
102     return lua_gettop(L) - top;
103 }
104 
105 /*-------------------------------------------------------------------------*\
106 * object:receive() interface
107 \*-------------------------------------------------------------------------*/
buffer_meth_receive(lua_State * L,p_buffer buf)108 int buffer_meth_receive(lua_State *L, p_buffer buf) {
109     int err = IO_DONE, top = lua_gettop(L);
110     luaL_Buffer b;
111     size_t size;
112     const char *part = luaL_optlstring(L, 3, "", &size);
113     timeout_markstart(buf->tm);
114     /* initialize buffer with optional extra prefix
115      * (useful for concatenating previous partial results) */
116     luaL_buffinit(L, &b);
117     luaL_addlstring(&b, part, size);
118     /* receive new patterns */
119     if (!lua_isnumber(L, 2)) {
120         const char *p= luaL_optstring(L, 2, "*l");
121         if (p[0] == '*' && p[1] == 'l') err = recvline(buf, &b);
122         else if (p[0] == '*' && p[1] == 'a') err = recvall(buf, &b);
123         else luaL_argcheck(L, 0, 2, "invalid receive pattern");
124     /* get a fixed number of bytes (minus what was already partially
125      * received) */
126     } else {
127         double n = lua_tonumber(L, 2);
128         size_t wanted = (size_t) n;
129         luaL_argcheck(L, n >= 0, 2, "invalid receive pattern");
130         if (size == 0 || wanted > size)
131             err = recvraw(buf, wanted-size, &b);
132     }
133     /* check if there was an error */
134     if (err != IO_DONE) {
135         /* we can't push anyting in the stack before pushing the
136          * contents of the buffer. this is the reason for the complication */
137         luaL_pushresult(&b);
138         lua_pushstring(L, buf->io->error(buf->io->ctx, err));
139         lua_pushvalue(L, -2);
140         lua_pushnil(L);
141         lua_replace(L, -4);
142     } else {
143         luaL_pushresult(&b);
144         lua_pushnil(L);
145         lua_pushnil(L);
146     }
147 #ifdef LUASOCKET_DEBUG
148     /* push time elapsed during operation as the last return value */
149     lua_pushnumber(L, timeout_gettime() - timeout_getstart(buf->tm));
150 #endif
151     return lua_gettop(L) - top;
152 }
153 
154 /*-------------------------------------------------------------------------*\
155 * Determines if there is any data in the read buffer
156 \*-------------------------------------------------------------------------*/
buffer_isempty(p_buffer buf)157 int buffer_isempty(p_buffer buf) {
158     return buf->first >= buf->last;
159 }
160 
161 /*=========================================================================*\
162 * Internal functions
163 \*=========================================================================*/
164 /*-------------------------------------------------------------------------*\
165 * Sends a block of data (unbuffered)
166 \*-------------------------------------------------------------------------*/
167 #define STEPSIZE 8192
sendraw(p_buffer buf,const char * data,size_t count,size_t * sent)168 static int sendraw(p_buffer buf, const char *data, size_t count, size_t *sent) {
169     p_io io = buf->io;
170     p_timeout tm = buf->tm;
171     size_t total = 0;
172     int err = IO_DONE;
173     while (total < count && err == IO_DONE) {
174         size_t done = 0;
175         size_t step = (count-total <= STEPSIZE)? count-total: STEPSIZE;
176         err = io->send(io->ctx, data+total, step, &done, tm);
177         total += done;
178     }
179     *sent = total;
180     buf->sent += total;
181     return err;
182 }
183 
184 /*-------------------------------------------------------------------------*\
185 * Reads a fixed number of bytes (buffered)
186 \*-------------------------------------------------------------------------*/
recvraw(p_buffer buf,size_t wanted,luaL_Buffer * b)187 static int recvraw(p_buffer buf, size_t wanted, luaL_Buffer *b) {
188     int err = IO_DONE;
189     size_t total = 0;
190     while (err == IO_DONE) {
191         size_t count; const char *data;
192         err = buffer_get(buf, &data, &count);
193         count = MIN(count, wanted - total);
194         luaL_addlstring(b, data, count);
195         buffer_skip(buf, count);
196         total += count;
197         if (total >= wanted) break;
198     }
199     return err;
200 }
201 
202 /*-------------------------------------------------------------------------*\
203 * Reads everything until the connection is closed (buffered)
204 \*-------------------------------------------------------------------------*/
recvall(p_buffer buf,luaL_Buffer * b)205 static int recvall(p_buffer buf, luaL_Buffer *b) {
206     int err = IO_DONE;
207     size_t total = 0;
208     while (err == IO_DONE) {
209         const char *data; size_t count;
210         err = buffer_get(buf, &data, &count);
211         total += count;
212         luaL_addlstring(b, data, count);
213         buffer_skip(buf, count);
214     }
215     if (err == IO_CLOSED) {
216         if (total > 0) return IO_DONE;
217         else return IO_CLOSED;
218     } else return err;
219 }
220 
221 /*-------------------------------------------------------------------------*\
222 * Reads a line terminated by a CR LF pair or just by a LF. The CR and LF
223 * are not returned by the function and are discarded from the buffer
224 \*-------------------------------------------------------------------------*/
recvline(p_buffer buf,luaL_Buffer * b)225 static int recvline(p_buffer buf, luaL_Buffer *b) {
226     int err = IO_DONE;
227     while (err == IO_DONE) {
228         size_t count, pos; const char *data;
229         err = buffer_get(buf, &data, &count);
230         pos = 0;
231         while (pos < count && data[pos] != '\n') {
232             /* we ignore all \r's */
233             if (data[pos] != '\r') luaL_addchar(b, data[pos]);
234             pos++;
235         }
236         if (pos < count) { /* found '\n' */
237             buffer_skip(buf, pos+1); /* skip '\n' too */
238             break; /* we are done */
239         } else /* reached the end of the buffer */
240             buffer_skip(buf, pos);
241     }
242     return err;
243 }
244 
245 /*-------------------------------------------------------------------------*\
246 * Skips a given number of bytes from read buffer. No data is read from the
247 * transport layer
248 \*-------------------------------------------------------------------------*/
buffer_skip(p_buffer buf,size_t count)249 static void buffer_skip(p_buffer buf, size_t count) {
250     buf->received += count;
251     buf->first += count;
252     if (buffer_isempty(buf))
253         buf->first = buf->last = 0;
254 }
255 
256 /*-------------------------------------------------------------------------*\
257 * Return any data available in buffer, or get more data from transport layer
258 * if buffer is empty
259 \*-------------------------------------------------------------------------*/
buffer_get(p_buffer buf,const char ** data,size_t * count)260 static int buffer_get(p_buffer buf, const char **data, size_t *count) {
261     int err = IO_DONE;
262     p_io io = buf->io;
263     p_timeout tm = buf->tm;
264     if (buffer_isempty(buf)) {
265         size_t got;
266         err = io->recv(io->ctx, buf->data, BUF_SIZE, &got, tm);
267         buf->first = 0;
268         buf->last = got;
269     }
270     *count = buf->last - buf->first;
271     *data = buf->data + buf->first;
272     return err;
273 }
274