@@ -495,21 +495,122 @@ class TestURLSession: LoopbackServerTest {
495
495
waitForExpectations ( timeout: 30 )
496
496
}
497
497
498
- func test_timeoutInterval ( ) {
498
+ func test_httpTimeout ( ) {
499
499
let config = URLSessionConfiguration . default
500
500
config. timeoutIntervalForRequest = 10
501
- let urlString = " http://127.0.0.1:-1 /Peru "
501
+ let urlString = " http://127.0.0.1: \( TestURLSession . serverPort ) /Peru "
502
502
let session = URLSession ( configuration: config, delegate: nil , delegateQueue: nil )
503
503
let expect = expectation ( description: " GET \( urlString) : will timeout " )
504
- var req = URLRequest ( url: URL ( string: " http://127.0.0.1:-1/Peru " ) !)
504
+ var req = URLRequest ( url: URL ( string: urlString) !)
505
+ req. setValue ( " 3 " , forHTTPHeaderField: " x-pause " )
505
506
req. timeoutInterval = 1
506
507
let task = session. dataTask ( with: req) { ( data, _, error) -> Void in
507
508
defer { expect. fulfill ( ) }
508
- XCTAssertNotNil ( error)
509
+ XCTAssertEqual ( ( error as? URLError ) ? . code , . timedOut , " Task should fail with URLError.timedOut error " )
509
510
}
510
511
task. resume ( )
512
+ waitForExpectations ( timeout: 30 )
513
+ }
514
+
515
+ func test_connectTimeout( ) {
516
+ // Reconfigure http server for this specific scenario:
517
+ // a slow request keeps web server busy, while other
518
+ // request times out on connection attempt.
519
+ Self . stopServer ( )
520
+ Self . options = Options ( serverBacklog: 1 , isAsynchronous: false )
521
+ Self . startServer ( )
522
+
523
+ let config = URLSessionConfiguration . default
524
+ let slowUrlString = " http://127.0.0.1: \( TestURLSession . serverPort) /Peru "
525
+ let fastUrlString = " http://127.0.0.1: \( TestURLSession . serverPort) /Italy "
526
+ let session = URLSession ( configuration: config, delegate: nil , delegateQueue: nil )
527
+ let slowReqExpect = expectation ( description: " GET \( slowUrlString) : will complete " )
528
+ let fastReqExpect = expectation ( description: " GET \( fastUrlString) : will timeout " )
529
+
530
+ var slowReq = URLRequest ( url: URL ( string: slowUrlString) !)
531
+ slowReq. setValue ( " 3 " , forHTTPHeaderField: " x-pause " )
532
+
533
+ var fastReq = URLRequest ( url: URL ( string: fastUrlString) !)
534
+ fastReq. timeoutInterval = 1
535
+
536
+ let slowTask = session. dataTask ( with: slowReq) { ( data, _, error) -> Void in
537
+ slowReqExpect. fulfill ( )
538
+ }
539
+ let fastTask = session. dataTask ( with: fastReq) { ( data, _, error) -> Void in
540
+ defer { fastReqExpect. fulfill ( ) }
541
+ XCTAssertEqual ( ( error as? URLError ) ? . code, . timedOut, " Task should fail with URLError.timedOut error " )
542
+ }
543
+ slowTask. resume ( )
544
+ Thread . sleep ( forTimeInterval: 0.1 ) // Give slow task some time to start
545
+ fastTask. resume ( )
511
546
512
547
waitForExpectations ( timeout: 30 )
548
+
549
+ // Reconfigure http server back to default settings
550
+ Self . stopServer ( )
551
+ Self . options = . default
552
+ Self . startServer ( )
553
+ }
554
+
555
+ func test_repeatedRequestsStress( ) throws {
556
+ // TODO: try disabling curl connection cache to force socket close early. Or create several url sessions (they have cleanup in deinit)
557
+
558
+ let config = URLSessionConfiguration . default
559
+ let urlString = " http://127.0.0.1: \( TestURLSession . serverPort) /Peru "
560
+ let session = URLSession ( configuration: config, delegate: nil , delegateQueue: nil )
561
+ let req = URLRequest ( url: URL ( string: urlString) !)
562
+
563
+ var requestsLeft = 3000
564
+ let expect = expectation ( description: " \( requestsLeft) x GET \( urlString) " )
565
+
566
+ func doRequests( completion: @escaping ( ) -> Void ) {
567
+ // We only care about completion of one of the tasks,
568
+ // so we could move to next cycle.
569
+ // Some overlapping would happen and that's what we
570
+ // want actually to provoke issue with socket reuse
571
+ // on Windows.
572
+ let task = session. dataTask ( with: req) { ( _, _, _) -> Void in
573
+ }
574
+ task. resume ( )
575
+ let task2 = session. dataTask ( with: req) { ( _, _, _) -> Void in
576
+ }
577
+ task2. resume ( )
578
+ let task3 = session. dataTask ( with: req) { ( _, _, _) -> Void in
579
+ completion ( )
580
+ }
581
+ task3. resume ( )
582
+ }
583
+
584
+ func checkCountAndRunNext( ) {
585
+ guard requestsLeft > 0 else {
586
+ expect. fulfill ( )
587
+ return
588
+ }
589
+ requestsLeft -= 1
590
+ doRequests ( completion: checkCountAndRunNext)
591
+ }
592
+
593
+ checkCountAndRunNext ( )
594
+
595
+ waitForExpectations ( timeout: 30 )
596
+ }
597
+
598
+ func test_largePost( ) throws {
599
+ let session = URLSession ( configuration: URLSessionConfiguration . default)
600
+ var dataTask : URLSessionDataTask ? = nil
601
+
602
+ let data = Data ( ( 0 ..< 131076 ) . map { _ in UInt8 . random ( in: UInt8 . min ... UInt8 . max) } )
603
+ var req = URLRequest ( url: URL ( string: " http://127.0.0.1: \( TestURLSession . serverPort) /POST " ) !)
604
+ req. httpMethod = " POST "
605
+ req. httpBody = data
606
+
607
+ let e = expectation ( description: " POST completed " )
608
+ dataTask = session. uploadTask ( with: req, from: data) { data, response, error in
609
+ e. fulfill ( )
610
+ }
611
+ dataTask? . resume ( )
612
+
613
+ waitForExpectations ( timeout: 5 )
513
614
}
514
615
515
616
func test_httpRedirectionWithCode300( ) throws {
@@ -2049,7 +2150,8 @@ class TestURLSession: LoopbackServerTest {
2049
2150
( " test_taskTimeout " , test_taskTimeout) ,
2050
2151
( " test_verifyRequestHeaders " , test_verifyRequestHeaders) ,
2051
2152
( " test_verifyHttpAdditionalHeaders " , test_verifyHttpAdditionalHeaders) ,
2052
- ( " test_timeoutInterval " , test_timeoutInterval) ,
2153
+ ( " test_httpTimeout " , test_httpTimeout) ,
2154
+ ( " test_connectTimeout " , test_connectTimeout) ,
2053
2155
( " test_httpRedirectionWithCode300 " , test_httpRedirectionWithCode300) ,
2054
2156
( " test_httpRedirectionWithCode301_302 " , test_httpRedirectionWithCode301_302) ,
2055
2157
( " test_httpRedirectionWithCode303 " , test_httpRedirectionWithCode303) ,
@@ -2098,6 +2200,7 @@ class TestURLSession: LoopbackServerTest {
2098
2200
/* ⚠️ */ testExpectedToFail ( test_noDoubleCallbackWhenCancellingAndProtocolFailsFast, " This test crashes nondeterministically: https://bugs.swift.org/browse/SR-11310 " ) ) ,
2099
2201
/* ⚠️ */ ( " test_cancelledTasksCannotBeResumed " , testExpectedToFail ( test_cancelledTasksCannotBeResumed, " Breaks on Ubuntu 18.04 " ) ) ,
2100
2202
]
2203
+ #if NS_FOUNDATION_ALLOWS_TESTABLE_IMPORT
2101
2204
if #available( macOS 12 . 0 , * ) {
2102
2205
retVal. append ( contentsOf: [
2103
2206
( " test_webSocket " , asyncTest ( test_webSocket) ) ,
@@ -2106,6 +2209,14 @@ class TestURLSession: LoopbackServerTest {
2106
2209
( " test_webSocketSemiAbruptClose " , asyncTest ( test_webSocketSemiAbruptClose) ) ,
2107
2210
] )
2108
2211
}
2212
+ #endif
2213
+ // This is heavy test and it could time out in CI environment giving false negative result.
2214
+ // Uncomment to use for local URLSession stability testing.
2215
+ // #if os(Windows)
2216
+ // retVal.append(contentsOf: [
2217
+ // ("test_repeatedRequestsStress", test_repeatedRequestsStress),
2218
+ // ])
2219
+ // #endif
2109
2220
return retVal
2110
2221
}
2111
2222
0 commit comments