1require "net/ftp" 2require "test/unit" 3require "ostruct" 4require "stringio" 5 6class FTPTest < Test::Unit::TestCase 7 SERVER_ADDR = "127.0.0.1" 8 9 def setup 10 @thread = nil 11 end 12 13 def teardown 14 if @thread 15 @thread.join 16 end 17 end 18 19 def test_not_connected 20 ftp = Net::FTP.new 21 assert_raise(Net::FTPConnectionError) do 22 ftp.quit 23 end 24 end 25 26 def test_connect_fail 27 server = create_ftp_server { |sock| 28 sock.print("421 Service not available, closing control connection.\r\n") 29 } 30 begin 31 ftp = Net::FTP.new 32 assert_raise(Net::FTPTempError){ ftp.connect(SERVER_ADDR, server.port) } 33 ensure 34 ftp.close if ftp 35 server.close 36 end 37 end 38 39 def test_parse227 40 ftp = Net::FTP.new 41 host, port = ftp.send(:parse227, "227 Entering Passive Mode (192,168,0,1,12,34)") 42 assert_equal("192.168.0.1", host) 43 assert_equal(3106, port) 44 assert_raise(Net::FTPReplyError) do 45 ftp.send(:parse227, "500 Syntax error") 46 end 47 assert_raise(Net::FTPProtoError) do 48 ftp.send(:parse227, "227 Entering Passive Mode") 49 end 50 assert_raise(Net::FTPProtoError) do 51 ftp.send(:parse227, "227 Entering Passive Mode (192,168,0,1,12,34,56)") 52 end 53 assert_raise(Net::FTPProtoError) do 54 ftp.send(:parse227, "227 Entering Passive Mode (192,168,0,1)") 55 end 56 assert_raise(Net::FTPProtoError) do 57 ftp.send(:parse227, "227 ) foo bar (") 58 end 59 end 60 61 def test_parse228 62 ftp = Net::FTP.new 63 host, port = ftp.send(:parse228, "228 Entering Long Passive Mode (4,4,192,168,0,1,2,12,34)") 64 assert_equal("192.168.0.1", host) 65 assert_equal(3106, port) 66 host, port = ftp.send(:parse228, "228 Entering Long Passive Mode (6,16,16,128,0,0,0,0,0,0,0,8,8,0,32,12,65,122,2,12,34)") 67 assert_equal("1080:0000:0000:0000:0008:0800:200c:417a", host) 68 assert_equal(3106, port) 69 assert_raise(Net::FTPReplyError) do 70 ftp.send(:parse228, "500 Syntax error") 71 end 72 assert_raise(Net::FTPProtoError) do 73 ftp.send(:parse228, "228 Entering Passive Mode") 74 end 75 assert_raise(Net::FTPProtoError) do 76 ftp.send(:parse228, "228 Entering Long Passive Mode (6,4,192,168,0,1,2,12,34)") 77 end 78 assert_raise(Net::FTPProtoError) do 79 ftp.send(:parse228, "228 Entering Long Passive Mode (4,4,192,168,0,1,3,12,34,56)") 80 end 81 assert_raise(Net::FTPProtoError) do 82 ftp.send(:parse228, "228 Entering Long Passive Mode (4,16,16,128,0,0,0,0,0,0,0,8,8,0,32,12,65,122,2,12,34)") 83 end 84 assert_raise(Net::FTPProtoError) do 85 ftp.send(:parse228, "228 Entering Long Passive Mode (6,16,16,128,0,0,0,0,0,0,0,8,8,0,32,12,65,122,3,12,34,56)") 86 end 87 assert_raise(Net::FTPProtoError) do 88 ftp.send(:parse228, "228 Entering Long Passive Mode (6,16,16,128,0,0,0,0,0,0,0,8,8,0,32,12,65,122,2,12,34,56)") 89 end 90 assert_raise(Net::FTPProtoError) do 91 ftp.send(:parse227, "227 ) foo bar (") 92 end 93 end 94 95 def test_parse229 96 ftp = Net::FTP.new 97 sock = OpenStruct.new 98 sock.peeraddr = [nil, nil, nil, "1080:0000:0000:0000:0008:0800:200c:417a"] 99 ftp.instance_variable_set(:@sock, sock) 100 host, port = ftp.send(:parse229, "229 Entering Passive Mode (|||3106|)") 101 assert_equal("1080:0000:0000:0000:0008:0800:200c:417a", host) 102 assert_equal(3106, port) 103 host, port = ftp.send(:parse229, "229 Entering Passive Mode (!!!3106!)") 104 assert_equal("1080:0000:0000:0000:0008:0800:200c:417a", host) 105 assert_equal(3106, port) 106 host, port = ftp.send(:parse229, "229 Entering Passive Mode (~~~3106~)") 107 assert_equal("1080:0000:0000:0000:0008:0800:200c:417a", host) 108 assert_equal(3106, port) 109 assert_raise(Net::FTPReplyError) do 110 ftp.send(:parse229, "500 Syntax error") 111 end 112 assert_raise(Net::FTPProtoError) do 113 ftp.send(:parse229, "229 Entering Passive Mode") 114 end 115 assert_raise(Net::FTPProtoError) do 116 ftp.send(:parse229, "229 Entering Passive Mode (|!!3106!)") 117 end 118 assert_raise(Net::FTPProtoError) do 119 ftp.send(:parse229, "229 Entering Passive Mode ( 3106 )") 120 end 121 assert_raise(Net::FTPProtoError) do 122 ftp.send(:parse229, "229 Entering Passive Mode (\x7f\x7f\x7f3106\x7f)") 123 end 124 assert_raise(Net::FTPProtoError) do 125 ftp.send(:parse229, "229 ) foo bar (") 126 end 127 end 128 129 def test_parse_pasv_port 130 ftp = Net::FTP.new 131 assert_equal(12, ftp.send(:parse_pasv_port, "12")) 132 assert_equal(3106, ftp.send(:parse_pasv_port, "12,34")) 133 assert_equal(795192, ftp.send(:parse_pasv_port, "12,34,56")) 134 assert_equal(203569230, ftp.send(:parse_pasv_port, "12,34,56,78")) 135 end 136 137 def test_login 138 commands = [] 139 server = create_ftp_server { |sock| 140 sock.print("220 (test_ftp).\r\n") 141 commands.push(sock.gets) 142 sock.print("331 Please specify the password.\r\n") 143 commands.push(sock.gets) 144 sock.print("230 Login successful.\r\n") 145 commands.push(sock.gets) 146 sock.print("200 Switching to Binary mode.\r\n") 147 } 148 begin 149 begin 150 ftp = Net::FTP.new 151 ftp.connect(SERVER_ADDR, server.port) 152 ftp.login 153 assert_match(/\AUSER /, commands.shift) 154 assert_match(/\APASS /, commands.shift) 155 assert_equal("TYPE I\r\n", commands.shift) 156 assert_equal(nil, commands.shift) 157 ensure 158 ftp.close if ftp 159 end 160 ensure 161 server.close 162 end 163 end 164 165 def test_login_fail1 166 commands = [] 167 server = create_ftp_server { |sock| 168 sock.print("220 (test_ftp).\r\n") 169 commands.push(sock.gets) 170 sock.print("502 Command not implemented.\r\n") 171 } 172 begin 173 begin 174 ftp = Net::FTP.new 175 ftp.connect(SERVER_ADDR, server.port) 176 assert_raise(Net::FTPPermError){ ftp.login } 177 ensure 178 ftp.close if ftp 179 end 180 ensure 181 server.close 182 end 183 end 184 185 def test_login_fail2 186 commands = [] 187 server = create_ftp_server { |sock| 188 sock.print("220 (test_ftp).\r\n") 189 commands.push(sock.gets) 190 sock.print("331 Please specify the password.\r\n") 191 commands.push(sock.gets) 192 sock.print("530 Not logged in.\r\n") 193 } 194 begin 195 begin 196 ftp = Net::FTP.new 197 ftp.connect(SERVER_ADDR, server.port) 198 assert_raise(Net::FTPPermError){ ftp.login } 199 ensure 200 ftp.close if ftp 201 end 202 ensure 203 server.close 204 end 205 end 206 207 # TODO: How can we test open_timeout? sleep before accept cannot delay 208 # connections. 209 def _test_open_timeout_exceeded 210 commands = [] 211 server = create_ftp_server(0.2) { |sock| 212 sock.print("220 (test_ftp).\r\n") 213 commands.push(sock.gets) 214 sock.print("331 Please specify the password.\r\n") 215 commands.push(sock.gets) 216 sock.print("230 Login successful.\r\n") 217 commands.push(sock.gets) 218 sock.print("200 Switching to Binary mode.\r\n") 219 } 220 begin 221 begin 222 ftp = Net::FTP.new 223 ftp.open_timeout = 0.1 224 ftp.connect(SERVER_ADDR, server.port) 225 assert_raise(Net::OpenTimeout) do 226 ftp.login 227 end 228 assert_match(/\AUSER /, commands.shift) 229 assert_match(/\APASS /, commands.shift) 230 assert_equal(nil, commands.shift) 231 ensure 232 ftp.close if ftp 233 end 234 ensure 235 server.close 236 end 237 end 238 239 def test_read_timeout_exceeded 240 commands = [] 241 server = create_ftp_server { |sock| 242 sock.print("220 (test_ftp).\r\n") 243 commands.push(sock.gets) 244 sleep(0.1) 245 sock.print("331 Please specify the password.\r\n") 246 commands.push(sock.gets) 247 sleep(0.3) 248 sock.print("230 Login successful.\r\n") 249 commands.push(sock.gets) 250 sleep(0.1) 251 sock.print("200 Switching to Binary mode.\r\n") 252 } 253 begin 254 begin 255 ftp = Net::FTP.new 256 ftp.read_timeout = 0.2 257 ftp.connect(SERVER_ADDR, server.port) 258 assert_raise(Net::ReadTimeout) do 259 ftp.login 260 end 261 assert_match(/\AUSER /, commands.shift) 262 assert_match(/\APASS /, commands.shift) 263 assert_equal(nil, commands.shift) 264 ensure 265 ftp.close if ftp 266 end 267 ensure 268 server.close 269 end 270 end 271 272 def test_read_timeout_not_exceeded 273 commands = [] 274 server = create_ftp_server { |sock| 275 sock.print("220 (test_ftp).\r\n") 276 commands.push(sock.gets) 277 sleep(0.1) 278 sock.print("331 Please specify the password.\r\n") 279 commands.push(sock.gets) 280 sleep(0.1) 281 sock.print("230 Login successful.\r\n") 282 commands.push(sock.gets) 283 sleep(0.1) 284 sock.print("200 Switching to Binary mode.\r\n") 285 } 286 begin 287 begin 288 ftp = Net::FTP.new 289 ftp.read_timeout = 0.2 290 ftp.connect(SERVER_ADDR, server.port) 291 ftp.login 292 assert_match(/\AUSER /, commands.shift) 293 assert_match(/\APASS /, commands.shift) 294 assert_equal("TYPE I\r\n", commands.shift) 295 assert_equal(nil, commands.shift) 296 ensure 297 ftp.close 298 assert_equal(0.2, ftp.read_timeout) 299 end 300 ensure 301 server.close 302 end 303 end 304 305 def test_list_read_timeout_exceeded 306 commands = [] 307 list_lines = [ 308 "-rw-r--r-- 1 0 0 0 Mar 30 11:22 foo.txt", 309 "-rw-r--r-- 1 0 0 0 Mar 30 11:22 bar.txt", 310 "-rw-r--r-- 1 0 0 0 Mar 30 11:22 baz.txt" 311 ] 312 server = create_ftp_server { |sock| 313 sock.print("220 (test_ftp).\r\n") 314 commands.push(sock.gets) 315 sock.print("331 Please specify the password.\r\n") 316 commands.push(sock.gets) 317 sock.print("230 Login successful.\r\n") 318 commands.push(sock.gets) 319 sock.print("200 Switching to Binary mode.\r\n") 320 commands.push(sock.gets) 321 sock.print("200 Switching to ASCII mode.\r\n") 322 line = sock.gets 323 commands.push(line) 324 port_args = line.slice(/\APORT (.*)/, 1).split(/,/) 325 host = port_args[0, 4].join(".") 326 port = port_args[4, 2].map(&:to_i).inject {|x, y| (x << 8) + y} 327 sock.print("200 PORT command successful.\r\n") 328 commands.push(sock.gets) 329 sock.print("150 Here comes the directory listing.\r\n") 330 begin 331 conn = TCPSocket.new(host, port) 332 list_lines.each_with_index do |l, i| 333 if i == 1 334 sleep(0.5) 335 else 336 sleep(0.1) 337 end 338 conn.print(l, "\r\n") 339 end 340 rescue Errno::EPIPE 341 ensure 342 assert_nil($!) 343 conn.close 344 end 345 sock.print("226 Directory send OK.\r\n") 346 } 347 begin 348 begin 349 ftp = Net::FTP.new 350 ftp.read_timeout = 0.2 351 ftp.connect(SERVER_ADDR, server.port) 352 ftp.login 353 assert_match(/\AUSER /, commands.shift) 354 assert_match(/\APASS /, commands.shift) 355 assert_equal("TYPE I\r\n", commands.shift) 356 assert_raise(Net::ReadTimeout) do 357 ftp.list 358 end 359 assert_equal("TYPE A\r\n", commands.shift) 360 assert_match(/\APORT /, commands.shift) 361 assert_equal("LIST\r\n", commands.shift) 362 assert_equal(nil, commands.shift) 363 ensure 364 ftp.close if ftp 365 end 366 ensure 367 server.close 368 end 369 end 370 371 def test_list_read_timeout_not_exceeded 372 commands = [] 373 list_lines = [ 374 "-rw-r--r-- 1 0 0 0 Mar 30 11:22 foo.txt", 375 "-rw-r--r-- 1 0 0 0 Mar 30 11:22 bar.txt", 376 "-rw-r--r-- 1 0 0 0 Mar 30 11:22 baz.txt" 377 ] 378 server = create_ftp_server { |sock| 379 sock.print("220 (test_ftp).\r\n") 380 commands.push(sock.gets) 381 sock.print("331 Please specify the password.\r\n") 382 commands.push(sock.gets) 383 sock.print("230 Login successful.\r\n") 384 commands.push(sock.gets) 385 sock.print("200 Switching to Binary mode.\r\n") 386 commands.push(sock.gets) 387 sock.print("200 Switching to ASCII mode.\r\n") 388 line = sock.gets 389 commands.push(line) 390 port_args = line.slice(/\APORT (.*)/, 1).split(/,/) 391 host = port_args[0, 4].join(".") 392 port = port_args[4, 2].map(&:to_i).inject {|x, y| (x << 8) + y} 393 sock.print("200 PORT command successful.\r\n") 394 commands.push(sock.gets) 395 sock.print("150 Here comes the directory listing.\r\n") 396 conn = TCPSocket.new(host, port) 397 list_lines.each do |l| 398 sleep(0.1) 399 conn.print(l, "\r\n") 400 end 401 conn.close 402 sock.print("226 Directory send OK.\r\n") 403 commands.push(sock.gets) 404 sock.print("200 Switching to Binary mode.\r\n") 405 } 406 begin 407 begin 408 ftp = Net::FTP.new 409 ftp.read_timeout = 0.2 410 ftp.connect(SERVER_ADDR, server.port) 411 ftp.login 412 assert_match(/\AUSER /, commands.shift) 413 assert_match(/\APASS /, commands.shift) 414 assert_equal("TYPE I\r\n", commands.shift) 415 assert_equal(list_lines, ftp.list) 416 assert_equal("TYPE A\r\n", commands.shift) 417 assert_match(/\APORT /, commands.shift) 418 assert_equal("LIST\r\n", commands.shift) 419 assert_equal("TYPE I\r\n", commands.shift) 420 assert_equal(nil, commands.shift) 421 ensure 422 ftp.close if ftp 423 end 424 ensure 425 server.close 426 end 427 end 428 429 def test_list_fail 430 commands = [] 431 list_lines = [ 432 "-rw-r--r-- 1 0 0 0 Mar 30 11:22 foo.txt", 433 "-rw-r--r-- 1 0 0 0 Mar 30 11:22 bar.txt", 434 "-rw-r--r-- 1 0 0 0 Mar 30 11:22 baz.txt" 435 ] 436 server = create_ftp_server { |sock| 437 sock.print("220 (test_ftp).\r\n") 438 commands.push(sock.gets) 439 sock.print("331 Please specify the password.\r\n") 440 commands.push(sock.gets) 441 sock.print("230 Login successful.\r\n") 442 commands.push(sock.gets) 443 sock.print("200 Switching to Binary mode.\r\n") 444 commands.push(sock.gets) 445 sock.print("200 Switching to ASCII mode.\r\n") 446 line = sock.gets 447 commands.push(line) 448 port_args = line.slice(/\APORT (.*)/, 1).split(/,/) 449 host = port_args[0, 4].join(".") 450 port = port_args[4, 2].map(&:to_i).inject {|x, y| (x << 8) + y} 451 sock.print("200 PORT command successful.\r\n") 452 commands.push(sock.gets) 453 sock.print("553 Requested action not taken.\r\n") 454 commands.push(sock.gets) 455 sock.print("200 Switching to Binary mode.\r\n") 456 } 457 begin 458 begin 459 ftp = Net::FTP.new 460 ftp.read_timeout = 0.2 461 ftp.connect(SERVER_ADDR, server.port) 462 ftp.login 463 assert_match(/\AUSER /, commands.shift) 464 assert_match(/\APASS /, commands.shift) 465 assert_equal("TYPE I\r\n", commands.shift) 466 assert_raise(Net::FTPPermError){ ftp.list } 467 assert_equal("TYPE A\r\n", commands.shift) 468 assert_match(/\APORT /, commands.shift) 469 assert_equal("LIST\r\n", commands.shift) 470 assert_equal("TYPE I\r\n", commands.shift) 471 assert_equal(nil, commands.shift) 472 ensure 473 ftp.close if ftp 474 end 475 ensure 476 server.close 477 end 478 end 479 480 def test_retrbinary_read_timeout_exceeded 481 commands = [] 482 binary_data = (0..0xff).map {|i| i.chr}.join * 4 * 3 483 server = create_ftp_server { |sock| 484 sock.print("220 (test_ftp).\r\n") 485 commands.push(sock.gets) 486 sock.print("331 Please specify the password.\r\n") 487 commands.push(sock.gets) 488 sock.print("230 Login successful.\r\n") 489 commands.push(sock.gets) 490 sock.print("200 Switching to Binary mode.\r\n") 491 line = sock.gets 492 commands.push(line) 493 port_args = line.slice(/\APORT (.*)/, 1).split(/,/) 494 host = port_args[0, 4].join(".") 495 port = port_args[4, 2].map(&:to_i).inject {|x, y| (x << 8) + y} 496 sock.print("200 PORT command successful.\r\n") 497 commands.push(sock.gets) 498 sock.print("150 Opening BINARY mode data connection for foo (#{binary_data.size} bytes)\r\n") 499 conn = TCPSocket.new(host, port) 500 sleep(0.1) 501 conn.print(binary_data[0,1024]) 502 sleep(0.5) 503 conn.print(binary_data[1024, 1024]) rescue nil # may raise EPIPE or something 504 conn.close 505 sock.print("226 Transfer complete.\r\n") 506 } 507 begin 508 begin 509 ftp = Net::FTP.new 510 ftp.read_timeout = 0.2 511 ftp.connect(SERVER_ADDR, server.port) 512 ftp.login 513 assert_match(/\AUSER /, commands.shift) 514 assert_match(/\APASS /, commands.shift) 515 assert_equal("TYPE I\r\n", commands.shift) 516 buf = "" 517 assert_raise(Net::ReadTimeout) do 518 ftp.retrbinary("RETR foo", 1024) do |s| 519 buf << s 520 end 521 end 522 assert_equal(1024, buf.bytesize) 523 assert_equal(binary_data[0, 1024], buf) 524 assert_match(/\APORT /, commands.shift) 525 assert_equal("RETR foo\r\n", commands.shift) 526 assert_equal(nil, commands.shift) 527 ensure 528 ftp.close unless ftp.closed? 529 end 530 ensure 531 server.close 532 end 533 end 534 535 def test_retrbinary_read_timeout_not_exceeded 536 commands = [] 537 binary_data = (0..0xff).map {|i| i.chr}.join * 4 * 3 538 server = create_ftp_server { |sock| 539 sock.print("220 (test_ftp).\r\n") 540 commands.push(sock.gets) 541 sock.print("331 Please specify the password.\r\n") 542 commands.push(sock.gets) 543 sock.print("230 Login successful.\r\n") 544 commands.push(sock.gets) 545 sock.print("200 Switching to Binary mode.\r\n") 546 line = sock.gets 547 commands.push(line) 548 port_args = line.slice(/\APORT (.*)/, 1).split(/,/) 549 host = port_args[0, 4].join(".") 550 port = port_args[4, 2].map(&:to_i).inject {|x, y| (x << 8) + y} 551 sock.print("200 PORT command successful.\r\n") 552 commands.push(sock.gets) 553 sock.print("150 Opening BINARY mode data connection for foo (#{binary_data.size} bytes)\r\n") 554 conn = TCPSocket.new(host, port) 555 binary_data.scan(/.{1,1024}/nm) do |s| 556 sleep(0.1) 557 conn.print(s) 558 end 559 conn.shutdown(Socket::SHUT_WR) 560 conn.read 561 conn.close 562 sock.print("226 Transfer complete.\r\n") 563 } 564 begin 565 begin 566 ftp = Net::FTP.new 567 ftp.read_timeout = 0.2 568 ftp.connect(SERVER_ADDR, server.port) 569 ftp.login 570 assert_match(/\AUSER /, commands.shift) 571 assert_match(/\APASS /, commands.shift) 572 assert_equal("TYPE I\r\n", commands.shift) 573 buf = "" 574 ftp.retrbinary("RETR foo", 1024) do |s| 575 buf << s 576 end 577 assert_equal(binary_data.bytesize, buf.bytesize) 578 assert_equal(binary_data, buf) 579 assert_match(/\APORT /, commands.shift) 580 assert_equal("RETR foo\r\n", commands.shift) 581 assert_equal(nil, commands.shift) 582 ensure 583 ftp.close if ftp 584 end 585 ensure 586 server.close 587 end 588 end 589 590 def test_retrbinary_fail 591 commands = [] 592 binary_data = (0..0xff).map {|i| i.chr}.join * 4 * 3 593 server = create_ftp_server { |sock| 594 sock.print("220 (test_ftp).\r\n") 595 commands.push(sock.gets) 596 sock.print("331 Please specify the password.\r\n") 597 commands.push(sock.gets) 598 sock.print("230 Login successful.\r\n") 599 commands.push(sock.gets) 600 sock.print("200 Switching to Binary mode.\r\n") 601 line = sock.gets 602 commands.push(line) 603 port_args = line.slice(/\APORT (.*)/, 1).split(/,/) 604 host = port_args[0, 4].join(".") 605 port = port_args[4, 2].map(&:to_i).inject {|x, y| (x << 8) + y} 606 sock.print("200 PORT command successful.\r\n") 607 commands.push(sock.gets) 608 sock.print("550 Requested action not taken.\r\n") 609 } 610 begin 611 begin 612 ftp = Net::FTP.new 613 ftp.read_timeout = 0.2 614 ftp.connect(SERVER_ADDR, server.port) 615 ftp.login 616 assert_match(/\AUSER /, commands.shift) 617 assert_match(/\APASS /, commands.shift) 618 assert_equal("TYPE I\r\n", commands.shift) 619 assert_raise(Net::FTPPermError){ ftp.retrbinary("RETR foo", 1024) } 620 assert_match(/\APORT /, commands.shift) 621 assert_equal("RETR foo\r\n", commands.shift) 622 assert_equal(nil, commands.shift) 623 ensure 624 ftp.close if ftp 625 end 626 ensure 627 server.close 628 end 629 end 630 631 def test_storbinary 632 commands = [] 633 binary_data = (0..0xff).map {|i| i.chr}.join * 4 * 3 634 stored_data = nil 635 server = create_ftp_server { |sock| 636 sock.print("220 (test_ftp).\r\n") 637 commands.push(sock.gets) 638 sock.print("331 Please specify the password.\r\n") 639 commands.push(sock.gets) 640 sock.print("230 Login successful.\r\n") 641 commands.push(sock.gets) 642 sock.print("200 Switching to Binary mode.\r\n") 643 line = sock.gets 644 commands.push(line) 645 port_args = line.slice(/\APORT (.*)/, 1).split(/,/) 646 host = port_args[0, 4].join(".") 647 port = port_args[4, 2].map(&:to_i).inject {|x, y| (x << 8) + y} 648 sock.print("200 PORT command successful.\r\n") 649 commands.push(sock.gets) 650 sock.print("150 Opening BINARY mode data connection for foo\r\n") 651 conn = TCPSocket.new(host, port) 652 stored_data = conn.read 653 conn.close 654 sock.print("226 Transfer complete.\r\n") 655 } 656 begin 657 begin 658 ftp = Net::FTP.new 659 ftp.read_timeout = 0.2 660 ftp.connect(SERVER_ADDR, server.port) 661 ftp.login 662 assert_match(/\AUSER /, commands.shift) 663 assert_match(/\APASS /, commands.shift) 664 assert_equal("TYPE I\r\n", commands.shift) 665 ftp.storbinary("STOR foo", StringIO.new(binary_data), 1024) 666 assert_equal(binary_data, stored_data) 667 assert_match(/\APORT /, commands.shift) 668 assert_equal("STOR foo\r\n", commands.shift) 669 assert_equal(nil, commands.shift) 670 ensure 671 ftp.close if ftp 672 end 673 ensure 674 server.close 675 end 676 end 677 678 def test_storbinary_fail 679 commands = [] 680 binary_data = (0..0xff).map {|i| i.chr}.join * 4 * 3 681 stored_data = nil 682 server = create_ftp_server { |sock| 683 sock.print("220 (test_ftp).\r\n") 684 commands.push(sock.gets) 685 sock.print("331 Please specify the password.\r\n") 686 commands.push(sock.gets) 687 sock.print("230 Login successful.\r\n") 688 commands.push(sock.gets) 689 sock.print("200 Switching to Binary mode.\r\n") 690 line = sock.gets 691 commands.push(line) 692 port_args = line.slice(/\APORT (.*)/, 1).split(/,/) 693 host = port_args[0, 4].join(".") 694 port = port_args[4, 2].map(&:to_i).inject {|x, y| (x << 8) + y} 695 sock.print("200 PORT command successful.\r\n") 696 commands.push(sock.gets) 697 sock.print("452 Requested file action aborted.\r\n") 698 } 699 begin 700 begin 701 ftp = Net::FTP.new 702 ftp.read_timeout = 0.2 703 ftp.connect(SERVER_ADDR, server.port) 704 ftp.login 705 assert_match(/\AUSER /, commands.shift) 706 assert_match(/\APASS /, commands.shift) 707 assert_equal("TYPE I\r\n", commands.shift) 708 assert_raise(Net::FTPTempError){ ftp.storbinary("STOR foo", StringIO.new(binary_data), 1024) } 709 assert_match(/\APORT /, commands.shift) 710 assert_equal("STOR foo\r\n", commands.shift) 711 assert_equal(nil, commands.shift) 712 ensure 713 ftp.close if ftp 714 end 715 ensure 716 server.close 717 end 718 end 719 720 def test_abort 721 commands = [] 722 server = create_ftp_server { |sock| 723 sock.print("220 (test_ftp).\r\n") 724 commands.push(sock.gets) 725 sock.print("331 Please specify the password.\r\n") 726 commands.push(sock.gets) 727 sock.print("230 Login successful.\r\n") 728 commands.push(sock.gets) 729 sock.print("200 Switching to Binary mode.\r\n") 730 commands.push(sock.recv(1024)) 731 sock.print("225 No transfer to ABOR.\r\n") 732 } 733 begin 734 begin 735 ftp = Net::FTP.new 736 #ftp.read_timeout = 0.2 737 ftp.connect(SERVER_ADDR, server.port) 738 ftp.login 739 assert_match(/\AUSER /, commands.shift) 740 assert_match(/\APASS /, commands.shift) 741 assert_equal("TYPE I\r\n", commands.shift) 742 ftp.abort 743 assert_equal("ABOR\r", commands.shift) 744 assert_equal(nil, commands.shift) 745 ensure 746 ftp.close if ftp 747 end 748 ensure 749 server.close 750 end 751 end 752 753 def test_status 754 commands = [] 755 server = create_ftp_server { |sock| 756 sock.print("220 (test_ftp).\r\n") 757 commands.push(sock.gets) 758 sock.print("331 Please specify the password.\r\n") 759 commands.push(sock.gets) 760 sock.print("230 Login successful.\r\n") 761 commands.push(sock.gets) 762 sock.print("200 Switching to Binary mode.\r\n") 763 commands.push(sock.recv(1024)) 764 sock.print("211 End of status\r\n") 765 } 766 begin 767 begin 768 ftp = Net::FTP.new 769 ftp.read_timeout = 0.2 770 ftp.connect(SERVER_ADDR, server.port) 771 ftp.login 772 assert_match(/\AUSER /, commands.shift) 773 assert_match(/\APASS /, commands.shift) 774 assert_equal("TYPE I\r\n", commands.shift) 775 ftp.status 776 assert_equal("STAT\r", commands.shift) 777 assert_equal(nil, commands.shift) 778 ensure 779 ftp.close if ftp 780 end 781 ensure 782 server.close 783 end 784 end 785 786 private 787 788 789 def create_ftp_server(sleep_time = nil) 790 server = TCPServer.new(SERVER_ADDR, 0) 791 @thread = Thread.start do 792 begin 793 if sleep_time 794 sleep(sleep_time) 795 end 796 sock = server.accept 797 begin 798 yield(sock) 799 sock.shutdown(Socket::SHUT_WR) 800 sock.read_timeout = 1 801 sock.read unless sock.eof? 802 ensure 803 sock.close 804 end 805 rescue 806 end 807 end 808 def server.port 809 addr[1] 810 end 811 return server 812 end 813end 814