Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

Merge branch 'ta/sendfile' into pu

* ta/sendfile:
  Implement file:sendfile
  • Loading branch information...
commit 34042f49c7b5f0ac1b91463344db663eddc2e76d 2 parents bae2264 + c652dda
Björn Gustavsson authored November 26, 2010
15  erts/configure.in
@@ -1733,6 +1733,21 @@ if test "X$host" = "Xwin32"; then
1733 1733
 fi
1734 1734
 AC_FUNC_SETVBUF_REVERSED	
1735 1735
 
  1736
+dnl TODO: not sure this is enough
  1737
+dnl TODO: maybe use AC_CHECK_FUNC instead of AC_CHECK_FUNS([])
  1738
+case $host_os in
  1739
+  solaris*)
  1740
+    AC_CHECK_FUNCS([sendfile])
  1741
+    AC_CHECK_LIB(sendfile, sendfile)
  1742
+    ;;
  1743
+  linux*|darwin*|freebsd*)
  1744
+    AC_CHECK_FUNCS([sendfile])
  1745
+  ;;
  1746
+  *)
  1747
+    AC_MSG_NOTICE([not checking for sendfile() on this platform])
  1748
+  ;;
  1749
+esac
  1750
+
1736 1751
 disable_vfork=false
1737 1752
 if test "x$EMU_THR_LIB_NAME" != "x"; then
1738 1753
 	AC_MSG_CHECKING([if vfork is known to hang multithreaded applications])
3  erts/emulator/Makefile.in
@@ -286,7 +286,8 @@ endif
286 286
 endif
287 287
 
288 288
 ifeq ($(TARGET),win32)
289  
-LIBS    += -L$(ERL_TOP)/erts/emulator/pcre/obj/$(TARGET)/$(TYPE) -lepcre
  289
+# TODO: is this the right place to add -lmswsock?
  290
+LIBS    += -L$(ERL_TOP)/erts/emulator/pcre/obj/$(TARGET)/$(TYPE) -lepcre -lmswsock
290 291
 else
291 292
 LIBS    += $(ERL_TOP)/erts/emulator/pcre/obj/$(TARGET)/$(TYPE)/$(LIB_PREFIX)epcre$(LIB_SUFFIX)
292 293
 endif
96  erts/emulator/drivers/common/efile_drv.c
@@ -55,6 +55,7 @@
55 55
 #define FILE_READ_LINE          29
56 56
 #define FILE_FDATASYNC          30
57 57
 #define FILE_FADVISE            31
  58
+#define FILE_SENDFILE           32
58 59
 
59 60
 /* Return codes */
60 61
 
@@ -364,6 +365,11 @@ struct t_data
364 365
 	    Sint64 length;
365 366
 	    int advise;
366 367
 	} fadvise;
  368
+	struct {
  369
+	    Sint destfd;
  370
+	    off_t offset;
  371
+	    size_t size;
  372
+	} sendfile;
367 373
     } c;
368 374
     char b[1];
369 375
 };
@@ -1665,6 +1671,75 @@ static void invoke_fadvise(void *data)
1665 1671
     d->result_ok = efile_fadvise(&d->errInfo, fd, offset, length, advise);
1666 1672
 }
1667 1673
 
  1674
+static void invoke_sendfile(void *data)
  1675
+{
  1676
+    struct t_data *d = (struct t_data *) data;
  1677
+    int fd = (int) d->fd;
  1678
+    int destfd = (int) d->c.sendfile.destfd;
  1679
+    off_t offset = (off_t) d->c.sendfile.offset;
  1680
+    size_t count = (size_t) d->c.sendfile.size;
  1681
+    d->result_ok = efile_sendfile(&d->errInfo, fd, destfd, &offset, &count);
  1682
+
  1683
+    if (d->result_ok)
  1684
+	d->again=0;
  1685
+    else {
  1686
+	switch(d->errInfo.posix_errno){
  1687
+	/* TODO */
  1688
+	/* is this the right way? */
  1689
+	case 0: /*ok*/
  1690
+#if defined(__linux__)
  1691
+	/* case EAGAIN: TODO: is the check in efile_sendfile() enough? */
  1692
+	case EBADF:
  1693
+	case EFAULT:
  1694
+	case EINVAL:
  1695
+	case EIO:
  1696
+	case ENOMEM:
  1697
+#elif defined(__FreeBSD__)
  1698
+	/* case EAGAIN: TODO: is the check in efile_sendfile() enough? */
  1699
+	case EBADF:
  1700
+	case EBUSY:
  1701
+	case EFAULT:
  1702
+	/* case EINTR: TODO: is the check in efile_sendfile() enough? */
  1703
+	case EINVAL:
  1704
+	case EIO:
  1705
+	case ENOTCONN:
  1706
+	case ENOTSOCK:
  1707
+	case EOPNOTSUPP:
  1708
+	case EPIPE:
  1709
+/* TODO: check which to use */
  1710
+/* #elif defined(__APPLE__) && defined(__MACH__) */
  1711
+#elif defined(__APPLE__) && defined(__MACH__) && !defined(__DARWIN__)
  1712
+	/* case EAGAIN: TODO: is the check in efile_sendfile() enough? */
  1713
+	case EBADF:
  1714
+	case ENOTSUP:
  1715
+	case ENOTSOCK:
  1716
+	case EFAULT:
  1717
+	/* case EINTR: TODO: is the check in efile_sendfile() enough? */
  1718
+	case EINVAL:
  1719
+	case EIO:
  1720
+	case ENOTCONN:
  1721
+	case EOPNOTSUPP:
  1722
+/* TODO: check which to use */
  1723
+/* #if defined(__linux__) || defined(__solaris__) */
  1724
+#elif defined(__SVR4) && defined(__sun)
  1725
+	case EAFNOSUPPORT:
  1726
+	/* case EAGAIN: TODO: is the check in efile_sendfile() enough? */
  1727
+	case EBADF:
  1728
+	case EINVAL:
  1729
+	case EIO:
  1730
+	case ENOTCONN:
  1731
+	case EOPNOTSUPP:
  1732
+	case EPIPE:
  1733
+	/* case EINTR: TODO: is the check in efile_sendfile() enough? */
  1734
+#endif
  1735
+	    d->again = 0;
  1736
+	    break;
  1737
+	default:
  1738
+	    d->again = 1;
  1739
+	};
  1740
+    }
  1741
+}
  1742
+
1668 1743
 static void free_readdir(void *data)
1669 1744
 {
1670 1745
     struct t_data *d = (struct t_data *) data;
@@ -2077,6 +2152,10 @@ file_async_ready(ErlDrvData e, ErlDrvThreadData data)
2077 2152
 	  }
2078 2153
 	  free_preadv(data);
2079 2154
 	  break;
  2155
+      case FILE_SENDFILE:
  2156
+	  reply_Uint(desc, d->c.sendfile.size);
  2157
+	  free_data(data);
  2158
+	  break;
2080 2159
       default:
2081 2160
 	abort();
2082 2161
     }
@@ -2389,6 +2468,23 @@ file_output(ErlDrvData e, char* buf, int count)
2389 2468
         goto done;
2390 2469
     }
2391 2470
 
  2471
+    case FILE_SENDFILE:
  2472
+	{
  2473
+	    d = EF_SAFE_ALLOC(sizeof(struct t_data));
  2474
+	    d->fd = fd;
  2475
+	    d->command = command;
  2476
+	    d->invoke = invoke_sendfile;
  2477
+	    d->free = free_data;
  2478
+	    d->level = 2;
  2479
+	    d->c.sendfile.destfd = get_int32((uchar*) buf);
  2480
+	    /* TODO: are off_t and size_t 64bit on all platforms?
  2481
+	       off_t is 32bit on win32 msvc. maybe configurable in msvc. */
  2482
+	    d->c.sendfile.offset = get_int64(((uchar*) buf) + sizeof(Sint32));
  2483
+	    d->c.sendfile.size = get_int64(((uchar*) buf) + sizeof(Sint32)
  2484
+					   + sizeof(Sint64));
  2485
+	    goto done;
  2486
+	}
  2487
+
2392 2488
     }
2393 2489
 
2394 2490
     /*
2  erts/emulator/drivers/common/erl_efile.h
@@ -154,3 +154,5 @@ int efile_symlink(Efile_error* errInfo, char* old, char* new);
154 154
 int efile_may_openfile(Efile_error* errInfo, char *name);
155 155
 int efile_fadvise(Efile_error* errInfo, int fd, Sint64 offset, Sint64 length,
156 156
 		  int advise);
  157
+int efile_sendfile(Efile_error* errInfo, int in_fd, int out_fd, off_t *offset,
  158
+		   size_t *count);
64  erts/emulator/drivers/unix/unix_efile.c
@@ -33,6 +33,11 @@
33 33
 #include <sys/types.h>
34 34
 #include <sys/uio.h>
35 35
 #endif
  36
+/* TODO: check which to use */
  37
+/* #if defined(__linux__) || defined(__solaris__) */
  38
+#if defined(__linux__) || (defined(__SVR4) && defined(__sun))
  39
+#include <sys/sendfile.h>
  40
+#endif
36 41
 
37 42
 #if defined(__APPLE__) && defined(__MACH__) && !defined(__DARWIN__)
38 43
 #define DARWIN 1
@@ -1462,3 +1467,62 @@ efile_fadvise(Efile_error* errInfo, int fd, Sint64 offset,
1462 1467
     return check_error(0, errInfo);
1463 1468
 #endif
1464 1469
 }
  1470
+
  1471
+#ifdef HAVE_SENDFILE
  1472
+int
  1473
+efile_sendfile(Efile_error* errInfo, int in_fd, int out_fd,
  1474
+	       off_t *offset, size_t *count)
  1475
+{
  1476
+/* TODO: check which to use */
  1477
+/* #if defined(__linux__) || defined(__solaris__) */
  1478
+#if defined(__linux__) || (defined(__SVR4) && defined(__sun))
  1479
+    off_t cur = *offset;
  1480
+    ssize_t retval;
  1481
+    /* TODO: do we need a loop limit? */
  1482
+    do {
  1483
+        retval = sendfile(out_fd, in_fd, offset, *count);
  1484
+    } while (retval < 0 && errno == EINTR);
  1485
+    if (retval >= 0 && retval != *count) {
  1486
+        if (*offset == cur) {
  1487
+            *offset += retval;
  1488
+        }
  1489
+        retval = -1;
  1490
+	errno = EAGAIN;
  1491
+    }
  1492
+    *count = retval;
  1493
+    return check_error(retval == -1 ? -1 : 0, errInfo);
  1494
+/* TODO: check which to use */
  1495
+/* #elif defined(__APPLE__) && defined(__MACH__) */
  1496
+#elif defined(DARWIN)
  1497
+    off_t len = *count;
  1498
+    int retval;
  1499
+    do {
  1500
+        retval = sendfile(in_fd, out_fd, *offset, &len, NULL, 0);
  1501
+    } while (retval < 0 && errno == EINTR);
  1502
+    if (retval < 0 && errno == EAGAIN) {
  1503
+        *offset += len;
  1504
+    }
  1505
+    *count = retval == 0 ? len : -1;
  1506
+    return check_error(retval == 0 ? 0 : -1, errInfo);
  1507
+#elif defined(__FreeBSD__)
  1508
+    off_t len = 0;
  1509
+    int retval;
  1510
+    do {
  1511
+        retval = sendfile(in_fd, out_fd, *offset, *count, NULL, &len, 0);
  1512
+    } while (retval < 0 && errno == EINTR);
  1513
+    if (retval < 0 && errno == EAGAIN) {
  1514
+        *offset += len;
  1515
+    }
  1516
+    *count = retval == 0 ? len : -1;
  1517
+    return check_error(retval == 0 ? 0 : -1, errInfo);
  1518
+#endif
  1519
+}
  1520
+#else /* no sendfile() */
  1521
+int
  1522
+efile_sendfile(Efile_error* errInfo, int in_fd, int out_fd,
  1523
+	       off_t *offset, size_t *count)
  1524
+{
  1525
+    errno = ENOTSUP;
  1526
+    return check_error(-1, errInfo);
  1527
+}
  1528
+#endif
23  erts/emulator/drivers/win32/win_efile.c
@@ -1446,3 +1446,26 @@ efile_fadvise(Efile_error* errInfo, int fd, Sint64 offset,
1446 1446
     errno = ERROR_SUCCESS;
1447 1447
     return check_error(0, errInfo);
1448 1448
 }
  1449
+
  1450
+int
  1451
+efile_sendfile(Efile_error* errInfo, int in_fd, int out_fd,
  1452
+	       off_t *offset, size_t *count)
  1453
+{
  1454
+    /* TODO: Use SetFilePointerEx to support files larger than 4GB?
  1455
+             It would require that off_t is 64bit or another type is used.
  1456
+             SetFilePointerEx expects LARGE_INTEGER instead of LONG. */
  1457
+    if (SetFilePointer((HANDLE) in_fd, *offset, NULL, FILE_BEGIN)
  1458
+	!= INVALID_SET_FILE_POINTER) {
  1459
+	if (TransmitFile((SOCKET) out_fd, (HANDLE) in_fd, *count,
  1460
+			 0, NULL, NULL, 0)) {
  1461
+	    *offset += *count;
  1462
+	    return check_error(0, errInfo);
  1463
+	} else {
  1464
+	    /* TODO: correct error handling? */
  1465
+	    return set_error(errInfo);
  1466
+	}
  1467
+    } else {
  1468
+	/* TODO: correct error handling? */
  1469
+	return set_error(errInfo);
  1470
+    }
  1471
+}
8  erts/preloaded/src/prim_file.erl
@@ -26,7 +26,8 @@
26 26
 
27 27
 %% Generic file contents operations
28 28
 -export([open/2, close/1, datasync/1, sync/1, advise/4, position/2, truncate/1,
29  
-	 write/2, pwrite/2, pwrite/3, read/2, read_line/1, pread/2, pread/3, copy/3]).
  29
+	 write/2, pwrite/2, pwrite/3, read/2, read_line/1, pread/2, pread/3,
  30
+	 copy/3, sendfile/4]).
30 31
 
31 32
 %% Specialized file operations
32 33
 -export([open/1, open/3]).
@@ -98,6 +99,7 @@
98 99
 -define(FILE_READ_LINE,        29).
99 100
 -define(FILE_FDATASYNC,        30).
100 101
 -define(FILE_ADVISE,           31).
  102
+-define(FILE_SENDFILE,         32).
101 103
 
102 104
 %% Driver responses
103 105
 -define(FILE_RESP_OK,          0).
@@ -528,6 +530,10 @@ write_file(File, Bin) ->
528 530
     end.
529 531
 
530 532
 
  533
+%% Returns {error, Reason} | {ok, BytesCopied}
  534
+sendfile(#file_descriptor{module = ?MODULE, data = {Port, _}},
  535
+	 DestFD, Offset, Bytes) ->
  536
+    drv_command(Port, <<?FILE_SENDFILE, DestFD:32, Offset:64, Bytes:64>>).
531 537
 
532 538
 %%%-----------------------------------------------------------------
533 539
 %%% Functions operating on files without handle to the file. ?DRV.
82  lib/kernel/src/file.erl
@@ -41,7 +41,7 @@
41 41
 	 pread/2, pread/3, pwrite/2, pwrite/3,
42 42
 	 read_line/1,
43 43
 	 position/2, truncate/1, datasync/1, sync/1,
44  
-	 copy/2, copy/3]).
  44
+	 copy/2, copy/3, sendfile/4, sendfile/3, sendfile/2]).
45 45
 %% High level operations
46 46
 -export([consult/1, path_consult/2]).
47 47
 -export([eval/1, eval/2, path_eval/2, path_eval/3, path_open/3]).
@@ -287,6 +287,86 @@ raw_write_file_info(Name, #file_info{} = Info) ->
287 287
 	    Error
288 288
     end.
289 289
 
  290
+%% sendfile/4
  291
+-spec sendfile(File :: io_device(), Sock :: port() | integer(),
  292
+	       Offset :: non_neg_integer(), Bytes :: non_neg_integer())
  293
+	      -> {'ok', non_neg_integer()} | {'error', posix()}.
  294
+sendfile(_, _, _, 0) ->
  295
+    {ok, 0};
  296
+%% TODO: how to call is_pid(File) variant?
  297
+%%       is it OK to call one sendfile() from the other
  298
+%%       without documenting that clearer?
  299
+%%       specialization on arguments OK?
  300
+sendfile(File, Sock, Offset, Bytes) when is_integer(Sock)
  301
+					   andalso is_pid(File) ->
  302
+    R = file_request(File, {sendfile, Sock, Offset, Bytes}),
  303
+    wait_file_reply(File, R);
  304
+sendfile(File, Sock, Offset, Bytes) when is_port(Sock) andalso is_pid(File) ->
  305
+    {ok, SockFD} = prim_inet:getfd(Sock),
  306
+    sendfile(File, SockFD, Offset, Bytes);
  307
+sendfile(#file_descriptor{module = Module} = Handle, Sock, Offset, Bytes)
  308
+  when is_integer(Sock) ->
  309
+    Module:sendfile(Handle, Sock, Offset, Bytes);
  310
+sendfile(#file_descriptor{module = Module} = Handle, Sock, Offset, Bytes)
  311
+  when is_port(Sock) ->
  312
+    {ok, SockFD} = prim_inet:getfd(Sock),
  313
+    sendfile(Handle, SockFD, Offset, Bytes);
  314
+sendfile(_, _, _, _) ->
  315
+    {error, badarg}.
  316
+
  317
+%% sendfile/3
  318
+-spec sendfile(File :: name(), Sock :: port(), ChunkSize :: non_neg_integer())
  319
+	      -> {'ok', non_neg_integer()} | {'error', posix()}.
  320
+sendfile(File, Sock, ChunkSize) ->
  321
+    Offset = 0,
  322
+    {ok, #file_info{size = Bytes}} = read_file_info(File),
  323
+    {ok, Fd} = open(File, [read, raw, binary]),
  324
+    {ok, SockFD} = prim_inet:getfd(Sock),
  325
+    Res = sendfile_chunked_loop(Fd, SockFD, Offset, Bytes, Bytes, ChunkSize),
  326
+    %% Res = sendfile_chunked_loop(Fd, Sock, Offset, Bytes, Bytes, ChunkSize),
  327
+    ok = close(Fd),
  328
+    Res.
  329
+
  330
+-spec sendfile_calc_size(Bytes :: non_neg_integer(),
  331
+			 ChunkSize :: non_neg_integer()) -> non_neg_integer().
  332
+sendfile_calc_size(Bytes, ChunkSize) when Bytes >= ChunkSize -> ChunkSize;
  333
+sendfile_calc_size(0, _) -> 0;
  334
+sendfile_calc_size(Bytes, _) -> Bytes.
  335
+
  336
+-spec sendfile_chunked_loop(Fd :: io_device(), SockFD :: integer(),
  337
+			    Offset0 :: non_neg_integer(),
  338
+			    Bytes :: non_neg_integer(),
  339
+			    BytesLeft0 :: non_neg_integer(),
  340
+			    ChunkSize :: non_neg_integer())
  341
+			   -> {'ok', non_neg_integer()} | {'error', posix()}.
  342
+sendfile_chunked_loop(Fd, SockFD, Offset0, Bytes, BytesLeft0, ChunkSize) ->
  343
+    ToWrite = sendfile_calc_size(BytesLeft0, ChunkSize),
  344
+    case sendfile(Fd, SockFD, Offset0, ToWrite) of
  345
+	{ok, 0} ->
  346
+	    {ok, Bytes};
  347
+	{ok, Written} ->
  348
+	    Offset1 = Offset0 + Written,
  349
+	    BytesLeft1 = BytesLeft0 - Written,
  350
+	    sendfile_chunked_loop(Fd, SockFD, Offset1, Bytes,
  351
+				  BytesLeft1, ChunkSize);
  352
+	{error, Posix} ->
  353
+	    {error, Posix}
  354
+    end.
  355
+
  356
+%% TODO: keep this fun when we have chunked send?
  357
+%%       for large files this will block the vm in the driver
  358
+%%       far too long.
  359
+%% sendfile/2
  360
+-spec sendfile(File :: name(), Sock :: port()) ->
  361
+	{'ok', non_neg_integer()} | {'error', posix()}.
  362
+sendfile(File, Sock) ->
  363
+    Offset = 0,
  364
+    {ok, #file_info{size = Bytes}} = read_file_info(File),
  365
+    {ok, Fd} = open(File, [read, raw, binary]),
  366
+    Res = sendfile(Fd, Sock, Offset, Bytes),
  367
+    ok = close(Fd),
  368
+    Res.
  369
+
290 370
 %%%-----------------------------------------------------------------
291 371
 %%% File io server functions.
292 372
 %%% They operate on a single open file.
8  lib/kernel/src/file_io_server.erl
@@ -249,6 +249,14 @@ file_request(close,
249 249
 file_request({position,At}, 
250 250
 	     #state{handle=Handle,buf=Buf}=State) ->
251 251
     std_reply(position(Handle, At, Buf), State);
  252
+file_request({sendfile,DestFD,Offset,Bytes},
  253
+	     #state{handle=Handle}=State) ->
  254
+    case ?PRIM_FILE:sendfile(Handle, DestFD, Offset, Bytes) of
  255
+	{error,_}=Reply ->
  256
+	    {stop,normal,Reply,State};
  257
+	Reply ->
  258
+	    {reply,Reply,State}
  259
+    end;
252 260
 file_request(truncate, 
253 261
 	     #state{handle=Handle}=State) ->
254 262
     case ?PRIM_FILE:truncate(Handle) of
76  lib/kernel/test/file_SUITE.erl
@@ -86,6 +86,8 @@
86 86
 
87 87
 -export([standard_io/1,mini_server/1]).
88 88
 
  89
+-export([sendfile/1, sendfile_server/2]).
  90
+
89 91
 %% Debug exports
90 92
 -export([create_file_slow/2, create_file/2, create_bin/2]).
91 93
 -export([verify_file/2, verify_bin/3]).
@@ -106,7 +108,7 @@ all(suite) ->
106 108
       delayed_write, read_ahead, segment_read, segment_write,
107 109
       ipread, pid2name, interleaved_read_write, 
108 110
       otp_5814, large_file, read_line_1, read_line_2, read_line_3, read_line_4,
109  
-      standard_io],
  111
+      standard_io, sendfile],
110 112
      fini}.
111 113
 
112 114
 init(Config) when is_list(Config) ->
@@ -3914,3 +3916,75 @@ flush(Msgs) ->
3914 3916
     after 0 ->
3915 3917
 	    lists:reverse(Msgs)
3916 3918
     end.
  3919
+
  3920
+
  3921
+%% sendfile/4, sendfile/2
  3922
+-define(SENDFILE_TIMEOUT, 5000).
  3923
+-define(SENDFILE_CHUNKSIZE, 10*1024).
  3924
+
  3925
+sendfile(Config) when is_list(Config) ->
  3926
+    ?line Data = ?config(data_dir, Config),
  3927
+    ?line Real = filename:join(Data, "realmen.html"),
  3928
+    Host = "localhost",
  3929
+    Port = 1998,
  3930
+    %% TODO: running both test fails with econnrefused for the chunked test
  3931
+    %% ?line ok = sendfile_send(Host, Port, Real),
  3932
+    ?line ok = sendfile_send_chunked(Host, Port+1, Real).
  3933
+
  3934
+sendfile_send_chunked(Host, Port, File) ->
  3935
+    FileInfo = sendfile_file_info(File),
  3936
+    spawn_link(?MODULE, sendfile_server, [self(), Port]),
  3937
+    ?line {ok, Sock} = gen_tcp:connect(Host, Port, [binary,{packet,0}]),
  3938
+    ?line {ok, _} = file:sendfile(File, Sock, ?SENDFILE_CHUNKSIZE),
  3939
+    ?line ok = gen_tcp:close(Sock),
  3940
+    Dog = test_server:timetrap(test_server:seconds(5)),
  3941
+    receive
  3942
+	{ok, Bin} ->
  3943
+	    %% TODO: is it right to override the default 60s timetrap
  3944
+	    ?line ok = test_server:timetrap_cancel(Dog),
  3945
+	    FileInfo = sendfile_bin_info(Bin),
  3946
+	    ok
  3947
+    end.
  3948
+
  3949
+sendfile_send(Host, Port, File) ->
  3950
+    FileInfo = sendfile_file_info(File),
  3951
+    spawn_link(?MODULE, sendfile_server, [self(), Port]),
  3952
+    ?line {ok, Sock} = gen_tcp:connect(Host, Port, [binary,{packet,0}]),
  3953
+    ?line {ok, _} = file:sendfile(File, Sock),
  3954
+    ?line ok = gen_tcp:close(Sock),
  3955
+    Dog = test_server:timetrap(test_server:seconds(5)),
  3956
+    receive
  3957
+	{ok, Bin} ->
  3958
+	    %% TODO: is it right to override the default 60s timetrap
  3959
+	    ?line ok = test_server:timetrap_cancel(Dog),
  3960
+	    FileInfo = sendfile_bin_info(Bin),
  3961
+	    ok
  3962
+    end.
  3963
+
  3964
+sendfile_server(ClientPid, Port) ->
  3965
+    ?line {ok, LSock} = gen_tcp:listen(Port, [binary, {packet, 0},
  3966
+					      {active, false},
  3967
+					      {reuseaddr, true}]),
  3968
+    ?line {ok, Sock} = gen_tcp:accept(LSock),
  3969
+    ?line {ok, Bin} = sendfile_do_recv(Sock, []),
  3970
+    ?line ok = gen_tcp:close(Sock),
  3971
+    ClientPid ! {ok, Bin}.
  3972
+
  3973
+sendfile_do_recv(Sock, Bs) ->
  3974
+    case gen_tcp:recv(Sock, 0, ?SENDFILE_TIMEOUT) of
  3975
+	{ok, B} ->
  3976
+	    sendfile_do_recv(Sock, [B|Bs]);
  3977
+	{error, closed} ->
  3978
+	    {ok, lists:reverse(Bs)}
  3979
+    end.
  3980
+
  3981
+sendfile_file_info(File) ->
  3982
+    {ok, #file_info{size = Size}} = file:read_file_info(File),
  3983
+    {ok, Data} = file:read_file(File),
  3984
+    Md5 = erlang:md5(Data),
  3985
+    {Size, Md5}.
  3986
+
  3987
+sendfile_bin_info(Data) ->
  3988
+    Size = lists:foldl(fun(E,Sum) -> size(E) + Sum end, 0, Data),
  3989
+    Md5 = erlang:md5(Data),
  3990
+    {Size, Md5}.

0 notes on commit 34042f4

Please sign in to comment.
Something went wrong with that request. Please try again.