aboutsummaryrefslogtreecommitdiffstats
path: root/main/lua-socket/git.patch
blob: 65c5595a603277ac5044e9ac2da0a24ca98baf9d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
diff --git a/doc/reference.css b/doc/reference.css
index b1dd25d..04e38cf 100644
--- a/doc/reference.css
+++ b/doc/reference.css
@@ -2,6 +2,7 @@ body {
     margin-left: 1em; 
     margin-right: 1em; 
     font-family: "Verdana", sans-serif; 
+    background: #ffffff;
 }
 
 tt {
diff --git a/etc/dispatch.lua b/etc/dispatch.lua
index cab7f59..2485415 100644
--- a/etc/dispatch.lua
+++ b/etc/dispatch.lua
@@ -5,6 +5,7 @@
 -----------------------------------------------------------------------------
 local base = _G
 local table = require("table")
+local string = require("string")
 local socket = require("socket")
 local coroutine = require("coroutine")
 module("dispatch")
@@ -43,26 +44,32 @@ end
 -----------------------------------------------------------------------------
 -- Mega hack. Don't try to do this at home.
 -----------------------------------------------------------------------------
--- we can't yield across calls to protect, so we rewrite it with coxpcall
+-- we can't yield across calls to protect on Lua 5.1, so we rewrite it with
+-- coroutines
 -- make sure you don't require any module that uses socket.protect before
 -- loading our hack
-function socket.protect(f)
-  return function(...)
-    local co = coroutine.create(f)
-    while true do
-      local results = {coroutine.resume(co, ...)}
-      local status = table.remove(results, 1)
-      if not status then
-        if base.type(results[1]) == 'table' then
-          return nil, results[1][1]
-        else base.error(results[1]) end
-      end
-      if coroutine.status(co) == "suspended" then
-        arg = {coroutine.yield(base.unpack(results))}
+if string.sub(base._VERSION, -3) == "5.1" then
+  local function _protect(co, status, ...)
+    if not status then
+      local msg = ...
+      if base.type(msg) == 'table' then
+        return nil, msg[1]
       else
-        return base.unpack(results)
+        base.error(msg, 0)
       end
     end
+    if coroutine.status(co) == "suspended" then
+      return _protect(co, coroutine.resume(co, coroutine.yield(...)))
+    else
+      return ...
+    end
+  end
+
+  function socket.protect(f)
+    return function(...)
+      local co = coroutine.create(f)
+      return _protect(co, coroutine.resume(co, ...))
+    end
   end
 end
 
diff --git a/makefile b/makefile
index 04cd894..e34f5a9 100644
--- a/makefile
+++ b/makefile
@@ -5,12 +5,12 @@
 # Targets:
 #   install            install system independent support
 #   install-unix           also install unix-only support
-#   install-both       install for both lua5.1 and lua5.2 
-#   install-both-unix      also install unix-only 
+#   install-both       install for lua51 lua52 lua53
+#   install-both-unix      also install unix-only
 #   print	           print the build settings
 
 PLAT?= linux
-PLATS= macosx linux win32 mingw
+PLATS= macosx linux win32 mingw freebsd
 
 all: $(PLAT)
 
@@ -24,20 +24,26 @@ test:
 	lua test/hello.lua
 
 install-both:
-	$(MAKE) clean 
+	$(MAKE) clean
 	@cd src; $(MAKE) $(PLAT) LUAV=5.1
 	@cd src; $(MAKE) install LUAV=5.1
-	$(MAKE) clean 
+	$(MAKE) clean
 	@cd src; $(MAKE) $(PLAT) LUAV=5.2
 	@cd src; $(MAKE) install LUAV=5.2
+	$(MAKE) clean
+	@cd src; $(MAKE) $(PLAT) LUAV=5.3
+	@cd src; $(MAKE) install LUAV=5.3
 
 install-both-unix:
-	$(MAKE) clean 
+	$(MAKE) clean
 	@cd src; $(MAKE) $(PLAT) LUAV=5.1
 	@cd src; $(MAKE) install-unix LUAV=5.1
-	$(MAKE) clean 
+	$(MAKE) clean
 	@cd src; $(MAKE) $(PLAT) LUAV=5.2
 	@cd src; $(MAKE) install-unix LUAV=5.2
+	$(MAKE) clean
+	@cd src; $(MAKE) $(PLAT) LUAV=5.3
+	@cd src; $(MAKE) install-unix LUAV=5.3
 
 .PHONY: test
 
diff --git a/src/buffer.c b/src/buffer.c
index 4ef4e8e..423d804 100644
--- a/src/buffer.c
+++ b/src/buffer.c
@@ -78,9 +78,7 @@ int buffer_meth_send(lua_State *L, p_buffer buf) {
     const char *data = luaL_checklstring(L, 2, &size);
     long start = (long) luaL_optnumber(L, 3, 1);
     long end = (long) luaL_optnumber(L, 4, -1);
-#ifdef LUASOCKET_DEBUG
-    p_timeout tm = timeout_markstart(buf->tm);
-#endif
+    timeout_markstart(buf->tm);
     if (start < 0) start = (long) (size+start+1);
     if (end < 0) end = (long) (size+end+1);
     if (start < 1) start = (long) 1;
@@ -98,7 +96,7 @@ int buffer_meth_send(lua_State *L, p_buffer buf) {
     }
 #ifdef LUASOCKET_DEBUG
     /* push time elapsed during operation as the last return value */
-    lua_pushnumber(L, timeout_gettime() - timeout_getstart(tm));
+    lua_pushnumber(L, timeout_gettime() - timeout_getstart(buf->tm));
 #endif
     return lua_gettop(L) - top;
 }
@@ -111,9 +109,7 @@ int buffer_meth_receive(lua_State *L, p_buffer buf) {
     luaL_Buffer b;
     size_t size;
     const char *part = luaL_optlstring(L, 3, "", &size);
-#ifdef LUASOCKET_DEBUG
-    p_timeout tm = timeout_markstart(buf->tm);
-#endif
+    timeout_markstart(buf->tm);
     /* initialize buffer with optional extra prefix 
      * (useful for concatenating previous partial results) */
     luaL_buffinit(L, &b);
@@ -149,7 +145,7 @@ int buffer_meth_receive(lua_State *L, p_buffer buf) {
     }
 #ifdef LUASOCKET_DEBUG
     /* push time elapsed during operation as the last return value */
-    lua_pushnumber(L, timeout_gettime() - timeout_getstart(tm));
+    lua_pushnumber(L, timeout_gettime() - timeout_getstart(buf->tm));
 #endif
     return lua_gettop(L) - top;
 }
diff --git a/src/except.c b/src/except.c
index 002e701..4faa208 100644
--- a/src/except.c
+++ b/src/except.c
@@ -9,6 +9,15 @@
 
 #include "except.h"
 
+#if LUA_VERSION_NUM < 502
+#define lua_pcallk(L, na, nr, err, ctx, cont) \
+    ((void)ctx,(void)cont,lua_pcall(L, na, nr, err))
+#endif
+
+#if LUA_VERSION_NUM < 503
+typedef int lua_KContext;
+#endif
+
 /*=========================================================================*\
 * Internal function prototypes.
 \*=========================================================================*/
@@ -73,14 +82,30 @@ static int unwrap(lua_State *L) {
     } else return 0;
 }
 
+static int protected_finish(lua_State *L, int status, lua_KContext ctx) {
+    (void)ctx;
+    if (status != 0 && status != LUA_YIELD) {
+        if (unwrap(L)) return 2;
+        else return lua_error(L);
+    } else return lua_gettop(L);
+}
+
+#if LUA_VERSION_NUM == 502
+static int protected_cont(lua_State *L) {
+    int ctx = 0;
+    int status = lua_getctx(L, &ctx);
+    return protected_finish(L, status, ctx);
+}
+#else
+#define protected_cont protected_finish
+#endif
+
 static int protected_(lua_State *L) {
+    int status;
     lua_pushvalue(L, lua_upvalueindex(1));
     lua_insert(L, 1);
-    if (lua_pcall(L, lua_gettop(L) - 1, LUA_MULTRET, 0) != 0) {
-        if (unwrap(L)) return 2;
-        else lua_error(L);
-        return 0;
-    } else return lua_gettop(L);
+    status = lua_pcallk(L, lua_gettop(L) - 1, LUA_MULTRET, 0, 0, protected_cont);
+    return protected_finish(L, status, 0);
 }
 
 static int global_protect(lua_State *L) {
diff --git a/src/ftp.lua b/src/ftp.lua
index ea1145b..917cd89 100644
--- a/src/ftp.lua
+++ b/src/ftp.lua
@@ -268,7 +268,7 @@ _M.command = socket.protect(function(cmdt)
     cmdt = override(cmdt)
     socket.try(cmdt.host, "missing hostname")
     socket.try(cmdt.command, "missing command")
-    local f = open(cmdt.host, cmdt.port, cmdt.create)
+    local f = _M.open(cmdt.host, cmdt.port, cmdt.create)
     f:greet()
     f:login(cmdt.user, cmdt.password)
     f.try(f.tp:command(cmdt.command, cmdt.argument))
diff --git a/src/http.lua b/src/http.lua
index ac4b2d6..d5457f6 100644
--- a/src/http.lua
+++ b/src/http.lua
@@ -22,7 +22,7 @@ local _M = socket.http
 -- Program constants
 -----------------------------------------------------------------------------
 -- connection timeout in seconds
-TIMEOUT = 60
+_M.TIMEOUT = 60
 -- default port for document retrieval
 _M.PORT = 80
 -- user agent field sent in request
@@ -186,7 +186,7 @@ end
 local function adjusturi(reqt)
     local u = reqt
     -- if there is a proxy, we need the full url. otherwise, just a part.
-    if not reqt.proxy and not PROXY then
+    if not reqt.proxy and not _M.PROXY then
         u = {
            path = socket.try(reqt.path, "invalid path 'nil'"),
            params = reqt.params,
@@ -198,7 +198,7 @@ local function adjusturi(reqt)
 end
 
 local function adjustproxy(reqt)
-    local proxy = reqt.proxy or PROXY
+    local proxy = reqt.proxy or _M.PROXY
     if proxy then
         proxy = url.parse(proxy)
         return proxy.host, proxy.port or 3128
@@ -209,9 +209,10 @@ end
 
 local function adjustheaders(reqt)
     -- default headers
+    local host = string.gsub(reqt.authority, "^.-@", "")
     local lower = {
         ["user-agent"] = _M.USERAGENT,
-        ["host"] = reqt.host,
+        ["host"] = host,
         ["connection"] = "close, TE",
         ["te"] = "trailers"
     }
@@ -351,4 +352,4 @@ _M.request = socket.protect(function(reqt, body)
     else return trequest(reqt) end
 end)
 
-return _M
\ No newline at end of file
+return _M
diff --git a/src/inet.c b/src/inet.c
index 1a411f6..48e654b 100644
--- a/src/inet.c
+++ b/src/inet.c
@@ -183,6 +183,7 @@ static int inet_global_getaddrinfo(lua_State *L)
         ret = getnameinfo(iterator->ai_addr, (socklen_t) iterator->ai_addrlen, 
             hbuf, (socklen_t) sizeof(hbuf), NULL, 0, NI_NUMERICHOST);
         if (ret){
+          freeaddrinfo(resolved);
           lua_pushnil(L);
           lua_pushstring(L, socket_gaistrerror(ret));
           return 2;
diff --git a/src/ltn12.lua b/src/ltn12.lua
index 5b10f56..1014de2 100644
--- a/src/ltn12.lua
+++ b/src/ltn12.lua
@@ -139,7 +139,9 @@ function source.rewind(src)
     end
 end
 
-function source.chain(src, f)
+-- chains a source with one or several filter(s)
+function source.chain(src, f, ...)
+    if ... then f=filter.chain(f, ...) end
     base.assert(src and f)
     local last_in, last_out = "", ""
     local state = "feeding"
@@ -254,8 +256,13 @@ function sink.error(err)
     end
 end
 
--- chains a sink with a filter
-function sink.chain(f, snk)
+-- chains a sink with one or several filter(s)
+function sink.chain(f, snk, ...)
+    if ... then
+        local args = { f, snk, ... }
+        snk = table.remove(args, #args)
+        f = filter.chain(unpack(args))
+    end
     base.assert(f and snk)
     return function(chunk, err)
         if chunk ~= "" then
diff --git a/src/luasocket.c b/src/luasocket.c
index e6ee747..c4eeab3 100644
--- a/src/luasocket.c
+++ b/src/luasocket.c
@@ -18,7 +18,6 @@
 #include "lua.h"
 #include "lauxlib.h"
 
-
 /*=========================================================================*\
 * LuaSocket includes
 \*=========================================================================*/
@@ -64,7 +63,7 @@ static luaL_Reg func[] = {
 * Skip a few arguments
 \*-------------------------------------------------------------------------*/
 static int global_skip(lua_State *L) {
-    int amount = luaL_checkint(L, 1);
+    int amount = luaL_checkinteger(L, 1);
     int ret = lua_gettop(L) - amount - 1;
     return ret >= 0 ? ret : 0;
 }
@@ -78,14 +77,6 @@ static int global_unload(lua_State *L) {
     return 0;
 }
 
-#if LUA_VERSION_NUM > 501
-int luaL_typerror (lua_State *L, int narg, const char *tname) {
-  const char *msg = lua_pushfstring(L, "%s expected, got %s",
-                                    tname, luaL_typename(L, narg));
-  return luaL_argerror(L, narg, msg);
-}
-#endif
-
 /*-------------------------------------------------------------------------*\
 * Setup basic stuff.
 \*-------------------------------------------------------------------------*/
diff --git a/src/makefile b/src/makefile
index c24e61b..7f118a7 100644
--- a/src/makefile
+++ b/src/makefile
@@ -20,6 +20,12 @@ PLAT?=linux
 # lua version to build against
 LUAV?=5.1
 
+# MYCFLAGS: to be set by user if needed
+MYCFLAGS=
+
+# MYLDFLAGS: to be set by user if needed
+MYLDFLAGS=
+
 # DEBUG: NODEBUG DEBUG
 # debug mode causes luasocket to collect and returns timing information useful
 # for testing and debugging luasocket itself
@@ -40,7 +46,6 @@ LUAPREFIX_macosx?=/opt/local
 CDIR_macosx?=lib/lua/$(LUAV)
 LDIR_macosx?=share/lua/$(LUAV)
 
-
 # LUAINC_linux:
 # /usr/include/lua$(LUAV)
 # /usr/local/include
@@ -52,8 +57,17 @@ LUAPREFIX_linux?=/usr/local
 CDIR_linux?=lib/lua/$(LUAV)
 LDIR_linux?=share/lua/$(LUAV)
 
+# LUAINC_freebsd:
+# /usr/local/include/lua$(LUAV)
+# where lua headers are found for linux builds
+LUAINC_freebsd_base?=/usr/local/include/
+LUAINC_freebsd?=$(LUAINC_freebsd_base)/lua$(LUAV)
+LUAPREFIX_freebsd?=/usr/local/
+CDIR_freebsd?=lib/lua/$(LUAV)
+LDIR_freebsd?=share/lua/$(LUAV)
+
 # where lua headers are found for mingw builds
-# LUAINC_mingw: 
+# LUAINC_mingw:
 # /opt/local/include
 LUAINC_mingw_base?=/usr/include
 LUAINC_mingw?=$(LUAINC_mingw_base)/lua/$(LUAV)
@@ -133,7 +147,7 @@ DEF_macosx= -DLUASOCKET_$(DEBUG) -DUNIX_HAS_SUN_LEN -DLUA_$(COMPAT)_MODULE \
 	-DLUASOCKET_API='__attribute__((visibility("default")))' \
 	-DUNIX_API='__attribute__((visibility("default")))' \
 	-DMIME_API='__attribute__((visibility("default")))'
-CFLAGS_macosx= -I$(LUAINC) $(DEF) -pedantic -Wall -O2 -fno-common \
+CFLAGS_macosx= -I$(LUAINC) $(DEF) -Wall -O2 -fno-common \
 	-fvisibility=hidden
 LDFLAGS_macosx= -bundle -undefined dynamic_lookup -o 
 LD_macosx= export MACOSX_DEPLOYMENT_TARGET="10.3"; gcc
@@ -149,7 +163,7 @@ DEF_linux=-DLUASOCKET_$(DEBUG) -DLUA_$(COMPAT)_MODULE \
 	-DLUASOCKET_API='__attribute__((visibility("default")))' \
 	-DUNIX_API='__attribute__((visibility("default")))' \
 	-DMIME_API='__attribute__((visibility("default")))'
-CFLAGS_linux= -I$(LUAINC) $(DEF) -pedantic -Wall -Wshadow -Wextra \
+CFLAGS_linux= -I$(LUAINC) $(DEF) -Wall -Wshadow -Wextra \
 	-Wimplicit -O2 -ggdb3 -fpic -fvisibility=hidden
 LDFLAGS_linux=-O -shared -fpic -o 
 LD_linux=gcc
@@ -157,6 +171,22 @@ SOCKET_linux=usocket.o
 
 #------
 # Compiler and linker settings
+# for FreeBSD
+SO_freebsd=so
+O_freebsd=o
+CC_freebsd=gcc
+DEF_freebsd=-DLUASOCKET_$(DEBUG) -DLUA_$(COMPAT)_MODULE \
+	-DLUASOCKET_API='__attribute__((visibility("default")))' \
+	-DUNIX_API='__attribute__((visibility("default")))' \
+	-DMIME_API='__attribute__((visibility("default")))'
+CFLAGS_freebsd= -I$(LUAINC) $(DEF) -Wall -Wshadow -Wextra \
+	-Wimplicit -O2 -ggdb3 -fpic -fvisibility=hidden
+LDFLAGS_freebsd=-O -shared -fpic -o 
+LD_freebsd=gcc
+SOCKET_freebsd=usocket.o
+
+#------
+# Compiler and linker settings
 # for MingW
 SO_mingw=dll
 O_mingw=o
@@ -164,7 +194,7 @@ CC_mingw=gcc
 DEF_mingw= -DLUASOCKET_INET_PTON -DLUASOCKET_$(DEBUG) -DLUA_$(COMPAT)_MODULE \
 	-DWINVER=0x0501 -DLUASOCKET_API='__declspec(dllexport)' \
 	-DMIME_API='__declspec(dllexport)'
-CFLAGS_mingw= -I$(LUAINC) $(DEF) -pedantic -Wall -O2 -fno-common \
+CFLAGS_mingw= -I$(LUAINC) $(DEF) -Wall -O2 -fno-common \
 	-fvisibility=hidden
 LDFLAGS_mingw= $(LUALIB) -shared -Wl,-s -lws2_32 -o 
 LD_mingw=gcc
@@ -195,7 +225,7 @@ SOCKET_win32=wsocket.obj
 .SUFFIXES: .obj
 
 .c.obj:
-	$(CC) $(CFLAGS) //Fo"$@" //c $<
+	$(CC) $(LUASOCKET_CFLAGS) //Fo"$@" //c $<
 
 #------
 # Output file names
@@ -215,8 +245,8 @@ SOCKET=$(SOCKET_$(PLAT))
 #
 CC=$(CC_$(PLAT))
 DEF=$(DEF_$(PLAT))
-CFLAGS=$(CFLAGS_$(PLAT))
-LDFLAGS=$(LDFLAGS_$(PLAT))
+CFLAGS=$(MYCFLAGS) $(CFLAGS_$(PLAT))
+LDFLAGS=$(MYLDFLAGS) $(LDFLAGS_$(PLAT))
 LD=$(LD_$(PLAT))
 LUAINC= $(LUAINC_$(PLAT))
 LUALIB= $(LUALIB_$(PLAT))
@@ -259,7 +289,7 @@ UNIX_OBJS=\
 #------
 # Modules belonging to serial (device streams)
 #
-SERIAL_OBJS:=\
+SERIAL_OBJS=\
 	buffer.$(O) \
 	auxiliar.$(O) \
 	options.$(O) \
@@ -289,6 +319,10 @@ TO_TOP_LDIR= \
 #
 default: $(PLAT)
 
+
+freebsd:
+	$(MAKE) all-unix PLAT=freebsd
+
 macosx:
 	$(MAKE) all-unix PLAT=macosx
 
diff --git a/src/mime.c b/src/mime.c
index dd37dcf..d121e9e 100644
--- a/src/mime.c
+++ b/src/mime.c
@@ -41,7 +41,7 @@ static size_t b64decode(UC c, UC *input, size_t size, luaL_Buffer *buffer);
 static void qpsetup(UC *class, UC *unbase);
 static void qpquote(UC c, luaL_Buffer *buffer);
 static size_t qpdecode(UC c, UC *input, size_t size, luaL_Buffer *buffer);
-static size_t qpencode(UC c, UC *input, size_t size, 
+static size_t qpencode(UC c, UC *input, size_t size,
         const char *marker, luaL_Buffer *buffer);
 static size_t qppad(UC *input, size_t size, luaL_Buffer *buffer);
 
@@ -103,15 +103,15 @@ MIME_API int luaopen_mime_core(lua_State *L)
 /*-------------------------------------------------------------------------*\
 * Incrementaly breaks a string into lines. The string can have CRLF breaks.
 * A, n = wrp(l, B, length)
-* A is a copy of B, broken into lines of at most 'length' bytes. 
-* 'l' is how many bytes are left for the first line of B. 
-* 'n' is the number of bytes left in the last line of A. 
+* A is a copy of B, broken into lines of at most 'length' bytes.
+* 'l' is how many bytes are left for the first line of B.
+* 'n' is the number of bytes left in the last line of A.
 \*-------------------------------------------------------------------------*/
 static int mime_global_wrp(lua_State *L)
 {
     size_t size = 0;
     int left = (int) luaL_checknumber(L, 1);
-    const UC *input = (UC *) luaL_optlstring(L, 2, NULL, &size);
+    const UC *input = (const UC *) luaL_optlstring(L, 2, NULL, &size);
     const UC *last = input + size;
     int length = (int) luaL_optnumber(L, 3, 76);
     luaL_Buffer buffer;
@@ -123,7 +123,7 @@ static int mime_global_wrp(lua_State *L)
         else lua_pushnil(L);
         lua_pushnumber(L, length);
         return 2;
-    } 
+    }
     luaL_buffinit(L, &buffer);
     while (input < last) {
         switch (*input) {
@@ -150,9 +150,9 @@ static int mime_global_wrp(lua_State *L)
 }
 
 /*-------------------------------------------------------------------------*\
-* Fill base64 decode map. 
+* Fill base64 decode map.
 \*-------------------------------------------------------------------------*/
-static void b64setup(UC *unbase) 
+static void b64setup(UC *unbase)
 {
     int i;
     for (i = 0; i <= 255; i++) unbase[i] = (UC) 255;
@@ -161,11 +161,11 @@ static void b64setup(UC *unbase)
 }
 
 /*-------------------------------------------------------------------------*\
-* Acumulates bytes in input buffer until 3 bytes are available. 
+* Acumulates bytes in input buffer until 3 bytes are available.
 * Translate the 3 bytes into Base64 form and append to buffer.
 * Returns new number of bytes in buffer.
 \*-------------------------------------------------------------------------*/
-static size_t b64encode(UC c, UC *input, size_t size, 
+static size_t b64encode(UC c, UC *input, size_t size,
         luaL_Buffer *buffer)
 {
     input[size++] = c;
@@ -174,7 +174,7 @@ static size_t b64encode(UC c, UC *input, size_t size,
         unsigned long value = 0;
         value += input[0]; value <<= 8;
         value += input[1]; value <<= 8;
-        value += input[2]; 
+        value += input[2];
         code[3] = b64base[value & 0x3f]; value >>= 6;
         code[2] = b64base[value & 0x3f]; value >>= 6;
         code[1] = b64base[value & 0x3f]; value >>= 6;
@@ -186,11 +186,11 @@ static size_t b64encode(UC c, UC *input, size_t size,
 }
 
 /*-------------------------------------------------------------------------*\
-* Encodes the Base64 last 1 or 2 bytes and adds padding '=' 
+* Encodes the Base64 last 1 or 2 bytes and adds padding '='
 * Result, if any, is appended to buffer.
 * Returns 0.
 \*-------------------------------------------------------------------------*/
-static size_t b64pad(const UC *input, size_t size, 
+static size_t b64pad(const UC *input, size_t size,
         luaL_Buffer *buffer)
 {
     unsigned long value = 0;
@@ -203,7 +203,7 @@ static size_t b64pad(const UC *input, size_t size,
             luaL_addlstring(buffer, (char *) code, 4);
             break;
         case 2:
-            value = input[0]; value <<= 8; 
+            value = input[0]; value <<= 8;
             value |= input[1]; value <<= 2;
             code[2] = b64base[value & 0x3f]; value >>= 6;
             code[1] = b64base[value & 0x3f]; value >>= 6;
@@ -217,11 +217,11 @@ static size_t b64pad(const UC *input, size_t size,
 }
 
 /*-------------------------------------------------------------------------*\
-* Acumulates bytes in input buffer until 4 bytes are available. 
+* Acumulates bytes in input buffer until 4 bytes are available.
 * Translate the 4 bytes from Base64 form and append to buffer.
 * Returns new number of bytes in buffer.
 \*-------------------------------------------------------------------------*/
-static size_t b64decode(UC c, UC *input, size_t size, 
+static size_t b64decode(UC c, UC *input, size_t size,
         luaL_Buffer *buffer)
 {
     /* ignore invalid characters */
@@ -239,7 +239,7 @@ static size_t b64decode(UC c, UC *input, size_t size,
         decoded[1] = (UC) (value & 0xff); value >>= 8;
         decoded[0] = (UC) value;
         /* take care of paddding */
-        valid = (input[2] == '=') ? 1 : (input[3] == '=') ? 2 : 3; 
+        valid = (input[2] == '=') ? 1 : (input[3] == '=') ? 2 : 3;
         luaL_addlstring(buffer, (char *) decoded, valid);
         return 0;
     /* need more data */
@@ -251,7 +251,7 @@ static size_t b64decode(UC c, UC *input, size_t size,
 * A, B = b64(C, D)
 * A is the encoded version of the largest prefix of C .. D that is
 * divisible by 3. B has the remaining bytes of C .. D, *without* encoding.
-* The easiest thing would be to concatenate the two strings and 
+* The easiest thing would be to concatenate the two strings and
 * encode the result, but we can't afford that or Lua would dupplicate
 * every chunk we received.
 \*-------------------------------------------------------------------------*/
@@ -259,7 +259,7 @@ static int mime_global_b64(lua_State *L)
 {
     UC atom[3];
     size_t isize = 0, asize = 0;
-    const UC *input = (UC *) luaL_optlstring(L, 1, NULL, &isize);
+    const UC *input = (const UC *) luaL_optlstring(L, 1, NULL, &isize);
     const UC *last = input + isize;
     luaL_Buffer buffer;
     /* end-of-input blackhole */
@@ -272,9 +272,9 @@ static int mime_global_b64(lua_State *L)
     lua_settop(L, 2);
     /* process first part of the input */
     luaL_buffinit(L, &buffer);
-    while (input < last) 
+    while (input < last)
         asize = b64encode(*input++, atom, asize, &buffer);
-    input = (UC *) luaL_optlstring(L, 2, NULL, &isize);
+    input = (const UC *) luaL_optlstring(L, 2, NULL, &isize);
     /* if second part is nil, we are done */
     if (!input) {
         size_t osize = 0;
@@ -288,7 +288,7 @@ static int mime_global_b64(lua_State *L)
     }
     /* otherwise process the second part */
     last = input + isize;
-    while (input < last) 
+    while (input < last)
         asize = b64encode(*input++, atom, asize, &buffer);
     luaL_pushresult(&buffer);
     lua_pushlstring(L, (char *) atom, asize);
@@ -305,7 +305,7 @@ static int mime_global_unb64(lua_State *L)
 {
     UC atom[4];
     size_t isize = 0, asize = 0;
-    const UC *input = (UC *) luaL_optlstring(L, 1, NULL, &isize);
+    const UC *input = (const UC *) luaL_optlstring(L, 1, NULL, &isize);
     const UC *last = input + isize;
     luaL_Buffer buffer;
     /* end-of-input blackhole */
@@ -318,9 +318,9 @@ static int mime_global_unb64(lua_State *L)
     lua_settop(L, 2);
     /* process first part of the input */
     luaL_buffinit(L, &buffer);
-    while (input < last) 
+    while (input < last)
         asize = b64decode(*input++, atom, asize, &buffer);
-    input = (UC *) luaL_optlstring(L, 2, NULL, &isize);
+    input = (const UC *) luaL_optlstring(L, 2, NULL, &isize);
     /* if second is nil, we are done */
     if (!input) {
         size_t osize = 0;
@@ -333,7 +333,7 @@ static int mime_global_unb64(lua_State *L)
     }
     /* otherwise, process the rest of the input */
     last = input + isize;
-    while (input < last) 
+    while (input < last)
         asize = b64decode(*input++, atom, asize, &buffer);
     luaL_pushresult(&buffer);
     lua_pushlstring(L, (char *) atom, asize);
@@ -349,7 +349,7 @@ static int mime_global_unb64(lua_State *L)
 * 9 and 32 can be plain, unless in the end of a line, where must be =XX
 * encoded lines must be no longer than 76 not counting CRLF
 * soft line-break are =CRLF
-* To encode one byte, we need to see the next two. 
+* To encode one byte, we need to see the next two.
 * Worst case is when we see a space, and wonder if a CRLF is comming
 \*-------------------------------------------------------------------------*/
 /*-------------------------------------------------------------------------*\
@@ -362,7 +362,7 @@ static void qpsetup(UC *cl, UC *unbase)
     for (i = 0; i < 256; i++) cl[i] = QP_QUOTED;
     for (i = 33; i <= 60; i++) cl[i] = QP_PLAIN;
     for (i = 62; i <= 126; i++) cl[i] = QP_PLAIN;
-    cl['\t'] = QP_IF_LAST; 
+    cl['\t'] = QP_IF_LAST;
     cl[' '] = QP_IF_LAST;
     cl['\r'] = QP_CR;
     for (i = 0; i < 256; i++) unbase[i] = 255;
@@ -388,9 +388,9 @@ static void qpquote(UC c, luaL_Buffer *buffer)
 
 /*-------------------------------------------------------------------------*\
 * Accumulate characters until we are sure about how to deal with them.
-* Once we are sure, output to the buffer, in the correct form. 
+* Once we are sure, output to the buffer, in the correct form.
 \*-------------------------------------------------------------------------*/
-static size_t qpencode(UC c, UC *input, size_t size, 
+static size_t qpencode(UC c, UC *input, size_t size,
         const char *marker, luaL_Buffer *buffer)
 {
     input[size++] = c;
@@ -431,7 +431,7 @@ static size_t qpencode(UC c, UC *input, size_t size,
 }
 
 /*-------------------------------------------------------------------------*\
-* Deal with the final characters 
+* Deal with the final characters
 \*-------------------------------------------------------------------------*/
 static size_t qppad(UC *input, size_t size, luaL_Buffer *buffer)
 {
@@ -448,8 +448,8 @@ static size_t qppad(UC *input, size_t size, luaL_Buffer *buffer)
 * Incrementally converts a string to quoted-printable
 * A, B = qp(C, D, marker)
 * Marker is the text to be used to replace CRLF sequences found in A.
-* A is the encoded version of the largest prefix of C .. D that 
-* can be encoded without doubts. 
+* A is the encoded version of the largest prefix of C .. D that
+* can be encoded without doubts.
 * B has the remaining bytes of C .. D, *without* encoding.
 \*-------------------------------------------------------------------------*/
 static int mime_global_qp(lua_State *L)
@@ -457,7 +457,7 @@ static int mime_global_qp(lua_State *L)
 
     size_t asize = 0, isize = 0;
     UC atom[3];
-    const UC *input = (UC *) luaL_optlstring(L, 1, NULL, &isize);
+    const UC *input = (const UC *) luaL_optlstring(L, 1, NULL, &isize);
     const UC *last = input + isize;
     const char *marker = luaL_optstring(L, 3, CRLF);
     luaL_Buffer buffer;
@@ -473,7 +473,7 @@ static int mime_global_qp(lua_State *L)
     luaL_buffinit(L, &buffer);
     while (input < last)
         asize = qpencode(*input++, atom, asize, marker, &buffer);
-    input = (UC *) luaL_optlstring(L, 2, NULL, &isize);
+    input = (const UC *) luaL_optlstring(L, 2, NULL, &isize);
     /* if second part is nil, we are done */
     if (!input) {
         asize = qppad(atom, asize, &buffer);
@@ -493,7 +493,7 @@ static int mime_global_qp(lua_State *L)
 
 /*-------------------------------------------------------------------------*\
 * Accumulate characters until we are sure about how to deal with them.
-* Once we are sure, output the to the buffer, in the correct form. 
+* Once we are sure, output the to the buffer, in the correct form.
 \*-------------------------------------------------------------------------*/
 static size_t qpdecode(UC c, UC *input, size_t size, luaL_Buffer *buffer) {
     int d;
@@ -501,8 +501,8 @@ static size_t qpdecode(UC c, UC *input, size_t size, luaL_Buffer *buffer) {
     /* deal with all characters we can deal */
     switch (input[0]) {
         /* if we have an escape character */
-        case '=': 
-            if (size < 3) return size; 
+        case '=':
+            if (size < 3) return size;
             /* eliminate soft line break */
             if (input[1] == '\r' && input[2] == '\n') return 0;
             /* decode quoted representation */
@@ -512,7 +512,7 @@ static size_t qpdecode(UC c, UC *input, size_t size, luaL_Buffer *buffer) {
             else luaL_addchar(buffer, (char) ((c << 4) + d));
             return 0;
         case '\r':
-            if (size < 2) return size; 
+            if (size < 2) return size;
             if (input[1] == '\n') luaL_addlstring(buffer, (char *)input, 2);
             return 0;
         default:
@@ -525,15 +525,15 @@ static size_t qpdecode(UC c, UC *input, size_t size, luaL_Buffer *buffer) {
 /*-------------------------------------------------------------------------*\
 * Incrementally decodes a string in quoted-printable
 * A, B = qp(C, D)
-* A is the decoded version of the largest prefix of C .. D that 
-* can be decoded without doubts. 
+* A is the decoded version of the largest prefix of C .. D that
+* can be decoded without doubts.
 * B has the remaining bytes of C .. D, *without* decoding.
 \*-------------------------------------------------------------------------*/
 static int mime_global_unqp(lua_State *L)
 {
     size_t asize = 0, isize = 0;
     UC atom[3];
-    const UC *input = (UC *) luaL_optlstring(L, 1, NULL, &isize);
+    const UC *input = (const UC *) luaL_optlstring(L, 1, NULL, &isize);
     const UC *last = input + isize;
     luaL_Buffer buffer;
     /* end-of-input blackhole */
@@ -548,14 +548,14 @@ static int mime_global_unqp(lua_State *L)
     luaL_buffinit(L, &buffer);
     while (input < last)
         asize = qpdecode(*input++, atom, asize, &buffer);
-    input = (UC *) luaL_optlstring(L, 2, NULL, &isize);
+    input = (const UC *) luaL_optlstring(L, 2, NULL, &isize);
     /* if second part is nil, we are done */
     if (!input) {
         luaL_pushresult(&buffer);
         if (!(*lua_tostring(L, -1))) lua_pushnil(L);
         lua_pushnil(L);
         return 2;
-    } 
+    }
     /* otherwise process rest of input */
     last = input + isize;
     while (input < last)
@@ -568,9 +568,9 @@ static int mime_global_unqp(lua_State *L)
 /*-------------------------------------------------------------------------*\
 * Incrementally breaks a quoted-printed string into lines
 * A, n = qpwrp(l, B, length)
-* A is a copy of B, broken into lines of at most 'length' bytes. 
-* 'l' is how many bytes are left for the first line of B. 
-* 'n' is the number of bytes left in the last line of A. 
+* A is a copy of B, broken into lines of at most 'length' bytes.
+* 'l' is how many bytes are left for the first line of B.
+* 'n' is the number of bytes left in the last line of A.
 * There are two complications: lines can't be broken in the middle
 * of an encoded =XX, and there might be line breaks already
 \*-------------------------------------------------------------------------*/
@@ -578,7 +578,7 @@ static int mime_global_qpwrp(lua_State *L)
 {
     size_t size = 0;
     int left = (int) luaL_checknumber(L, 1);
-    const UC *input = (UC *) luaL_optlstring(L, 2, NULL, &size);
+    const UC *input = (const UC *) luaL_optlstring(L, 2, NULL, &size);
     const UC *last = input + size;
     int length = (int) luaL_optnumber(L, 3, 76);
     luaL_Buffer buffer;
@@ -603,11 +603,11 @@ static int mime_global_qpwrp(lua_State *L)
                 if (left <= 3) {
                     left = length;
                     luaL_addstring(&buffer, EQCRLF);
-                } 
+                }
                 luaL_addchar(&buffer, *input);
                 left--;
                 break;
-            default: 
+            default:
                 if (left <= 1) {
                     left = length;
                     luaL_addstring(&buffer, EQCRLF);
@@ -635,7 +635,7 @@ static int mime_global_qpwrp(lua_State *L)
 * last is the previous character
 \*-------------------------------------------------------------------------*/
 #define eolcandidate(c) (c == '\r' || c == '\n')
-static int eolprocess(int c, int last, const char *marker, 
+static int eolprocess(int c, int last, const char *marker,
         luaL_Buffer *buffer)
 {
     if (eolcandidate(c)) {
@@ -653,15 +653,15 @@ static int eolprocess(int c, int last, const char *marker,
 }
 
 /*-------------------------------------------------------------------------*\
-* Converts a string to uniform EOL convention. 
+* Converts a string to uniform EOL convention.
 * A, n = eol(o, B, marker)
 * A is the converted version of the largest prefix of B that can be
-* converted unambiguously. 'o' is the context returned by the previous 
+* converted unambiguously. 'o' is the context returned by the previous
 * call. 'n' is the new context.
 \*-------------------------------------------------------------------------*/
 static int mime_global_eol(lua_State *L)
 {
-    int ctx = luaL_checkint(L, 1);
+    int ctx = luaL_checkinteger(L, 1);
     size_t isize = 0;
     const char *input = luaL_optlstring(L, 2, NULL, &isize);
     const char *last = input + isize;
@@ -683,18 +683,18 @@ static int mime_global_eol(lua_State *L)
 }
 
 /*-------------------------------------------------------------------------*\
-* Takes one byte and stuff it if needed. 
+* Takes one byte and stuff it if needed.
 \*-------------------------------------------------------------------------*/
 static size_t dot(int c, size_t state, luaL_Buffer *buffer)
 {
     luaL_addchar(buffer, (char) c);
     switch (c) {
-        case '\r': 
+        case '\r':
             return 1;
-        case '\n': 
-            return (state == 1)? 2: 0; 
-        case '.':  
-            if (state == 2) 
+        case '\n':
+            return (state == 1)? 2: 0;
+        case '.':
+            if (state == 2)
                 luaL_addchar(buffer, '.');
         default:
             return 0;
@@ -719,7 +719,7 @@ static int mime_global_dot(lua_State *L)
     }
     /* process all input */
     luaL_buffinit(L, &buffer);
-    while (input < last) 
+    while (input < last)
         state = dot(*input++, state, &buffer);
     luaL_pushresult(&buffer);
     lua_pushnumber(L, (lua_Number) state);
diff --git a/src/options.h b/src/options.h
index 5657a06..b75db37 100644
--- a/src/options.h
+++ b/src/options.h
@@ -51,7 +51,8 @@ int opt_get_error(lua_State *L, p_socket ps);
 int opt_get_ip6_multicast_loop(lua_State *L, p_socket ps);
 int opt_get_ip6_multicast_hops(lua_State *L, p_socket ps);
 int opt_get_ip6_unicast_hops(lua_State *L, p_socket ps);
-int opt_get_ip6_v6only(lua_State *L, p_socket ps); 
+int opt_get_ip6_v6only(lua_State *L, p_socket ps);
+int opt_get_reuseport(lua_State *L, p_socket ps);
 
 /* invokes the appropriate option handler */
 int opt_meth_setoption(lua_State *L, p_opt opt, p_socket ps);
diff --git a/src/pierror.h b/src/pierror.h
new file mode 100644
index 0000000..cb773ab
--- /dev/null
+++ b/src/pierror.h
@@ -0,0 +1,28 @@
+#ifndef PIERROR_H
+#define PIERROR_H
+/*=========================================================================*\
+* Error messages
+* Defines platform independent error messages
+\*=========================================================================*/
+
+#define PIE_HOST_NOT_FOUND "host not found"
+#define PIE_ADDRINUSE      "address already in use"
+#define PIE_ISCONN         "already connected"
+#define PIE_ACCESS         "permission denied"
+#define PIE_CONNREFUSED    "connection refused"
+#define PIE_CONNABORTED    "closed"
+#define PIE_CONNRESET      "closed"
+#define PIE_TIMEDOUT       "timeout"
+#define PIE_AGAIN          "temporary failure in name resolution"
+#define PIE_BADFLAGS       "invalid value for ai_flags"
+#define PIE_BADHINTS       "invalid value for hints"
+#define PIE_FAIL           "non-recoverable failure in name resolution"
+#define PIE_FAMILY         "ai_family not supported"
+#define PIE_MEMORY         "memory allocation failure"
+#define PIE_NONAME         "host or service not provided, or not known"
+#define PIE_OVERFLOW       "argument buffer overflow"
+#define PIE_PROTOCOL       "resolved protocol is unknown"
+#define PIE_SERVICE        "service not supported for socket type"
+#define PIE_SOCKTYPE       "ai_socktype not supported"
+
+#endif
diff --git a/src/udp.c b/src/udp.c
index a9f2393..12e320a 100644
--- a/src/udp.c
+++ b/src/udp.c
@@ -131,7 +131,7 @@ int udp_open(lua_State *L)
 /*=========================================================================*\
 * Lua methods
 \*=========================================================================*/
-const char *udp_strerror(int err) {
+static const char *udp_strerror(int err) {
     /* a 'closed' error on an unconnected means the target address was not
      * accepted by the transport layer */
     if (err == IO_CLOSED) return "refused";
diff --git a/src/usocket.c b/src/usocket.c
index 096ecd0..99e551b 100644
--- a/src/usocket.c
+++ b/src/usocket.c
@@ -4,12 +4,13 @@
 *
 * The code is now interrupt-safe.
 * The penalty of calling select to avoid busy-wait is only paid when
-* the I/O call fail in the first place. 
+* the I/O call fail in the first place.
 \*=========================================================================*/
-#include <string.h> 
+#include <string.h>
 #include <signal.h>
 
 #include "socket.h"
+#include "pierror.h"
 
 /*-------------------------------------------------------------------------*\
 * Wait for readable/writable/connected socket with timeout
@@ -72,7 +73,7 @@ int socket_waitfd(p_socket ps, int sw, p_timeout tm) {
 
 
 /*-------------------------------------------------------------------------*\
-* Initializes module 
+* Initializes module
 \*-------------------------------------------------------------------------*/
 int socket_open(void) {
     /* instals a handler to ignore sigpipe or it will crash us */
@@ -81,7 +82,7 @@ int socket_open(void) {
 }
 
 /*-------------------------------------------------------------------------*\
-* Close module 
+* Close module
 \*-------------------------------------------------------------------------*/
 int socket_close(void) {
     return 1;
@@ -92,7 +93,6 @@ int socket_close(void) {
 \*-------------------------------------------------------------------------*/
 void socket_destroy(p_socket ps) {
     if (*ps != SOCKET_INVALID) {
-        socket_setblocking(ps);
         close(*ps);
         *ps = SOCKET_INVALID;
     }
@@ -101,7 +101,7 @@ void socket_destroy(p_socket ps) {
 /*-------------------------------------------------------------------------*\
 * Select with timeout control
 \*-------------------------------------------------------------------------*/
-int socket_select(t_socket n, fd_set *rfds, fd_set *wfds, fd_set *efds, 
+int socket_select(t_socket n, fd_set *rfds, fd_set *wfds, fd_set *efds,
         p_timeout tm) {
     int ret;
     do {
@@ -120,8 +120,8 @@ int socket_select(t_socket n, fd_set *rfds, fd_set *wfds, fd_set *efds,
 \*-------------------------------------------------------------------------*/
 int socket_create(p_socket ps, int domain, int type, int protocol) {
     *ps = socket(domain, type, protocol);
-    if (*ps != SOCKET_INVALID) return IO_DONE; 
-    else return errno; 
+    if (*ps != SOCKET_INVALID) return IO_DONE;
+    else return errno;
 }
 
 /*-------------------------------------------------------------------------*\
@@ -130,29 +130,25 @@ int socket_create(p_socket ps, int domain, int type, int protocol) {
 int socket_bind(p_socket ps, SA *addr, socklen_t len) {
     int err = IO_DONE;
     socket_setblocking(ps);
-    if (bind(*ps, addr, len) < 0) err = errno; 
+    if (bind(*ps, addr, len) < 0) err = errno;
     socket_setnonblocking(ps);
     return err;
 }
 
 /*-------------------------------------------------------------------------*\
-* 
+*
 \*-------------------------------------------------------------------------*/
 int socket_listen(p_socket ps, int backlog) {
-    int err = IO_DONE; 
-    socket_setblocking(ps);
-    if (listen(*ps, backlog)) err = errno; 
-    socket_setnonblocking(ps);
+    int err = IO_DONE;
+    if (listen(*ps, backlog)) err = errno;
     return err;
 }
 
 /*-------------------------------------------------------------------------*\
-* 
+*
 \*-------------------------------------------------------------------------*/
 void socket_shutdown(p_socket ps, int how) {
-    socket_setblocking(ps);
     shutdown(*ps, how);
-    socket_setnonblocking(ps);
 }
 
 /*-------------------------------------------------------------------------*\
@@ -166,7 +162,7 @@ int socket_connect(p_socket ps, SA *addr, socklen_t len, p_timeout tm) {
     do if (connect(*ps, addr, len) == 0) return IO_DONE;
     while ((err = errno) == EINTR);
     /* if connection failed immediately, return error code */
-    if (err != EINPROGRESS && err != EAGAIN) return err; 
+    if (err != EINPROGRESS && err != EAGAIN) return err;
     /* zero timeout case optimization */
     if (timeout_iszero(tm)) return IO_TIMEOUT;
     /* wait until we have the result of the connection attempt or timeout */
@@ -181,7 +177,7 @@ int socket_connect(p_socket ps, SA *addr, socklen_t len, p_timeout tm) {
 * Accept with timeout
 \*-------------------------------------------------------------------------*/
 int socket_accept(p_socket ps, p_socket pa, SA *addr, socklen_t *len, p_timeout tm) {
-    if (*ps == SOCKET_INVALID) return IO_CLOSED; 
+    if (*ps == SOCKET_INVALID) return IO_CLOSED;
     for ( ;; ) {
         int err;
         if ((*pa = accept(*ps, addr, len)) != SOCKET_INVALID) return IO_DONE;
@@ -197,7 +193,7 @@ int socket_accept(p_socket ps, p_socket pa, SA *addr, socklen_t *len, p_timeout
 /*-------------------------------------------------------------------------*\
 * Send with timeout
 \*-------------------------------------------------------------------------*/
-int socket_send(p_socket ps, const char *data, size_t count, 
+int socket_send(p_socket ps, const char *data, size_t count,
         size_t *sent, p_timeout tm)
 {
     int err;
@@ -229,14 +225,14 @@ int socket_send(p_socket ps, const char *data, size_t count,
 /*-------------------------------------------------------------------------*\
 * Sendto with timeout
 \*-------------------------------------------------------------------------*/
-int socket_sendto(p_socket ps, const char *data, size_t count, size_t *sent, 
+int socket_sendto(p_socket ps, const char *data, size_t count, size_t *sent,
         SA *addr, socklen_t len, p_timeout tm)
 {
     int err;
     *sent = 0;
     if (*ps == SOCKET_INVALID) return IO_CLOSED;
     for ( ;; ) {
-        long put = (long) sendto(*ps, data, count, 0, addr, len);  
+        long put = (long) sendto(*ps, data, count, 0, addr, len); 
         if (put >= 0) {
             *sent = put;
             return IO_DONE;
@@ -266,8 +262,8 @@ int socket_recv(p_socket ps, char *data, size_t count, size_t *got, p_timeout tm
         err = errno;
         if (taken == 0) return IO_CLOSED;
         if (err == EINTR) continue;
-        if (err != EAGAIN) return err; 
-        if ((err = socket_waitfd(ps, WAITFD_R, tm)) != IO_DONE) return err; 
+        if (err != EAGAIN) return err;
+        if ((err = socket_waitfd(ps, WAITFD_R, tm)) != IO_DONE) return err;
     }
     return IO_UNKNOWN;
 }
@@ -275,7 +271,7 @@ int socket_recv(p_socket ps, char *data, size_t count, size_t *got, p_timeout tm
 /*-------------------------------------------------------------------------*\
 * Recvfrom with timeout
 \*-------------------------------------------------------------------------*/
-int socket_recvfrom(p_socket ps, char *data, size_t count, size_t *got, 
+int socket_recvfrom(p_socket ps, char *data, size_t count, size_t *got,
         SA *addr, socklen_t *len, p_timeout tm) {
     int err;
     *got = 0;
@@ -289,8 +285,8 @@ int socket_recvfrom(p_socket ps, char *data, size_t count, size_t *got,
         err = errno;
         if (taken == 0) return IO_CLOSED;
         if (err == EINTR) continue;
-        if (err != EAGAIN) return err; 
-        if ((err = socket_waitfd(ps, WAITFD_R, tm)) != IO_DONE) return err; 
+        if (err != EAGAIN) return err;
+        if ((err = socket_waitfd(ps, WAITFD_R, tm)) != IO_DONE) return err;
     }
     return IO_UNKNOWN;
 }
@@ -303,7 +299,7 @@ int socket_recvfrom(p_socket ps, char *data, size_t count, size_t *got,
 * with send/recv replaced with write/read. We can't just use write/read
 * in the socket version, because behaviour when size is zero is different.
 \*-------------------------------------------------------------------------*/
-int socket_write(p_socket ps, const char *data, size_t count, 
+int socket_write(p_socket ps, const char *data, size_t count,
         size_t *sent, p_timeout tm)
 {
     int err;
@@ -349,8 +345,8 @@ int socket_read(p_socket ps, char *data, size_t count, size_t *got, p_timeout tm
         err = errno;
         if (taken == 0) return IO_CLOSED;
         if (err == EINTR) continue;
-        if (err != EAGAIN) return err; 
-        if ((err = socket_waitfd(ps, WAITFD_R, tm)) != IO_DONE) return err; 
+        if (err != EAGAIN) return err;
+        if ((err = socket_waitfd(ps, WAITFD_R, tm)) != IO_DONE) return err;
     }
     return IO_UNKNOWN;
 }
@@ -374,7 +370,7 @@ void socket_setnonblocking(p_socket ps) {
 }
 
 /*-------------------------------------------------------------------------*\
-* DNS helpers 
+* DNS helpers
 \*-------------------------------------------------------------------------*/
 int socket_gethostbyaddr(const char *addr, socklen_t len, struct hostent **hp) {
     *hp = gethostbyaddr(addr, len, AF_INET);
@@ -399,7 +395,7 @@ int socket_gethostbyname(const char *addr, struct hostent **hp) {
 const char *socket_hoststrerror(int err) {
     if (err <= 0) return io_strerror(err);
     switch (err) {
-        case HOST_NOT_FOUND: return "host not found";
+        case HOST_NOT_FOUND: return PIE_HOST_NOT_FOUND;
         default: return hstrerror(err);
     }
 }
@@ -407,13 +403,13 @@ const char *socket_hoststrerror(int err) {
 const char *socket_strerror(int err) {
     if (err <= 0) return io_strerror(err);
     switch (err) {
-        case EADDRINUSE: return "address already in use";
-        case EISCONN: return "already connected";
-        case EACCES: return "permission denied";
-        case ECONNREFUSED: return "connection refused";
-        case ECONNABORTED: return "closed";
-        case ECONNRESET: return "closed";
-        case ETIMEDOUT: return "timeout";
+        case EADDRINUSE: return PIE_ADDRINUSE;
+        case EISCONN: return PIE_ISCONN;
+        case EACCES: return PIE_ACCESS;
+        case ECONNREFUSED: return PIE_CONNREFUSED;
+        case ECONNABORTED: return PIE_CONNABORTED;
+        case ECONNRESET: return PIE_CONNRESET;
+        case ETIMEDOUT: return PIE_TIMEDOUT;
         default: return strerror(err);
     }
 }
@@ -421,28 +417,27 @@ const char *socket_strerror(int err) {
 const char *socket_ioerror(p_socket ps, int err) {
     (void) ps;
     return socket_strerror(err);
-} 
+}
 
 const char *socket_gaistrerror(int err) {
-    if (err == 0) return NULL; 
+    if (err == 0) return NULL;
     switch (err) {
-        case EAI_AGAIN: return "temporary failure in name resolution";
-        case EAI_BADFLAGS: return "invalid value for ai_flags";
+        case EAI_AGAIN: return PIE_AGAIN;
+        case EAI_BADFLAGS: return PIE_BADFLAGS;
 #ifdef EAI_BADHINTS
-        case EAI_BADHINTS: return "invalid value for hints";
+        case EAI_BADHINTS: return PIE_BADHINTS;
 #endif
-        case EAI_FAIL: return "non-recoverable failure in name resolution";
-        case EAI_FAMILY: return "ai_family not supported";
-        case EAI_MEMORY: return "memory allocation failure";
-        case EAI_NONAME: 
-            return "host or service not provided, or not known";
-        case EAI_OVERFLOW: return "argument buffer overflow";
+        case EAI_FAIL: return PIE_FAIL;
+        case EAI_FAMILY: return PIE_FAMILY;
+        case EAI_MEMORY: return PIE_MEMORY;
+        case EAI_NONAME: return PIE_NONAME;
+        case EAI_OVERFLOW: return PIE_OVERFLOW;
 #ifdef EAI_PROTOCOL
-        case EAI_PROTOCOL: return "resolved protocol is unknown";
+        case EAI_PROTOCOL: return PIE_PROTOCOL;
 #endif
-        case EAI_SERVICE: return "service not supported for socket type";
-        case EAI_SOCKTYPE: return "ai_socktype not supported";
-        case EAI_SYSTEM: return strerror(errno); 
+        case EAI_SERVICE: return PIE_SERVICE;
+        case EAI_SOCKTYPE: return PIE_SOCKTYPE;
+        case EAI_SYSTEM: return strerror(errno);
         default: return gai_strerror(err);
     }
 }
diff --git a/src/wsocket.c b/src/wsocket.c
index b4a4384..10800e3 100644
--- a/src/wsocket.c
+++ b/src/wsocket.c
@@ -8,6 +8,7 @@
 #include <string.h>
 
 #include "socket.h"
+#include "pierror.h"
 
 /* WinSock doesn't have a strerror... */
 static const char *wstrerror(int err);
@@ -330,7 +331,7 @@ int socket_gethostbyname(const char *addr, struct hostent **hp) {
 const char *socket_hoststrerror(int err) {
     if (err <= 0) return io_strerror(err);
     switch (err) {
-        case WSAHOST_NOT_FOUND: return "host not found";
+        case WSAHOST_NOT_FOUND: return PIE_HOST_NOT_FOUND;
         default: return wstrerror(err); 
     }
 }
@@ -338,13 +339,13 @@ const char *socket_hoststrerror(int err) {
 const char *socket_strerror(int err) {
     if (err <= 0) return io_strerror(err);
     switch (err) {
-        case WSAEADDRINUSE: return "address already in use";
-        case WSAECONNREFUSED: return "connection refused";
-        case WSAEISCONN: return "already connected";
-        case WSAEACCES: return "permission denied";
-        case WSAECONNABORTED: return "closed";
-        case WSAECONNRESET: return "closed";
-        case WSAETIMEDOUT: return "timeout";
+        case WSAEADDRINUSE: return PIE_ADDRINUSE;
+        case WSAECONNREFUSED : return PIE_CONNREFUSED;
+        case WSAEISCONN: return PIE_ISCONN;
+        case WSAEACCES: return PIE_ACCESS;
+        case WSAECONNABORTED: return PIE_CONNABORTED;
+        case WSAECONNRESET: return PIE_CONNRESET;
+        case WSAETIMEDOUT: return PIE_TIMEDOUT;
         default: return wstrerror(err);
     }
 }
@@ -357,7 +358,7 @@ const char *socket_ioerror(p_socket ps, int err) {
 static const char *wstrerror(int err) {
     switch (err) {
         case WSAEINTR: return "Interrupted function call";
-        case WSAEACCES: return "Permission denied";
+        case WSAEACCES: return PIE_ACCESS; // "Permission denied";
         case WSAEFAULT: return "Bad address";
         case WSAEINVAL: return "Invalid argument";
         case WSAEMFILE: return "Too many open files";
@@ -370,24 +371,23 @@ static const char *wstrerror(int err) {
         case WSAEPROTOTYPE: return "Protocol wrong type for socket";
         case WSAENOPROTOOPT: return "Bad protocol option";
         case WSAEPROTONOSUPPORT: return "Protocol not supported";
-        case WSAESOCKTNOSUPPORT: return "Socket type not supported";
+        case WSAESOCKTNOSUPPORT: return PIE_SOCKTYPE; // "Socket type not supported";
         case WSAEOPNOTSUPP: return "Operation not supported";
         case WSAEPFNOSUPPORT: return "Protocol family not supported";
-        case WSAEAFNOSUPPORT: 
-            return "Address family not supported by protocol family"; 
-        case WSAEADDRINUSE: return "Address already in use";
+        case WSAEAFNOSUPPORT: return PIE_FAMILY; // "Address family not supported by protocol family"; 
+        case WSAEADDRINUSE: return PIE_ADDRINUSE; // "Address already in use";
         case WSAEADDRNOTAVAIL: return "Cannot assign requested address";
         case WSAENETDOWN: return "Network is down";
         case WSAENETUNREACH: return "Network is unreachable";
         case WSAENETRESET: return "Network dropped connection on reset";
         case WSAECONNABORTED: return "Software caused connection abort";
-        case WSAECONNRESET: return "Connection reset by peer";
+        case WSAECONNRESET: return PIE_CONNRESET; // "Connection reset by peer";
         case WSAENOBUFS: return "No buffer space available";
-        case WSAEISCONN: return "Socket is already connected";
+        case WSAEISCONN: return PIE_ISCONN; // "Socket is already connected";
         case WSAENOTCONN: return "Socket is not connected";
         case WSAESHUTDOWN: return "Cannot send after socket shutdown";
-        case WSAETIMEDOUT: return "Connection timed out";
-        case WSAECONNREFUSED: return "Connection refused";
+        case WSAETIMEDOUT: return PIE_TIMEDOUT; // "Connection timed out";
+        case WSAECONNREFUSED: return PIE_CONNREFUSED; // "Connection refused";
         case WSAEHOSTDOWN: return "Host is down";
         case WSAEHOSTUNREACH: return "No route to host";
         case WSAEPROCLIM: return "Too many processes";
@@ -396,9 +396,9 @@ static const char *wstrerror(int err) {
         case WSANOTINITIALISED: 
             return "Successful WSAStartup not yet performed";
         case WSAEDISCON: return "Graceful shutdown in progress";
-        case WSAHOST_NOT_FOUND: return "Host not found";
+        case WSAHOST_NOT_FOUND: return PIE_HOST_NOT_FOUND; // "Host not found";
         case WSATRY_AGAIN: return "Nonauthoritative host not found";
-        case WSANO_RECOVERY: return "Nonrecoverable name lookup error"; 
+        case WSANO_RECOVERY: return PIE_FAIL; // "Nonrecoverable name lookup error"; 
         case WSANO_DATA: return "Valid name, no data record of requested type";
         default: return "Unknown error";
     }
@@ -407,24 +407,23 @@ static const char *wstrerror(int err) {
 const char *socket_gaistrerror(int err) {
     if (err == 0) return NULL; 
     switch (err) {
-        case EAI_AGAIN: return "temporary failure in name resolution";
-        case EAI_BADFLAGS: return "invalid value for ai_flags";
+        case EAI_AGAIN: return PIE_AGAIN;
+        case EAI_BADFLAGS: return PIE_BADFLAGS;
 #ifdef EAI_BADHINTS
-        case EAI_BADHINTS: return "invalid value for hints";
+        case EAI_BADHINTS: return PIE_BADHINTS;
 #endif
-        case EAI_FAIL: return "non-recoverable failure in name resolution";
-        case EAI_FAMILY: return "ai_family not supported";
-        case EAI_MEMORY: return "memory allocation failure";
-        case EAI_NONAME: 
-            return "host or service not provided, or not known";
+        case EAI_FAIL: return PIE_FAIL;
+        case EAI_FAMILY: return PIE_FAMILY;
+        case EAI_MEMORY: return PIE_MEMORY;
+        case EAI_NONAME: return PIE_NONAME;
 #ifdef EAI_OVERFLOW
-        case EAI_OVERFLOW: return "argument buffer overflow";
+        case EAI_OVERFLOW: return PIE_OVERFLOW;
 #endif
 #ifdef EAI_PROTOCOL
-        case EAI_PROTOCOL: return "resolved protocol is unknown";
+        case EAI_PROTOCOL: return PIE_PROTOCOL;
 #endif
-        case EAI_SERVICE: return "service not supported for socket type";
-        case EAI_SOCKTYPE: return "ai_socktype not supported";
+        case EAI_SERVICE: return PIE_SERVICE;
+        case EAI_SOCKTYPE: return PIE_SOCKTYPE;
 #ifdef EAI_SYSTEM
         case EAI_SYSTEM: return strerror(errno); 
 #endif
diff --git a/test/auth/.htaccess b/test/auth/.htaccess
new file mode 100644
index 0000000..bb2794a
--- /dev/null
+++ b/test/auth/.htaccess
@@ -0,0 +1,4 @@
+AuthName "test-auth"
+    AuthType Basic
+    AuthUserFile /Users/diego/impa/luasocket/test/auth/.htpasswd
+    Require valid-user
diff --git a/test/auth/.htpasswd b/test/auth/.htpasswd
index fd9002b..cfb2603 100644
--- a/test/auth/.htpasswd
+++ b/test/auth/.htpasswd
@@ -1 +1 @@
-luasocket:l8n2npozPB.sQ
+luasocket:$apr1$47u2O.Me$.m/5BWAtt7GVoxsouIPBR1
diff --git a/test/httptest.lua b/test/httptest.lua
index d5fbb37..63ff921 100644
--- a/test/httptest.lua
+++ b/test/httptest.lua
@@ -1,4 +1,4 @@
--- needs Alias from /home/c/diego/tec/luasocket/test to 
+-- needs Alias from /home/c/diego/tec/luasocket/test to
 -- "/luasocket-test" and "/luasocket-test/"
 -- needs ScriptAlias from /home/c/diego/tec/luasocket/test/cgi
 -- to "/luasocket-test-cgi" and "/luasocket-test-cgi/"
@@ -36,22 +36,22 @@ index = readfile(index_file)
 local check_result = function(response, expect, ignore)
     for i,v in pairs(response) do
         if not ignore[i] then
-            if v ~= expect[i] then 
+            if v ~= expect[i] then
                 local f = io.open("err", "w")
                 f:write(tostring(v), "\n\n versus\n\n", tostring(expect[i]))
                 f:close()
-                fail(i .. " differs!") 
+                fail(i .. " differs!")
             end
         end
     end
     for i,v in pairs(expect) do
         if not ignore[i] then
-            if v ~= response[i] then 
+            if v ~= response[i] then
                 local f = io.open("err", "w")
                 f:write(tostring(response[i]), "\n\n versus\n\n", tostring(v))
                 v = string.sub(type(v) == "string" and v or "", 1, 70)
                 f:close()
-                fail(i .. " differs!") 
+                fail(i .. " differs!")
             end
         end
     end
@@ -61,10 +61,10 @@ end
 local check_request = function(request, expect, ignore)
     local t
     if not request.sink then request.sink, t = ltn12.sink.table() end
-    request.source = request.source or 
+    request.source = request.source or
         (request.body and ltn12.source.string(request.body))
     local response = {}
-    response.code, response.headers, response.status = 
+    response.code, response.headers, response.status =
         socket.skip(1, http.request(request))
     if t and #t > 0 then response.body = table.concat(t) end
     check_result(response, expect, ignore)
@@ -82,7 +82,7 @@ else fail(back.query) end
 ------------------------------------------------------------------------
 io.write("testing query string correctness: ")
 forth = "this+is+the+query+string"
-back = http.request("http://" .. host .. cgiprefix .. 
+back = http.request("http://" .. host .. cgiprefix ..
     "/query-string?" .. forth)
 if similar(back, forth) then print("ok")
 else fail("failed!") end
@@ -120,10 +120,10 @@ check_request(request, expect, ignore)
 ------------------------------------------------------------------------
 io.write("testing invalid url: ")
 local r, e = http.request{url = host .. prefix}
-assert(r == nil and e == "invalid host ''") 
+assert(r == nil and e == "invalid host ''")
 r, re = http.request(host .. prefix)
-assert(r == nil and e == re, tostring(r) ..", " .. tostring(re) .. 
-    " vs " .. tostring(e)) 
+assert(r == nil and e == re, tostring(r) ..", " .. tostring(re) ..
+    " vs " .. tostring(e))
 print("ok")
 
 io.write("testing invalid empty port: ")
@@ -212,7 +212,7 @@ os.remove(index_file .. "-back")
 io.write("testing ltn12.(sink|source).chain and mime.(encode|decode): ")
 
 local function b64length(len)
-    local a = math.ceil(len/3)*4 
+    local a = math.ceil(len/3)*4
     local l = math.ceil(a/76)
     return a + l*2
 end
@@ -313,7 +313,7 @@ ignore = {
     headers = 1
 }
 check_request(request, expect, ignore)
-    
+
 ------------------------------------------------------------------------
 io.write("testing document not found: ")
 request = {
@@ -429,9 +429,9 @@ print("ok")
 io.write("testing host not found: ")
 local c, e = socket.connect("example.invalid", 80)
 local r, re = http.request{url = "http://example.invalid/does/not/exist"}
-assert(r == nil and e == re, tostring(r) .. " " .. tostring(re)) 
+assert(r == nil and e == re, tostring(r) .. " " .. tostring(re))
 r, re = http.request("http://example.invalid/does/not/exist")
-assert(r == nil and e == re) 
+assert(r == nil and e == re)
 print("ok")
 
 ------------------------------------------------------------------------
diff --git a/test/ltn12test.lua b/test/ltn12test.lua
index 74a45e8..e3f85fb 100644
--- a/test/ltn12test.lua
+++ b/test/ltn12test.lua
@@ -192,6 +192,21 @@ assert(filter(nil, 1), "filter not empty")
 print("ok")
 
 --------------------------------
+io.write("testing source.chain (with several filters): ")
+local function double(x) -- filter turning "ABC" into "AABBCC"
+    if not x then return end
+    local b={}
+    for k in x:gmatch'.' do table.insert(b, k..k) end
+    return table.concat(b)
+end
+source = ltn12.source.string(s)
+source = ltn12.source.chain(source, double, double, double)
+sink, t = ltn12.sink.table()
+assert(ltn12.pump.all(source, sink), "returned error")
+assert(table.concat(t) == double(double(double(s))), "mismatch")
+print("ok")
+
+--------------------------------
 io.write("testing source.chain (with split) and sink.chain (with merge): ")
 source = ltn12.source.string(s)
 filter = split(5)
@@ -206,6 +221,15 @@ assert(filter2(nil, 1), "filter2 not empty")
 print("ok")
 
 --------------------------------
+io.write("testing sink.chain (with several filters): ")
+source = ltn12.source.string(s)
+sink, t = ltn12.sink.table()
+sink = ltn12.sink.chain(double, double, double, sink)
+assert(ltn12.pump.all(source, sink), "returned error")
+assert(table.concat(t) == double(double(double(s))), "mismatch")
+print("ok")
+
+--------------------------------
 io.write("testing filter.chain (and sink.chain, with split, merge): ")
 source = ltn12.source.string(s)
 filter = split(5)
@@ -272,3 +296,4 @@ assert(filter3(nil, 1), "filter3 not empty")
 assert(filter4(nil, 1), "filter4 not empty")
 assert(filter5(nil, 1), "filter5 not empty")
 print("ok")
+