15
15
*/
16
16
@testable import GRPC
17
17
import NIOCore
18
+ import NIOEmbedded
18
19
import NIOHTTP2
19
20
import XCTest
20
21
@@ -249,35 +250,23 @@ class GRPCPingHandlerTests: GRPCTestCase {
249
250
pingData: HTTP2PingData ( withInteger: 1 ) ,
250
251
ack: false
251
252
)
252
- XCTAssertEqual (
253
- response,
254
- . reply( HTTP2Frame . FramePayload. ping ( HTTP2PingData ( withInteger: 1 ) , ack: true ) )
255
- )
253
+ XCTAssertEqual ( response, . ack)
256
254
257
255
// Received another ping, response should be a pong (ping strikes not in effect)
258
256
response = self . pingHandler. read ( pingData: HTTP2PingData ( withInteger: 1 ) , ack: false )
259
- XCTAssertEqual (
260
- response,
261
- . reply( HTTP2Frame . FramePayload. ping ( HTTP2PingData ( withInteger: 1 ) , ack: true ) )
262
- )
257
+ XCTAssertEqual ( response, . ack)
263
258
264
259
// Received another ping, response should be a pong (ping strikes not in effect)
265
260
response = self . pingHandler. read ( pingData: HTTP2PingData ( withInteger: 1 ) , ack: false )
266
- XCTAssertEqual (
267
- response,
268
- . reply( HTTP2Frame . FramePayload. ping ( HTTP2PingData ( withInteger: 1 ) , ack: true ) )
269
- )
261
+ XCTAssertEqual ( response, . ack)
270
262
}
271
263
272
264
func testPingWithoutDataResultsInPongForClient( ) {
273
265
// Don't allow _sending_ pings when no calls are active (receiving pings should be tolerated).
274
266
self . setupPingHandler ( permitWithoutCalls: false )
275
267
276
268
let action = self . pingHandler. read ( pingData: HTTP2PingData ( withInteger: 1 ) , ack: false )
277
- XCTAssertEqual (
278
- action,
279
- . reply( HTTP2Frame . FramePayload. ping ( HTTP2PingData ( withInteger: 1 ) , ack: true ) )
280
- )
269
+ XCTAssertEqual ( action, . ack)
281
270
}
282
271
283
272
func testPingWithoutDataResultsInPongForServer( ) {
@@ -291,10 +280,7 @@ class GRPCPingHandlerTests: GRPCTestCase {
291
280
)
292
281
293
282
let action = self . pingHandler. read ( pingData: HTTP2PingData ( withInteger: 1 ) , ack: false )
294
- XCTAssertEqual (
295
- action,
296
- . reply( HTTP2Frame . FramePayload. ping ( HTTP2PingData ( withInteger: 1 ) , ack: true ) )
297
- )
283
+ XCTAssertEqual ( action, . ack)
298
284
}
299
285
300
286
func testPingStrikesOnServer( ) {
@@ -312,10 +298,7 @@ class GRPCPingHandlerTests: GRPCTestCase {
312
298
pingData: HTTP2PingData ( withInteger: 1 ) ,
313
299
ack: false
314
300
)
315
- XCTAssertEqual (
316
- response,
317
- . reply( HTTP2Frame . FramePayload. ping ( HTTP2PingData ( withInteger: 1 ) , ack: true ) )
318
- )
301
+ XCTAssertEqual ( response, . ack)
319
302
320
303
// Received another ping, which is invalid (ping strike), response should be no action
321
304
response = self . pingHandler. read ( pingData: HTTP2PingData ( withInteger: 1 ) , ack: false )
@@ -326,10 +309,7 @@ class GRPCPingHandlerTests: GRPCTestCase {
326
309
327
310
// Received another ping, which is valid now, response should be a pong
328
311
response = self . pingHandler. read ( pingData: HTTP2PingData ( withInteger: 1 ) , ack: false )
329
- XCTAssertEqual (
330
- response,
331
- . reply( HTTP2Frame . FramePayload. ping ( HTTP2PingData ( withInteger: 1 ) , ack: true ) )
332
- )
312
+ XCTAssertEqual ( response, . ack)
333
313
334
314
// Received another ping, which is invalid (ping strike), response should be no action
335
315
response = self . pingHandler. read ( pingData: HTTP2PingData ( withInteger: 1 ) , ack: false )
@@ -381,6 +361,8 @@ extension PingHandler.Action: Equatable {
381
361
switch ( lhs, rhs) {
382
362
case ( . none, . none) :
383
363
return true
364
+ case ( . ack, . ack) :
365
+ return true
384
366
case ( let . schedulePing( lhsDelay, lhsTimeout) , let . schedulePing( rhsDelay, rhsTimeout) ) :
385
367
return lhsDelay == rhsDelay && lhsTimeout == rhsTimeout
386
368
case ( . cancelScheduledTimeout, . cancelScheduledTimeout) :
@@ -401,3 +383,89 @@ extension PingHandler.Action: Equatable {
401
383
}
402
384
}
403
385
}
386
+
387
+ extension GRPCPingHandlerTests {
388
+ func testSingleAckIsEmittedOnPing( ) throws {
389
+ let client = EmbeddedChannel ( )
390
+ let _ = try client. configureHTTP2Pipeline ( mode: . client) { _ in
391
+ fatalError ( " Unexpected inbound stream " )
392
+ } . wait ( )
393
+
394
+ let server = EmbeddedChannel ( )
395
+ let serverMux = try server. configureHTTP2Pipeline ( mode: . server) { _ in
396
+ fatalError ( " Unexpected inbound stream " )
397
+ } . wait ( )
398
+
399
+ let idleHandler = GRPCIdleHandler (
400
+ idleTimeout: . minutes( 5 ) ,
401
+ keepalive: . init( ) ,
402
+ logger: self . serverLogger
403
+ )
404
+ try server. pipeline. syncOperations. addHandler ( idleHandler, position: . before( serverMux) )
405
+ try server. connect ( to: . init( unixDomainSocketPath: " /ignored " ) ) . wait ( )
406
+ try client. connect ( to: . init( unixDomainSocketPath: " /ignored " ) ) . wait ( )
407
+
408
+ func interact( client: EmbeddedChannel , server: EmbeddedChannel ) throws {
409
+ var didRead = true
410
+ while didRead {
411
+ didRead = false
412
+
413
+ if let data = try client. readOutbound ( as: ByteBuffer . self) {
414
+ didRead = true
415
+ try server. writeInbound ( data)
416
+ }
417
+
418
+ if let data = try server. readOutbound ( as: ByteBuffer . self) {
419
+ didRead = true
420
+ try client. writeInbound ( data)
421
+ }
422
+ }
423
+ }
424
+
425
+ try interact ( client: client, server: server)
426
+
427
+ // Settings.
428
+ let f1 = try XCTUnwrap ( client. readInbound ( as: HTTP2Frame . self) )
429
+ f1. payload. assertSettings ( ack: false )
430
+
431
+ // Settings ack.
432
+ let f2 = try XCTUnwrap ( client. readInbound ( as: HTTP2Frame . self) )
433
+ f2. payload. assertSettings ( ack: true )
434
+
435
+ // Send a ping.
436
+ let ping = HTTP2Frame ( streamID: . rootStream, payload: . ping( . init( withInteger: 42 ) , ack: false ) )
437
+ try client. writeOutbound ( ping)
438
+ try interact ( client: client, server: server)
439
+
440
+ // Ping ack.
441
+ let f3 = try XCTUnwrap ( client. readInbound ( as: HTTP2Frame . self) )
442
+ f3. payload. assertPing ( ack: true )
443
+
444
+ XCTAssertNil ( try client. readInbound ( as: HTTP2Frame . self) )
445
+ }
446
+ }
447
+
448
+ extension HTTP2Frame . FramePayload {
449
+ func assertSettings( ack: Bool , file: StaticString = #file, line: UInt = #line) {
450
+ switch self {
451
+ case let . settings( settings) :
452
+ switch settings {
453
+ case . ack:
454
+ XCTAssertTrue ( ack, file: file, line: line)
455
+ case . settings:
456
+ XCTAssertFalse ( ack, file: file, line: line)
457
+ }
458
+ default :
459
+ XCTFail ( " Expected .settings got \( self ) " , file: file, line: line)
460
+ }
461
+ }
462
+
463
+ func assertPing( ack: Bool , file: StaticString = #file, line: UInt = #line) {
464
+ switch self {
465
+ case let . ping( _, ack: pingAck) :
466
+ XCTAssertEqual ( pingAck, ack, file: file, line: line)
467
+ default :
468
+ XCTFail ( " Expected .ping got \( self ) " , file: file, line: line)
469
+ }
470
+ }
471
+ }
0 commit comments