@@ -3,15 +3,22 @@ defmodule ThousandIsland.SocketTest do
33
44 use Machete
55
6- def gen_tcp_setup ( _context ) do
7- { :ok , % { client_mod: :gen_tcp , client_opts: [ active: false ] , server_opts: [ ] } }
6+ @ eight_mb_chunks 8 * 1024 * 1024
7+ @ large_file_size 256 * 1024 * 1024
8+
9+ def gen_tcp_setup ( context ) do
10+ if context [ :tmp_dir ] , do: maybe_create_big_file ( context . tmp_dir )
11+ { :ok , % { client_mod: :gen_tcp , client_opts: [ :binary , active: false ] , server_opts: [ ] } }
812 end
913
10- def ssl_setup ( _context ) do
14+ def ssl_setup ( context ) do
15+ if context [ :tmp_dir ] , do: maybe_create_big_file ( context . tmp_dir )
16+
1117 { :ok ,
1218 % {
1319 client_mod: :ssl ,
1420 client_opts: [
21+ :binary ,
1522 active: false ,
1623 verify: :verify_peer ,
1724 cacertfile: Path . join ( __DIR__ , "../support/ca.pem" )
@@ -50,6 +57,18 @@ defmodule ThousandIsland.SocketTest do
5057 end
5158 end
5259
60+ defmodule LargeSendfile do
61+ use ThousandIsland.Handler
62+
63+ @ impl ThousandIsland.Handler
64+ def handle_connection ( socket , state ) do
65+ large_file_path = Path . join ( state [ :tmp_dir ] , "large_sendfile" )
66+ ThousandIsland.Socket . sendfile ( socket , large_file_path , 0 , 0 )
67+ send ( state [ :test_pid ] , Process . info ( self ( ) , :monitored_by ) )
68+ { :close , state }
69+ end
70+ end
71+
5372 defmodule Closer do
5473 use ThousandIsland.Handler
5574
@@ -88,7 +107,7 @@ defmodule ThousandIsland.SocketTest do
88107 { :ok , client } = context . client_mod . connect ( ~c" localhost" , port , context . client_opts )
89108
90109 assert context . client_mod . send ( client , "HELLO" ) == :ok
91- assert context . client_mod . recv ( client , 0 ) == { :ok , ~c " HELLO" }
110+ assert context . client_mod . recv ( client , 0 ) == { :ok , "HELLO" }
92111 end
93112
94113 test "it should close connections" , context do
@@ -105,7 +124,7 @@ defmodule ThousandIsland.SocketTest do
105124 { :ok , client } = context . client_mod . connect ( ~c" localhost" , port , context . client_opts )
106125
107126 :ok = context . client_mod . send ( client , "HELLO" )
108- { :ok , ~c " HELLO" } = context . client_mod . recv ( client , 0 )
127+ { :ok , "HELLO" } = context . client_mod . recv ( client , 0 )
109128 context . client_mod . close ( client )
110129
111130 assert_receive { :telemetry , [ :thousand_island , :connection , :recv ] , measurements ,
@@ -151,7 +170,18 @@ defmodule ThousandIsland.SocketTest do
151170 server_opts = Keyword . put ( context . server_opts , :handler_options , test_pid: self ( ) )
152171 { :ok , port } = start_handler ( Sendfile , server_opts )
153172 { :ok , client } = context . client_mod . connect ( ~c" localhost" , port , context . client_opts )
154- assert context . client_mod . recv ( client , 9 ) == { :ok , ~c" ABCDEFBCD" }
173+ assert context . client_mod . recv ( client , 9 ) == { :ok , "ABCDEFBCD" }
174+ assert_receive { :monitored_by , [ ] }
175+ end
176+
177+ @ tag :tmp_dir
178+ test "it should send large files" , % { tmp_dir: tmp_dir } = context do
179+ opts = [ test_pid: self ( ) , tmp_dir: tmp_dir ]
180+ server_opts = Keyword . put ( context . server_opts , :handler_options , opts )
181+ { :ok , port } = start_handler ( LargeSendfile , server_opts )
182+ { :ok , client } = context . client_mod . connect ( ~c" localhost" , port , context . client_opts )
183+ total_received = receive_all_data ( context . client_mod , client , @ large_file_size , "" )
184+ assert byte_size ( total_received ) == @ large_file_size
155185 assert_receive { :monitored_by , [ ] }
156186 end
157187 end
@@ -193,7 +223,18 @@ defmodule ThousandIsland.SocketTest do
193223 server_opts = Keyword . put ( context . server_opts , :handler_options , test_pid: self ( ) )
194224 { :ok , port } = start_handler ( Sendfile , server_opts )
195225 { :ok , client } = context . client_mod . connect ( ~c" localhost" , port , context . client_opts )
196- assert context . client_mod . recv ( client , 9 ) == { :ok , ~c" ABCDEFBCD" }
226+ assert context . client_mod . recv ( client , 9 ) == { :ok , "ABCDEFBCD" }
227+ assert_receive { :monitored_by , [ _pid ] }
228+ end
229+
230+ @ tag :tmp_dir
231+ test "it should send large files" , % { tmp_dir: tmp_dir } = context do
232+ opts = [ test_pid: self ( ) , tmp_dir: tmp_dir ]
233+ server_opts = Keyword . put ( context . server_opts , :handler_options , opts )
234+ { :ok , port } = start_handler ( LargeSendfile , server_opts )
235+ { :ok , client } = context . client_mod . connect ( ~c" localhost" , port , context . client_opts )
236+ total_received = receive_all_data ( context . client_mod , client , @ large_file_size , "" )
237+ assert byte_size ( total_received ) == @ large_file_size
197238 assert_receive { :monitored_by , [ _pid ] }
198239 end
199240 end
@@ -204,4 +245,32 @@ defmodule ThousandIsland.SocketTest do
204245 { :ok , { _ip , port } } = ThousandIsland . listener_info ( server_pid )
205246 { :ok , port }
206247 end
248+
249+ defp maybe_create_big_file ( tmp_dir ) do
250+ path = Path . join ( tmp_dir , "large_sendfile" )
251+
252+ unless File . exists? ( path ) and File . stat! ( path ) . size == @ large_file_size do
253+ # Create a large file by writing 8MB chunks to avoid memory issues
254+ chunks_needed = div ( @ large_file_size , @ eight_mb_chunks )
255+ chunk_data = :binary . copy ( << 0 >> , @ eight_mb_chunks )
256+ { :ok , file } = File . open ( path , [ :write , :binary ] )
257+ for _i <- 1 .. chunks_needed , do: IO . binwrite ( file , chunk_data )
258+ File . close ( file )
259+ end
260+ end
261+
262+ defp receive_all_data ( _ , _ , total_size , acc ) when total_size <= 0 , do: acc
263+
264+ defp receive_all_data ( client_mod , client , total_size , acc ) do
265+ case client_mod . recv ( client , @ eight_mb_chunks ) do
266+ { :ok , data } ->
267+ receive_all_data ( client_mod , client , total_size - byte_size ( data ) , acc <> data )
268+
269+ { :error , :closed } when byte_size ( acc ) == total_size ->
270+ acc
271+
272+ { :error , reason } ->
273+ raise "Failed to receive data: #{ inspect ( reason ) } "
274+ end
275+ end
207276end
0 commit comments