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\*=========================================================================*/
14static int recvraw(p_buffer buf, size_t wanted, luaL_Buffer *b);
15static int recvline(p_buffer buf, luaL_Buffer *b);
16static int recvall(p_buffer buf, luaL_Buffer *b);
17static int buffer_get(p_buffer buf, const char **data, size_t *count);
18static void buffer_skip(p_buffer buf, size_t count);
19static 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\*-------------------------------------------------------------------------*/
35int buffer_open(lua_State *L) {
36 (void) L;
37 return 0;
38}
39
40/*-------------------------------------------------------------------------*\
41* Initializes C structure
42\*-------------------------------------------------------------------------*/
43void 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\*-------------------------------------------------------------------------*/
54int 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\*-------------------------------------------------------------------------*/
64int 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\*-------------------------------------------------------------------------*/
75int 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\*-------------------------------------------------------------------------*/
108int 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\*-------------------------------------------------------------------------*/
157int 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
168static 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\*-------------------------------------------------------------------------*/
187static 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\*-------------------------------------------------------------------------*/
205static 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\*-------------------------------------------------------------------------*/
225static 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\*-------------------------------------------------------------------------*/
249static 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\*-------------------------------------------------------------------------*/
260static 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