|
25 | 25 | use Bunny\Protocol\MethodTxCommitOkFrame; |
26 | 26 | use Bunny\Protocol\MethodTxRollbackOkFrame; |
27 | 27 | use Bunny\Protocol\MethodTxSelectOkFrame; |
28 | | -use Evenement\EventEmitterInterface; |
29 | 28 | use Evenement\EventEmitterTrait; |
30 | 29 | use LogicException; |
31 | 30 | use React\Promise\Deferred; |
32 | 31 | use React\Promise\PromiseInterface; |
33 | 32 | use SplQueue; |
| 33 | +use Throwable; |
34 | 34 | use function React\Async\async; |
35 | 35 | use function React\Async\await; |
36 | 36 | use function array_key_exists; |
|
47 | 47 | * @author Jakub Kulhan <jakub.kulhan@gmail.com> |
48 | 48 | * @final Will be marked final in a future major release |
49 | 49 | */ |
50 | | -class Channel implements ChannelInterface, EventEmitterInterface |
| 50 | +class Channel implements ChannelInterface |
51 | 51 | { |
52 | 52 | use EventEmitterTrait; |
53 | 53 | use ChannelMethods { |
@@ -438,141 +438,145 @@ private function enterConfirmMode(?callable $callback = null): void |
438 | 438 | */ |
439 | 439 | public function onFrameReceived(AbstractFrame $frame): void |
440 | 440 | { |
441 | | - if ($this->state === ChannelState::Error) { |
442 | | - throw new ChannelException('Channel in error state.'); |
443 | | - } |
444 | | - |
445 | | - if ($this->state === ChannelState::Closed) { |
446 | | - throw new ChannelException(sprintf('Received frame #%d on closed channel #%d.', $frame->type, $this->channelId)); |
447 | | - } |
448 | | - |
449 | | - if ($frame instanceof MethodFrame) { |
450 | | - if ($this->state === ChannelState::Closing && !($frame instanceof MethodChannelCloseOkFrame)) { |
451 | | - // drop frames in closing state |
452 | | - return; |
| 441 | + try { |
| 442 | + if ($this->state === ChannelState::Error) { |
| 443 | + throw new ChannelException('Channel in error state.'); |
453 | 444 | } |
454 | 445 |
|
455 | | - if ($this->state !== ChannelState::Ready && !($frame instanceof MethodChannelCloseOkFrame)) { |
456 | | - $currentState = $this->state; |
457 | | - $this->state = ChannelState::Error; |
| 446 | + if ($this->state === ChannelState::Closed) { |
| 447 | + throw new ChannelException(sprintf('Received frame #%d on closed channel #%d.', $frame->type, $this->channelId)); |
| 448 | + } |
458 | 449 |
|
459 | | - if ($currentState === ChannelState::AwaitingHeader) { |
460 | | - $msg = 'Got method frame, expected header frame.'; |
461 | | - } elseif ($currentState === ChannelState::AwaitingBody) { |
462 | | - $msg = 'Got method frame, expected body frame.'; |
463 | | - } else { |
464 | | - throw new LogicException('Unhandled channel state.'); |
| 450 | + if ($frame instanceof MethodFrame) { |
| 451 | + if ($this->state === ChannelState::Closing && !($frame instanceof MethodChannelCloseOkFrame)) { |
| 452 | + // drop frames in closing state |
| 453 | + return; |
465 | 454 | } |
466 | 455 |
|
467 | | - $this->connection->disconnect(Constants::STATUS_UNEXPECTED_FRAME, $msg); |
| 456 | + if ($this->state !== ChannelState::Ready && !($frame instanceof MethodChannelCloseOkFrame)) { |
| 457 | + $currentState = $this->state; |
| 458 | + $this->state = ChannelState::Error; |
468 | 459 |
|
469 | | - throw new ChannelException('Unexpected frame: ' . $msg); |
470 | | - } |
| 460 | + if ($currentState === ChannelState::AwaitingHeader) { |
| 461 | + $msg = 'Got method frame, expected header frame.'; |
| 462 | + } elseif ($currentState === ChannelState::AwaitingBody) { |
| 463 | + $msg = 'Got method frame, expected body frame.'; |
| 464 | + } else { |
| 465 | + throw new LogicException('Unhandled channel state.'); |
| 466 | + } |
471 | 467 |
|
472 | | - if ($frame instanceof MethodChannelCloseOkFrame) { |
473 | | - $this->state = ChannelState::Closed; |
| 468 | + $this->connection->disconnect(Constants::STATUS_UNEXPECTED_FRAME, $msg); |
474 | 469 |
|
475 | | - if ($this->closeDeferred !== null) { |
476 | | - $this->closeDeferred->resolve($this->channelId); |
| 470 | + throw new ChannelException('Unexpected frame: ' . $msg); |
477 | 471 | } |
478 | 472 |
|
479 | | -// // break reference cycle, must be called after resolving promise |
480 | | -// $this->client = null; |
481 | | - // break consumers' reference cycle |
482 | | - $this->consumeConcurrency = []; |
483 | | - $this->consumeConcurrent = []; |
484 | | - $this->deliverCallbacks = []; |
485 | | - $this->deliveryQueue = []; |
486 | | - } elseif ($frame instanceof MethodBasicReturnFrame) { |
487 | | - $this->returnFrame = $frame; |
488 | | - $this->state = ChannelState::AwaitingHeader; |
489 | | - } elseif ($frame instanceof MethodBasicDeliverFrame) { |
490 | | - $this->deliverFrame = $frame; |
491 | | - $this->state = ChannelState::AwaitingHeader; |
492 | | - } elseif ($frame instanceof MethodBasicAckFrame) { |
493 | | - foreach ($this->ackCallbacks as $callback) { |
494 | | - $callback($frame); |
| 473 | + if ($frame instanceof MethodChannelCloseOkFrame) { |
| 474 | + $this->state = ChannelState::Closed; |
| 475 | + |
| 476 | + if ($this->closeDeferred !== null) { |
| 477 | + $this->closeDeferred->resolve($this->channelId); |
| 478 | + } |
| 479 | + |
| 480 | + // break reference cycle, must be called after resolving promise |
| 481 | + // $this->client = null; |
| 482 | + // break consumers' reference cycle |
| 483 | + $this->consumeConcurrency = []; |
| 484 | + $this->consumeConcurrent = []; |
| 485 | + $this->deliverCallbacks = []; |
| 486 | + $this->deliveryQueue = []; |
| 487 | + } elseif ($frame instanceof MethodBasicReturnFrame) { |
| 488 | + $this->returnFrame = $frame; |
| 489 | + $this->state = ChannelState::AwaitingHeader; |
| 490 | + } elseif ($frame instanceof MethodBasicDeliverFrame) { |
| 491 | + $this->deliverFrame = $frame; |
| 492 | + $this->state = ChannelState::AwaitingHeader; |
| 493 | + } elseif ($frame instanceof MethodBasicAckFrame) { |
| 494 | + foreach ($this->ackCallbacks as $callback) { |
| 495 | + $callback($frame); |
| 496 | + } |
| 497 | + } elseif ($frame instanceof MethodBasicNackFrame) { |
| 498 | + foreach ($this->ackCallbacks as $callback) { |
| 499 | + $callback($frame); |
| 500 | + } |
| 501 | + } elseif ($frame instanceof MethodChannelCloseFrame) { |
| 502 | + throw new ChannelException('Channel closed by server: ' . $frame->replyText, $frame->replyCode); |
| 503 | + } else { |
| 504 | + throw new ChannelException('Unhandled method frame ' . $frame::class . '.'); |
495 | 505 | } |
496 | | - } elseif ($frame instanceof MethodBasicNackFrame) { |
497 | | - foreach ($this->ackCallbacks as $callback) { |
498 | | - $callback($frame); |
| 506 | + } elseif ($frame instanceof ContentHeaderFrame) { |
| 507 | + if ($this->state === ChannelState::Closing) { |
| 508 | + // drop frames in closing state |
| 509 | + return; |
499 | 510 | } |
500 | | - } elseif ($frame instanceof MethodChannelCloseFrame) { |
501 | | - throw new ChannelException('Channel closed by server: ' . $frame->replyText, $frame->replyCode); |
502 | | - } else { |
503 | | - throw new ChannelException('Unhandled method frame ' . $frame::class . '.'); |
504 | | - } |
505 | | - } elseif ($frame instanceof ContentHeaderFrame) { |
506 | | - if ($this->state === ChannelState::Closing) { |
507 | | - // drop frames in closing state |
508 | | - return; |
509 | | - } |
510 | 511 |
|
511 | | - if ($this->state !== ChannelState::AwaitingHeader) { |
512 | | - $currentState = $this->state; |
513 | | - $this->state = ChannelState::Error; |
| 512 | + if ($this->state !== ChannelState::AwaitingHeader) { |
| 513 | + $currentState = $this->state; |
| 514 | + $this->state = ChannelState::Error; |
| 515 | + |
| 516 | + if ($currentState === ChannelState::Ready) { |
| 517 | + $msg = 'Got header frame, expected method frame.'; |
| 518 | + } elseif ($currentState === ChannelState::AwaitingBody) { |
| 519 | + $msg = 'Got header frame, expected content frame.'; |
| 520 | + } else { |
| 521 | + throw new LogicException('Unhandled channel state.'); |
| 522 | + } |
514 | 523 |
|
515 | | - if ($currentState === ChannelState::Ready) { |
516 | | - $msg = 'Got header frame, expected method frame.'; |
517 | | - } elseif ($currentState === ChannelState::AwaitingBody) { |
518 | | - $msg = 'Got header frame, expected content frame.'; |
519 | | - } else { |
520 | | - throw new LogicException('Unhandled channel state.'); |
| 524 | + $this->connection->disconnect(Constants::STATUS_UNEXPECTED_FRAME, $msg); |
| 525 | + |
| 526 | + throw new ChannelException('Unexpected frame: ' . $msg); |
521 | 527 | } |
522 | 528 |
|
523 | | - $this->connection->disconnect(Constants::STATUS_UNEXPECTED_FRAME, $msg); |
| 529 | + $this->headerFrame = $frame; |
| 530 | + $this->bodySizeRemaining = $frame->bodySize; |
524 | 531 |
|
525 | | - throw new ChannelException('Unexpected frame: ' . $msg); |
526 | | - } |
| 532 | + if ($this->bodySizeRemaining > 0) { |
| 533 | + $this->state = ChannelState::AwaitingBody; |
| 534 | + } else { |
| 535 | + $this->state = ChannelState::Ready; |
| 536 | + $this->onBodyComplete(); |
| 537 | + } |
| 538 | + } elseif ($frame instanceof ContentBodyFrame) { |
| 539 | + if ($this->state === ChannelState::Closing) { |
| 540 | + // drop frames in closing state |
| 541 | + return; |
| 542 | + } |
527 | 543 |
|
528 | | - $this->headerFrame = $frame; |
529 | | - $this->bodySizeRemaining = $frame->bodySize; |
| 544 | + if ($this->state !== ChannelState::AwaitingBody) { |
| 545 | + $currentState = $this->state; |
| 546 | + $this->state = ChannelState::Error; |
530 | 547 |
|
531 | | - if ($this->bodySizeRemaining > 0) { |
532 | | - $this->state = ChannelState::AwaitingBody; |
533 | | - } else { |
534 | | - $this->state = ChannelState::Ready; |
535 | | - $this->onBodyComplete(); |
536 | | - } |
537 | | - } elseif ($frame instanceof ContentBodyFrame) { |
538 | | - if ($this->state === ChannelState::Closing) { |
539 | | - // drop frames in closing state |
540 | | - return; |
541 | | - } |
| 548 | + if ($currentState === ChannelState::Ready) { |
| 549 | + $msg = 'Got body frame, expected method frame.'; |
| 550 | + } elseif ($currentState === ChannelState::AwaitingHeader) { |
| 551 | + $msg = 'Got body frame, expected header frame.'; |
| 552 | + } else { |
| 553 | + throw new LogicException('Unhandled channel state.'); |
| 554 | + } |
542 | 555 |
|
543 | | - if ($this->state !== ChannelState::AwaitingBody) { |
544 | | - $currentState = $this->state; |
545 | | - $this->state = ChannelState::Error; |
| 556 | + $this->connection->disconnect(Constants::STATUS_UNEXPECTED_FRAME, $msg); |
546 | 557 |
|
547 | | - if ($currentState === ChannelState::Ready) { |
548 | | - $msg = 'Got body frame, expected method frame.'; |
549 | | - } elseif ($currentState === ChannelState::AwaitingHeader) { |
550 | | - $msg = 'Got body frame, expected header frame.'; |
551 | | - } else { |
552 | | - throw new LogicException('Unhandled channel state.'); |
| 558 | + throw new ChannelException('Unexpected frame: ' . $msg); |
553 | 559 | } |
554 | 560 |
|
555 | | - $this->connection->disconnect(Constants::STATUS_UNEXPECTED_FRAME, $msg); |
556 | | - |
557 | | - throw new ChannelException('Unexpected frame: ' . $msg); |
558 | | - } |
| 561 | + $this->bodyBuffer->append($frame->payload); |
| 562 | + $this->bodySizeRemaining -= $frame->payloadSize; |
559 | 563 |
|
560 | | - $this->bodyBuffer->append($frame->payload); |
561 | | - $this->bodySizeRemaining -= $frame->payloadSize; |
| 564 | + if ($this->bodySizeRemaining < 0) { |
| 565 | + $this->state = ChannelState::Error; |
| 566 | + $this->connection->disconnect(Constants::STATUS_SYNTAX_ERROR, 'Body overflow, received ' . (-$this->bodySizeRemaining) . ' more bytes.'); |
| 567 | + } elseif ($this->bodySizeRemaining === 0) { |
| 568 | + $this->state = ChannelState::Ready; |
| 569 | + $this->onBodyComplete(); |
| 570 | + } |
| 571 | + } elseif ($frame instanceof HeartbeatFrame) { |
| 572 | + $this->connection->disconnect(Constants::STATUS_UNEXPECTED_FRAME, 'Got heartbeat on non-zero channel.'); |
562 | 573 |
|
563 | | - if ($this->bodySizeRemaining < 0) { |
564 | | - $this->state = ChannelState::Error; |
565 | | - $this->connection->disconnect(Constants::STATUS_SYNTAX_ERROR, 'Body overflow, received ' . (-$this->bodySizeRemaining) . ' more bytes.'); |
566 | | - } elseif ($this->bodySizeRemaining === 0) { |
567 | | - $this->state = ChannelState::Ready; |
568 | | - $this->onBodyComplete(); |
| 574 | + throw new ChannelException('Unexpected heartbeat frame.'); |
| 575 | + } else { |
| 576 | + throw new ChannelException('Unhandled frame ' . $frame::class . '.'); |
569 | 577 | } |
570 | | - } elseif ($frame instanceof HeartbeatFrame) { |
571 | | - $this->connection->disconnect(Constants::STATUS_UNEXPECTED_FRAME, 'Got heartbeat on non-zero channel.'); |
572 | | - |
573 | | - throw new ChannelException('Unexpected heartbeat frame.'); |
574 | | - } else { |
575 | | - throw new ChannelException('Unhandled frame ' . $frame::class . '.'); |
| 578 | + } catch (Throwable $e) { |
| 579 | + $this->emit('error', [$e]); |
576 | 580 | } |
577 | 581 | } |
578 | 582 |
|
|
0 commit comments