@@ -519,21 +519,122 @@ class TestURLSession: LoopbackServerTest {
519
519
waitForExpectations ( timeout: 30 )
520
520
}
521
521
522
- func test_timeoutInterval ( ) {
522
+ func test_httpTimeout ( ) {
523
523
let config = URLSessionConfiguration . default
524
524
config. timeoutIntervalForRequest = 10
525
- let urlString = " http://127.0.0.1:-1 /Peru "
525
+ let urlString = " http://127.0.0.1: \( TestURLSession . serverPort ) /Peru "
526
526
let session = URLSession ( configuration: config, delegate: nil , delegateQueue: nil )
527
527
let expect = expectation ( description: " GET \( urlString) : will timeout " )
528
- var req = URLRequest ( url: URL ( string: " http://127.0.0.1:-1/Peru " ) !)
528
+ var req = URLRequest ( url: URL ( string: urlString) !)
529
+ req. setValue ( " 3 " , forHTTPHeaderField: " x-pause " )
529
530
req. timeoutInterval = 1
530
531
let task = session. dataTask ( with: req) { ( data, _, error) -> Void in
531
532
defer { expect. fulfill ( ) }
532
- XCTAssertNotNil ( error)
533
+ XCTAssertEqual ( ( error as? URLError ) ? . code , . timedOut , " Task should fail with URLError.timedOut error " )
533
534
}
534
535
task. resume ( )
536
+ waitForExpectations ( timeout: 30 )
537
+ }
538
+
539
+ func test_connectTimeout( ) {
540
+ // Reconfigure http server for this specific scenario:
541
+ // a slow request keeps web server busy, while other
542
+ // request times out on connection attempt.
543
+ Self . stopServer ( )
544
+ Self . options = Options ( serverBacklog: 1 , isAsynchronous: false )
545
+ Self . startServer ( )
546
+
547
+ let config = URLSessionConfiguration . default
548
+ let slowUrlString = " http://127.0.0.1: \( TestURLSession . serverPort) /Peru "
549
+ let fastUrlString = " http://127.0.0.1: \( TestURLSession . serverPort) /Italy "
550
+ let session = URLSession ( configuration: config, delegate: nil , delegateQueue: nil )
551
+ let slowReqExpect = expectation ( description: " GET \( slowUrlString) : will complete " )
552
+ let fastReqExpect = expectation ( description: " GET \( fastUrlString) : will timeout " )
553
+
554
+ var slowReq = URLRequest ( url: URL ( string: slowUrlString) !)
555
+ slowReq. setValue ( " 3 " , forHTTPHeaderField: " x-pause " )
556
+
557
+ var fastReq = URLRequest ( url: URL ( string: fastUrlString) !)
558
+ fastReq. timeoutInterval = 1
559
+
560
+ let slowTask = session. dataTask ( with: slowReq) { ( data, _, error) -> Void in
561
+ slowReqExpect. fulfill ( )
562
+ }
563
+ let fastTask = session. dataTask ( with: fastReq) { ( data, _, error) -> Void in
564
+ defer { fastReqExpect. fulfill ( ) }
565
+ XCTAssertEqual ( ( error as? URLError ) ? . code, . timedOut, " Task should fail with URLError.timedOut error " )
566
+ }
567
+ slowTask. resume ( )
568
+ Thread . sleep ( forTimeInterval: 0.1 ) // Give slow task some time to start
569
+ fastTask. resume ( )
535
570
536
571
waitForExpectations ( timeout: 30 )
572
+
573
+ // Reconfigure http server back to default settings
574
+ Self . stopServer ( )
575
+ Self . options = . default
576
+ Self . startServer ( )
577
+ }
578
+
579
+ func test_repeatedRequestsStress( ) throws {
580
+ // TODO: try disabling curl connection cache to force socket close early. Or create several url sessions (they have cleanup in deinit)
581
+
582
+ let config = URLSessionConfiguration . default
583
+ let urlString = " http://127.0.0.1: \( TestURLSession . serverPort) /Peru "
584
+ let session = URLSession ( configuration: config, delegate: nil , delegateQueue: nil )
585
+ let req = URLRequest ( url: URL ( string: urlString) !)
586
+
587
+ var requestsLeft = 3000
588
+ let expect = expectation ( description: " \( requestsLeft) x GET \( urlString) " )
589
+
590
+ func doRequests( completion: @escaping ( ) -> Void ) {
591
+ // We only care about completion of one of the tasks,
592
+ // so we could move to next cycle.
593
+ // Some overlapping would happen and that's what we
594
+ // want actually to provoke issue with socket reuse
595
+ // on Windows.
596
+ let task = session. dataTask ( with: req) { ( _, _, _) -> Void in
597
+ }
598
+ task. resume ( )
599
+ let task2 = session. dataTask ( with: req) { ( _, _, _) -> Void in
600
+ }
601
+ task2. resume ( )
602
+ let task3 = session. dataTask ( with: req) { ( _, _, _) -> Void in
603
+ completion ( )
604
+ }
605
+ task3. resume ( )
606
+ }
607
+
608
+ func checkCountAndRunNext( ) {
609
+ guard requestsLeft > 0 else {
610
+ expect. fulfill ( )
611
+ return
612
+ }
613
+ requestsLeft -= 1
614
+ doRequests ( completion: checkCountAndRunNext)
615
+ }
616
+
617
+ checkCountAndRunNext ( )
618
+
619
+ waitForExpectations ( timeout: 30 )
620
+ }
621
+
622
+ func test_largePost( ) throws {
623
+ let session = URLSession ( configuration: URLSessionConfiguration . default)
624
+ var dataTask : URLSessionDataTask ? = nil
625
+
626
+ let data = Data ( ( 0 ..< 131076 ) . map { _ in UInt8 . random ( in: UInt8 . min ... UInt8 . max) } )
627
+ var req = URLRequest ( url: URL ( string: " http://127.0.0.1: \( TestURLSession . serverPort) /POST " ) !)
628
+ req. httpMethod = " POST "
629
+ req. httpBody = data
630
+
631
+ let e = expectation ( description: " POST completed " )
632
+ dataTask = session. uploadTask ( with: req, from: data) { data, response, error in
633
+ e. fulfill ( )
634
+ }
635
+ dataTask? . resume ( )
636
+
637
+ waitForExpectations ( timeout: 5 )
537
638
}
538
639
539
640
func test_httpRedirectionWithCode300( ) throws {
@@ -2130,7 +2231,8 @@ class TestURLSession: LoopbackServerTest {
2130
2231
( " test_taskTimeout " , test_taskTimeout) ,
2131
2232
( " test_verifyRequestHeaders " , test_verifyRequestHeaders) ,
2132
2233
( " test_verifyHttpAdditionalHeaders " , test_verifyHttpAdditionalHeaders) ,
2133
- ( " test_timeoutInterval " , test_timeoutInterval) ,
2234
+ ( " test_httpTimeout " , test_httpTimeout) ,
2235
+ ( " test_connectTimeout " , test_connectTimeout) ,
2134
2236
( " test_httpRedirectionWithCode300 " , test_httpRedirectionWithCode300) ,
2135
2237
( " test_httpRedirectionWithCode301_302 " , test_httpRedirectionWithCode301_302) ,
2136
2238
( " test_httpRedirectionWithCode303 " , test_httpRedirectionWithCode303) ,
@@ -2181,6 +2283,7 @@ class TestURLSession: LoopbackServerTest {
2181
2283
/* ⚠️ */ testExpectedToFail ( test_noDoubleCallbackWhenCancellingAndProtocolFailsFast, " This test crashes nondeterministically: https://bugs.swift.org/browse/SR-11310 " ) ) ,
2182
2284
/* ⚠️ */ ( " test_cancelledTasksCannotBeResumed " , testExpectedToFail ( test_cancelledTasksCannotBeResumed, " Breaks on Ubuntu 18.04 " ) ) ,
2183
2285
]
2286
+ #if NS_FOUNDATION_ALLOWS_TESTABLE_IMPORT
2184
2287
if #available( macOS 12 . 0 , * ) {
2185
2288
retVal. append ( contentsOf: [
2186
2289
( " test_webSocket " , asyncTest ( test_webSocket) ) ,
@@ -2189,6 +2292,14 @@ class TestURLSession: LoopbackServerTest {
2189
2292
( " test_webSocketSemiAbruptClose " , asyncTest ( test_webSocketSemiAbruptClose) ) ,
2190
2293
] )
2191
2294
}
2295
+ #endif
2296
+ // This is heavy test and it could time out in CI environment giving false negative result.
2297
+ // Uncomment to use for local URLSession stability testing.
2298
+ // #if os(Windows)
2299
+ // retVal.append(contentsOf: [
2300
+ // ("test_repeatedRequestsStress", test_repeatedRequestsStress),
2301
+ // ])
2302
+ // #endif
2192
2303
return retVal
2193
2304
}
2194
2305
0 commit comments