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 | \*-------------------------------------------------------------------------*/ |
35 | int buffer_open(lua_State *L) { |
36 | (void) L; |
37 | return 0; |
38 | } |
39 | |
40 | /*-------------------------------------------------------------------------*\ |
41 | * Initializes C structure |
42 | \*-------------------------------------------------------------------------*/ |
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 | \*-------------------------------------------------------------------------*/ |
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 | \*-------------------------------------------------------------------------*/ |
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 | \*-------------------------------------------------------------------------*/ |
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 | \*-------------------------------------------------------------------------*/ |
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 | \*-------------------------------------------------------------------------*/ |
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 |
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 | \*-------------------------------------------------------------------------*/ |
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 | \*-------------------------------------------------------------------------*/ |
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 | \*-------------------------------------------------------------------------*/ |
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 | \*-------------------------------------------------------------------------*/ |
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 | \*-------------------------------------------------------------------------*/ |
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 | |