aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDiego Nehab <diego@tecgraf.puc-rio.br>2007-06-11 23:44:54 +0000
committerDiego Nehab <diego@tecgraf.puc-rio.br>2007-06-11 23:44:54 +0000
commit3cd10f5ab6cda5c4a8db829dd38f25168edcfc4a (patch)
treef0c49eb250f5375414025a9e7022261e729ad905
parent3074a8f56b5153f4477e662453102583d7b6f539 (diff)
downloadluasocket-3cd10f5ab6cda5c4a8db829dd38f25168edcfc4a.tar.gz
luasocket-3cd10f5ab6cda5c4a8db829dd38f25168edcfc4a.tar.bz2
luasocket-3cd10f5ab6cda5c4a8db829dd38f25168edcfc4a.zip
Crashy bug fixed in recvraw.
Also fixed returns on closed socket.
-rw-r--r--config6
-rw-r--r--doc/socket.html10
-rw-r--r--etc/dispatch.lua4
-rw-r--r--makefile.dist5
-rw-r--r--src/buffer.c15
-rw-r--r--src/luasocket.h4
-rw-r--r--src/mime.h4
-rw-r--r--src/mime.lua3
-rw-r--r--src/usocket.c8
-rw-r--r--src/wsocket.c8
-rw-r--r--test/testclnt.lua61
-rw-r--r--test/testmesg.lua2
12 files changed, 97 insertions, 33 deletions
diff --git a/config b/config
index 5a86f9c..6fd1646 100644
--- a/config
+++ b/config
@@ -6,8 +6,8 @@
6# Output file names 6# Output file names
7# 7#
8EXT=so 8EXT=so
9SOCKET_V=2.0.1 9SOCKET_V=2.0.2
10MIME_V=1.0.1 10MIME_V=1.0.2
11SOCKET_SO=socket.$(EXT).$(SOCKET_V) 11SOCKET_SO=socket.$(EXT).$(SOCKET_V)
12MIME_SO=mime.$(EXT).$(MIME_V) 12MIME_SO=mime.$(EXT).$(MIME_V)
13UNIX_SO=unix.$(EXT) 13UNIX_SO=unix.$(EXT)
@@ -41,7 +41,7 @@ INSTALL_EXEC=cp
41# for Mac OS X 41# for Mac OS X
42# 42#
43CC=gcc 43CC=gcc
44DEF=-DLUASOCKET_DEBUG -DUNIX_HAS_SUN_LEN 44DEF= -DAAA -DLUASOCKET_DEBUG -DUNIX_HAS_SUN_LEN
45CFLAGS= $(LUAINC) -I$(COMPAT) $(DEF) -pedantic -Wall -O2 -fno-common 45CFLAGS= $(LUAINC) -I$(COMPAT) $(DEF) -pedantic -Wall -O2 -fno-common
46LDFLAGS=-bundle -undefined dynamic_lookup 46LDFLAGS=-bundle -undefined dynamic_lookup
47LD=export MACOSX_DEPLOYMENT_TARGET="10.3"; gcc 47LD=export MACOSX_DEPLOYMENT_TARGET="10.3"; gcc
diff --git a/doc/socket.html b/doc/socket.html
index 16a025b..f096e4b 100644
--- a/doc/socket.html
+++ b/doc/socket.html
@@ -190,11 +190,13 @@ be empty tables or <tt><b>nil</b></tt>. Non-socket values (or values with
190non-numeric indices) in the arrays will be silently ignored. 190non-numeric indices) in the arrays will be silently ignored.
191</p> 191</p>
192 192
193<p class=return> The function returns a table with the sockets ready for 193<p class=return> The function returns a list with the sockets ready for
194reading, a table with the sockets ready for writing and an error message. 194reading, a list with the sockets ready for writing and an error message.
195The error message is "<tt>timeout</tt>" if a timeout condition was met and 195The error message is "<tt>timeout</tt>" if a timeout condition was met and
196<tt><b>nil</b></tt> otherwise. The returned tables are associative, to 196<tt><b>nil</b></tt> otherwise. The returned tables are
197simplify the test if a specific socket has changed status. 197doubly keyed both by integers and also by the sockets
198themselves, to simplify the test if a specific socket has
199changed status.
198</p> 200</p>
199 201
200<p class=note> 202<p class=note>
diff --git a/etc/dispatch.lua b/etc/dispatch.lua
index 6f3855e..3ef1e72 100644
--- a/etc/dispatch.lua
+++ b/etc/dispatch.lua
@@ -56,7 +56,7 @@ function socket.protect(f)
56 if not status then 56 if not status then
57 if type(results[1]) == 'table' then 57 if type(results[1]) == 'table' then
58 return nil, results[1][1] 58 return nil, results[1][1]
59 else error(results[1]) end 59 else base.error(results[1]) end
60 end 60 end
61 if coroutine.status(co) == "suspended" then 61 if coroutine.status(co) == "suspended" then
62 arg = {coroutine.yield(base.unpack(results))} 62 arg = {coroutine.yield(base.unpack(results))}
@@ -219,7 +219,7 @@ function schedule(cortn, status, operation, tcp)
219 operation.cortn[tcp] = cortn 219 operation.cortn[tcp] = cortn
220 operation.stamp[tcp] = socket.gettime() 220 operation.stamp[tcp] = socket.gettime()
221 end 221 end
222 else error(operation) end 222 else base.error(operation) end
223end 223end
224 224
225function kick(operation, tcp) 225function kick(operation, tcp)
diff --git a/makefile.dist b/makefile.dist
index e3b0e5c..58ae5b3 100644
--- a/makefile.dist
+++ b/makefile.dist
@@ -1,9 +1,7 @@
1#-------------------------------------------------------------------------- 1#--------------------------------------------------------------------------
2# Distribution makefile 2# Distribution makefile
3#-------------------------------------------------------------------------- 3#--------------------------------------------------------------------------
4DIST = luasocket-2.0.1 4DIST = luasocket-2.0.2
5
6COMPAT = src/compat-5.1r5
7 5
8TEST = \ 6TEST = \
9 test/README \ 7 test/README \
@@ -115,7 +113,6 @@ dist:
115 113
116 mkdir -p $(DIST)/src 114 mkdir -p $(DIST)/src
117 cp -vf $(SRC) $(DIST)/src 115 cp -vf $(SRC) $(DIST)/src
118 cp -vfr $(COMPAT) $(DIST)/src
119 116
120 mkdir -p $(DIST)/doc 117 mkdir -p $(DIST)/doc
121 cp -vf $(DOC) $(DIST)/doc 118 cp -vf $(DOC) $(DIST)/doc
diff --git a/src/buffer.c b/src/buffer.c
index df1a0bc..de817b2 100644
--- a/src/buffer.c
+++ b/src/buffer.c
@@ -75,12 +75,12 @@ int buffer_meth_setstats(lua_State *L, p_buffer buf) {
75\*-------------------------------------------------------------------------*/ 75\*-------------------------------------------------------------------------*/
76int buffer_meth_send(lua_State *L, p_buffer buf) { 76int buffer_meth_send(lua_State *L, p_buffer buf) {
77 int top = lua_gettop(L); 77 int top = lua_gettop(L);
78 p_timeout tm = timeout_markstart(buf->tm);
79 int err = IO_DONE; 78 int err = IO_DONE;
80 size_t size = 0, sent = 0; 79 size_t size = 0, sent = 0;
81 const char *data = luaL_checklstring(L, 2, &size); 80 const char *data = luaL_checklstring(L, 2, &size);
82 long start = (long) luaL_optnumber(L, 3, 1); 81 long start = (long) luaL_optnumber(L, 3, 1);
83 long end = (long) luaL_optnumber(L, 4, -1); 82 long end = (long) luaL_optnumber(L, 4, -1);
83 p_timeout tm = timeout_markstart(buf->tm);
84 if (start < 0) start = (long) (size+start+1); 84 if (start < 0) start = (long) (size+start+1);
85 if (end < 0) end = (long) (size+end+1); 85 if (end < 0) end = (long) (size+end+1);
86 if (start < 1) start = (long) 1; 86 if (start < 1) start = (long) 1;
@@ -108,10 +108,10 @@ int buffer_meth_send(lua_State *L, p_buffer buf) {
108\*-------------------------------------------------------------------------*/ 108\*-------------------------------------------------------------------------*/
109int buffer_meth_receive(lua_State *L, p_buffer buf) { 109int buffer_meth_receive(lua_State *L, p_buffer buf) {
110 int err = IO_DONE, top = lua_gettop(L); 110 int err = IO_DONE, top = lua_gettop(L);
111 p_timeout tm = timeout_markstart(buf->tm);
112 luaL_Buffer b; 111 luaL_Buffer b;
113 size_t size; 112 size_t size;
114 const char *part = luaL_optlstring(L, 3, "", &size); 113 const char *part = luaL_optlstring(L, 3, "", &size);
114 p_timeout tm = timeout_markstart(buf->tm);
115 /* initialize buffer with optional extra prefix 115 /* initialize buffer with optional extra prefix
116 * (useful for concatenating previous partial results) */ 116 * (useful for concatenating previous partial results) */
117 luaL_buffinit(L, &b); 117 luaL_buffinit(L, &b);
@@ -182,13 +182,14 @@ static int sendraw(p_buffer buf, const char *data, size_t count, size_t *sent) {
182static int recvraw(p_buffer buf, size_t wanted, luaL_Buffer *b) { 182static int recvraw(p_buffer buf, size_t wanted, luaL_Buffer *b) {
183 int err = IO_DONE; 183 int err = IO_DONE;
184 size_t total = 0; 184 size_t total = 0;
185 while (total < wanted && err == IO_DONE) { 185 while (err == IO_DONE) {
186 size_t count; const char *data; 186 size_t count; const char *data;
187 err = buffer_get(buf, &data, &count); 187 err = buffer_get(buf, &data, &count);
188 count = MIN(count, wanted - total); 188 count = MIN(count, wanted - total);
189 luaL_addlstring(b, data, count); 189 luaL_addlstring(b, data, count);
190 buffer_skip(buf, count); 190 buffer_skip(buf, count);
191 total += count; 191 total += count;
192 if (total >= wanted) break;
192 } 193 }
193 return err; 194 return err;
194} 195}
@@ -198,14 +199,18 @@ static int recvraw(p_buffer buf, size_t wanted, luaL_Buffer *b) {
198\*-------------------------------------------------------------------------*/ 199\*-------------------------------------------------------------------------*/
199static int recvall(p_buffer buf, luaL_Buffer *b) { 200static int recvall(p_buffer buf, luaL_Buffer *b) {
200 int err = IO_DONE; 201 int err = IO_DONE;
202 size_t total = 0;
201 while (err == IO_DONE) { 203 while (err == IO_DONE) {
202 const char *data; size_t count; 204 const char *data; size_t count;
203 err = buffer_get(buf, &data, &count); 205 err = buffer_get(buf, &data, &count);
206 total += count;
204 luaL_addlstring(b, data, count); 207 luaL_addlstring(b, data, count);
205 buffer_skip(buf, count); 208 buffer_skip(buf, count);
206 } 209 }
207 if (err == IO_CLOSED) return IO_DONE; 210 if (err == IO_CLOSED) {
208 else return err; 211 if (total > 0) return IO_DONE;
212 else return IO_CLOSED;
213 } else return err;
209} 214}
210 215
211/*-------------------------------------------------------------------------*\ 216/*-------------------------------------------------------------------------*\
diff --git a/src/luasocket.h b/src/luasocket.h
index 46b5d06..13134cf 100644
--- a/src/luasocket.h
+++ b/src/luasocket.h
@@ -13,8 +13,8 @@
13/*-------------------------------------------------------------------------*\ 13/*-------------------------------------------------------------------------*\
14* Current socket library version 14* Current socket library version
15\*-------------------------------------------------------------------------*/ 15\*-------------------------------------------------------------------------*/
16#define LUASOCKET_VERSION "LuaSocket 2.0.1" 16#define LUASOCKET_VERSION "LuaSocket 2.0.2"
17#define LUASOCKET_COPYRIGHT "Copyright (C) 2004-2006 Diego Nehab" 17#define LUASOCKET_COPYRIGHT "Copyright (C) 2004-2007 Diego Nehab"
18#define LUASOCKET_AUTHORS "Diego Nehab" 18#define LUASOCKET_AUTHORS "Diego Nehab"
19 19
20/*-------------------------------------------------------------------------*\ 20/*-------------------------------------------------------------------------*\
diff --git a/src/mime.h b/src/mime.h
index 166cf5b..0fe8000 100644
--- a/src/mime.h
+++ b/src/mime.h
@@ -15,8 +15,8 @@
15/*-------------------------------------------------------------------------*\ 15/*-------------------------------------------------------------------------*\
16* Current MIME library version 16* Current MIME library version
17\*-------------------------------------------------------------------------*/ 17\*-------------------------------------------------------------------------*/
18#define MIME_VERSION "MIME 1.0.1" 18#define MIME_VERSION "MIME 1.0.2"
19#define MIME_COPYRIGHT "Copyright (C) 2004-2006 Diego Nehab" 19#define MIME_COPYRIGHT "Copyright (C) 2004-2007 Diego Nehab"
20#define MIME_AUTHORS "Diego Nehab" 20#define MIME_AUTHORS "Diego Nehab"
21 21
22/*-------------------------------------------------------------------------*\ 22/*-------------------------------------------------------------------------*\
diff --git a/src/mime.lua b/src/mime.lua
index 3182545..eb75db2 100644
--- a/src/mime.lua
+++ b/src/mime.lua
@@ -27,7 +27,8 @@ local function choose(table)
27 name, opt1, opt2 = "default", name, opt1 27 name, opt1, opt2 = "default", name, opt1
28 end 28 end
29 local f = table[name or "nil"] 29 local f = table[name or "nil"]
30 if not f then error("unknown key (" .. base.tostring(name) .. ")", 3) 30 if not f then
31 base.error("unknown key (" .. base.tostring(name) .. ")", 3)
31 else return f(opt1, opt2) end 32 else return f(opt1, opt2) end
32 end 33 end
33end 34end
diff --git a/src/usocket.c b/src/usocket.c
index e70806a..ef275b4 100644
--- a/src/usocket.c
+++ b/src/usocket.c
@@ -206,10 +206,10 @@ int socket_send(p_socket ps, const char *data, size_t count,
206 size_t *sent, p_timeout tm) 206 size_t *sent, p_timeout tm)
207{ 207{
208 int err; 208 int err;
209 *sent = 0;
209 /* avoid making system calls on closed sockets */ 210 /* avoid making system calls on closed sockets */
210 if (*ps == SOCKET_INVALID) return IO_CLOSED; 211 if (*ps == SOCKET_INVALID) return IO_CLOSED;
211 /* loop until we send something or we give up on error */ 212 /* loop until we send something or we give up on error */
212 *sent = 0;
213 for ( ;; ) { 213 for ( ;; ) {
214 long put = (long) send(*ps, data, count, 0); 214 long put = (long) send(*ps, data, count, 0);
215 /* if we sent anything, we are done */ 215 /* if we sent anything, we are done */
@@ -239,8 +239,8 @@ int socket_sendto(p_socket ps, const char *data, size_t count, size_t *sent,
239 SA *addr, socklen_t len, p_timeout tm) 239 SA *addr, socklen_t len, p_timeout tm)
240{ 240{
241 int err; 241 int err;
242 if (*ps == SOCKET_INVALID) return IO_CLOSED;
243 *sent = 0; 242 *sent = 0;
243 if (*ps == SOCKET_INVALID) return IO_CLOSED;
244 for ( ;; ) { 244 for ( ;; ) {
245 long put = (long) sendto(*ps, data, count, 0, addr, len); 245 long put = (long) sendto(*ps, data, count, 0, addr, len);
246 if (put > 0) { 246 if (put > 0) {
@@ -261,6 +261,7 @@ int socket_sendto(p_socket ps, const char *data, size_t count, size_t *sent,
261\*-------------------------------------------------------------------------*/ 261\*-------------------------------------------------------------------------*/
262int socket_recv(p_socket ps, char *data, size_t count, size_t *got, p_timeout tm) { 262int socket_recv(p_socket ps, char *data, size_t count, size_t *got, p_timeout tm) {
263 int err; 263 int err;
264 *got = 0;
264 if (*ps == SOCKET_INVALID) return IO_CLOSED; 265 if (*ps == SOCKET_INVALID) return IO_CLOSED;
265 for ( ;; ) { 266 for ( ;; ) {
266 long taken = (long) recv(*ps, data, count, 0); 267 long taken = (long) recv(*ps, data, count, 0);
@@ -269,7 +270,6 @@ int socket_recv(p_socket ps, char *data, size_t count, size_t *got, p_timeout tm
269 return IO_DONE; 270 return IO_DONE;
270 } 271 }
271 err = errno; 272 err = errno;
272 *got = 0;
273 if (taken == 0) return IO_CLOSED; 273 if (taken == 0) return IO_CLOSED;
274 if (err == EINTR) continue; 274 if (err == EINTR) continue;
275 if (err != EAGAIN) return err; 275 if (err != EAGAIN) return err;
@@ -284,6 +284,7 @@ int socket_recv(p_socket ps, char *data, size_t count, size_t *got, p_timeout tm
284int socket_recvfrom(p_socket ps, char *data, size_t count, size_t *got, 284int socket_recvfrom(p_socket ps, char *data, size_t count, size_t *got,
285 SA *addr, socklen_t *len, p_timeout tm) { 285 SA *addr, socklen_t *len, p_timeout tm) {
286 int err; 286 int err;
287 *got = 0;
287 if (*ps == SOCKET_INVALID) return IO_CLOSED; 288 if (*ps == SOCKET_INVALID) return IO_CLOSED;
288 for ( ;; ) { 289 for ( ;; ) {
289 long taken = (long) recvfrom(*ps, data, count, 0, addr, len); 290 long taken = (long) recvfrom(*ps, data, count, 0, addr, len);
@@ -292,7 +293,6 @@ int socket_recvfrom(p_socket ps, char *data, size_t count, size_t *got,
292 return IO_DONE; 293 return IO_DONE;
293 } 294 }
294 err = errno; 295 err = errno;
295 *got = 0;
296 if (taken == 0) return IO_CLOSED; 296 if (taken == 0) return IO_CLOSED;
297 if (err == EINTR) continue; 297 if (err == EINTR) continue;
298 if (err != EAGAIN) return err; 298 if (err != EAGAIN) return err;
diff --git a/src/wsocket.c b/src/wsocket.c
index dce333b..e247777 100644
--- a/src/wsocket.c
+++ b/src/wsocket.c
@@ -201,10 +201,10 @@ int socket_send(p_socket ps, const char *data, size_t count,
201 size_t *sent, p_timeout tm) 201 size_t *sent, p_timeout tm)
202{ 202{
203 int err; 203 int err;
204 *sent = 0;
204 /* avoid making system calls on closed sockets */ 205 /* avoid making system calls on closed sockets */
205 if (*ps == SOCKET_INVALID) return IO_CLOSED; 206 if (*ps == SOCKET_INVALID) return IO_CLOSED;
206 /* loop until we send something or we give up on error */ 207 /* loop until we send something or we give up on error */
207 *sent = 0;
208 for ( ;; ) { 208 for ( ;; ) {
209 /* try to send something */ 209 /* try to send something */
210 int put = send(*ps, data, (int) count, 0); 210 int put = send(*ps, data, (int) count, 0);
@@ -231,8 +231,8 @@ int socket_sendto(p_socket ps, const char *data, size_t count, size_t *sent,
231 SA *addr, socklen_t len, p_timeout tm) 231 SA *addr, socklen_t len, p_timeout tm)
232{ 232{
233 int err; 233 int err;
234 if (*ps == SOCKET_INVALID) return IO_CLOSED;
235 *sent = 0; 234 *sent = 0;
235 if (*ps == SOCKET_INVALID) return IO_CLOSED;
236 for ( ;; ) { 236 for ( ;; ) {
237 int put = sendto(*ps, data, (int) count, 0, addr, len); 237 int put = sendto(*ps, data, (int) count, 0, addr, len);
238 if (put > 0) { 238 if (put > 0) {
@@ -251,8 +251,8 @@ int socket_sendto(p_socket ps, const char *data, size_t count, size_t *sent,
251\*-------------------------------------------------------------------------*/ 251\*-------------------------------------------------------------------------*/
252int socket_recv(p_socket ps, char *data, size_t count, size_t *got, p_timeout tm) { 252int socket_recv(p_socket ps, char *data, size_t count, size_t *got, p_timeout tm) {
253 int err; 253 int err;
254 if (*ps == SOCKET_INVALID) return IO_CLOSED;
255 *got = 0; 254 *got = 0;
255 if (*ps == SOCKET_INVALID) return IO_CLOSED;
256 for ( ;; ) { 256 for ( ;; ) {
257 int taken = recv(*ps, data, (int) count, 0); 257 int taken = recv(*ps, data, (int) count, 0);
258 if (taken > 0) { 258 if (taken > 0) {
@@ -273,8 +273,8 @@ int socket_recv(p_socket ps, char *data, size_t count, size_t *got, p_timeout tm
273int socket_recvfrom(p_socket ps, char *data, size_t count, size_t *got, 273int socket_recvfrom(p_socket ps, char *data, size_t count, size_t *got,
274 SA *addr, socklen_t *len, p_timeout tm) { 274 SA *addr, socklen_t *len, p_timeout tm) {
275 int err; 275 int err;
276 if (*ps == SOCKET_INVALID) return IO_CLOSED;
277 *got = 0; 276 *got = 0;
277 if (*ps == SOCKET_INVALID) return IO_CLOSED;
278 for ( ;; ) { 278 for ( ;; ) {
279 int taken = recvfrom(*ps, data, (int) count, 0, addr, len); 279 int taken = recvfrom(*ps, data, (int) count, 0, addr, len);
280 if (taken > 0) { 280 if (taken > 0) {
diff --git a/test/testclnt.lua b/test/testclnt.lua
index 304cfd3..2f4ffad 100644
--- a/test/testclnt.lua
+++ b/test/testclnt.lua
@@ -498,7 +498,63 @@ remote(string.format([[
498end 498end
499 499
500------------------------------------------------------------------------ 500------------------------------------------------------------------------
501 501function test_readafterclose()
502 local back, partial, err
503 local str = 'little string'
504 reconnect()
505 pass("trying repeated '*a' pattern")
506 remote (string.format ([[
507 data:send('%s')
508 data:close()
509 data = nil
510 ]], str))
511 back, err, partial = data:receive("*a")
512 assert(back == str, "unexpected data read")
513 back, err, partial = data:receive("*a")
514 assert(back == nil and err == "closed", "should have returned 'closed'")
515 print("ok")
516 reconnect()
517 pass("trying active close before '*a'")
518 remote (string.format ([[
519 data:close()
520 data = nil
521 ]]))
522 data:close()
523 back, err, partial = data:receive("*a")
524 assert(back == nil and err == "closed", "should have returned 'closed'")
525 print("ok")
526 reconnect()
527 pass("trying active close before '*l'")
528 remote (string.format ([[
529 data:close()
530 data = nil
531 ]]))
532 data:close()
533 back, err, partial = data:receive()
534 assert(back == nil and err == "closed", "should have returned 'closed'")
535 print("ok")
536 reconnect()
537 pass("trying active close before raw 1")
538 remote (string.format ([[
539 data:close()
540 data = nil
541 ]]))
542 data:close()
543 back, err, partial = data:receive(1)
544 assert(back == nil and err == "closed", "should have returned 'closed'")
545 print("ok")
546 reconnect()
547 pass("trying active close before raw 0")
548 remote (string.format ([[
549 data:close()
550 data = nil
551 ]]))
552 data:close()
553 back, err, partial = data:receive(0)
554 assert(back == nil and err == "closed", "should have returned 'closed'")
555 print("ok")
556os.exit()
557end
502 558
503test("method registration") 559test("method registration")
504test_methods(socket.tcp(), { 560test_methods(socket.tcp(), {
@@ -541,6 +597,9 @@ test_methods(socket.udp(), {
541 "settimeout" 597 "settimeout"
542}) 598})
543 599
600test("testing read after close")
601test_readafterclose()
602
544test("select function") 603test("select function")
545test_selectbugs() 604test_selectbugs()
546 605
diff --git a/test/testmesg.lua b/test/testmesg.lua
index 04305f4..580693b 100644
--- a/test/testmesg.lua
+++ b/test/testmesg.lua
@@ -62,7 +62,7 @@ source = smtp.message{
62 headers = { 62 headers = {
63 ["ConTenT-tYpE"] = 'image/png; name="luasocket.png"', 63 ["ConTenT-tYpE"] = 'image/png; name="luasocket.png"',
64 ["content-disposition"] = 'attachment; filename="luasocket.png"', 64 ["content-disposition"] = 'attachment; filename="luasocket.png"',
65 ["content-description"] = 'a beautiful image', 65 ["content-description"] = 'our logo',
66 ["content-transfer-encoding"] = "BASE64" 66 ["content-transfer-encoding"] = "BASE64"
67 }, 67 },
68 body = ltn12.source.chain( 68 body = ltn12.source.chain(