Skip to content

Commit

Permalink
Merge pull request #10 from rtckit/v0.3.1
Browse files Browse the repository at this point in the history
v0.3.1
  • Loading branch information
cdosoftei authored Jul 9, 2021
2 parents 5121628 + be75468 commit 54af835
Show file tree
Hide file tree
Showing 10 changed files with 749 additions and 26 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,7 @@ MIT, see [LICENSE file](LICENSE).

* [SIP Protocol Contributors/IETF Trust](https://www.ietf.org/standards/rfcs/)
* [PROTOS SIP Test Material](https://www.ee.oulu.fi/research/ouspg/PROTOS_Test-Suite_c07-sip) - Oulu University Secure Programming Group, Finland
* [lioneagle/sipparser Test Material](https://github.com/lioneagle/sipparser/blob/master/src/testdata/sip_msg.txt) (MIT license)

### Contributing

Expand Down
2 changes: 1 addition & 1 deletion composer.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "rtckit/sip",
"description": "Parser/Renderer for SIP protocol written in PHP",
"version": "0.3.0",
"version": "0.3.1",
"type": "library",
"keywords": [
"sip",
Expand Down
2 changes: 1 addition & 1 deletion examples/05-stream-parse.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@

$parser = new StreamParser;

$fp = fopen(__DIR__ . '/../tests/fixtures/stream.txt', 'r');
$fp = fopen(__DIR__ . '/../tests/fixtures/stream/generic.txt', 'r');

while (!feof($fp)) {
$bytes = fread($fp, 256);
Expand Down
2 changes: 1 addition & 1 deletion examples/99-crude-benchmark.php
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@
$runs = 0;
$stats = [];
$elapsed = 0;
$fp = fopen(__DIR__ . '/../tests/fixtures/stream.txt', 'r');
$fp = fopen(__DIR__ . '/../tests/fixtures/stream/generic.txt', 'r');

for ($i = 6; $i < $chunkExp; $i++) {
$chunkSize = pow(2, $i);
Expand Down
87 changes: 87 additions & 0 deletions src/Header/RAckHeader.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
<?php
/**
* RTCKit\SIP\RAckHeader Class
*/
declare(strict_types = 1);

namespace RTCKit\SIP\Header;

use RTCKit\SIP\Response;
use RTCKit\SIP\Exception\InvalidDuplicateHeader;
use RTCKit\SIP\Exception\InvalidHeaderLineException;
use RTCKit\SIP\Exception\InvalidHeaderValue;
use RTCKit\SIP\Exception\InvalidScalarValue;

/**
* RAck Header Class
*
* https://datatracker.ietf.org/doc/html/rfc3262#section-7.2
*/
class RAckHeader
{
/** @var int RSeq sequence number */
public int $rSequence;

/** @var int CSeq sequence number */
public int $cSequence;

/** @var string Original request method */
public string $method;

final public function __construct() {}

/**
* RAck header value parser
*
* @param list<string> $hbody Header body
* @throws InvalidDuplicateHeader
* @throws InvalidHeaderLineException
* @throws InvalidScalarValue
* @return RAckHeader
*/
public static function parse(array $hbody): RAckHeader
{
if (isset($hbody[1])) {
throw new InvalidDuplicateHeader('Cannot have more than one RAck header', Response::BAD_REQUEST);
}

$rack = preg_split('/\s+/', trim($hbody[0]), -1, PREG_SPLIT_NO_EMPTY);

if (!is_array($rack) || (count($rack) != 3)) {
throw new InvalidHeaderLineException('Invalid RAck header', Response::BAD_REQUEST);
}

$ret = new static;
$ret->rSequence = (int) $rack[0];

if (($ret->rSequence < 0) || ($ret->rSequence > ScalarHeader::MAX_VALUE)) {
throw new InvalidScalarValue('RAck provisional sequence number out of bounds', Response::BAD_REQUEST);
}

$ret->cSequence = (int) $rack[1];

if (($ret->cSequence < 0) || ($ret->cSequence > ScalarHeader::MAX_VALUE)) {
throw new InvalidScalarValue('RAck sequence number out of bounds', Response::BAD_REQUEST);
}

$ret->method = $rack[2];

return $ret;
}

/**
* RAck header value renderer
*
* @param string $hname Header field name
* @throws InvalidHeaderValue
* @return string
*/
public function render(string $hname): string
{
if (!isset($this->rSequence, $this->cSequence, $this->method[0])) {
throw new InvalidHeaderValue('Missing Sequence(s)/Method for RAck header field value');
}

return "{$hname}: {$this->rSequence} {$this->cSequence} {$this->method}\r\n";
}
}
26 changes: 26 additions & 0 deletions src/Message.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@
use RTCKit\SIP\Header\MultiValueHeader;
use RTCKit\SIP\Header\MultiValueWithParamsHeader;
use RTCKit\SIP\Header\NameAddrHeader;
use RTCKit\SIP\Header\RAckHeader;
use RTCKit\SIP\Header\RSeqHeader;
use RTCKit\SIP\Header\ScalarHeader;
use RTCKit\SIP\Header\SingleValueWithParamsHeader;
use RTCKit\SIP\Header\ViaHeader;
Expand Down Expand Up @@ -119,6 +121,10 @@ class Message
/* REFER header fields */
public Header $referTo;

/* PRACK header fields */
public RAckHeader $rAck;
public ScalarHeader $rSeq;

/** @var string Message body */
public string $body;

Expand Down Expand Up @@ -462,6 +468,18 @@ public static function parse(string $text, bool $ignoreBody = false): Message

continue 2;

/* https://datatracker.ietf.org/doc/html/rfc3262#section-7.2 */
case 'rack':
$msg->rAck = RAckHeader::parse($hbody);

continue 2;

/* https://datatracker.ietf.org/doc/html/rfc3262#section-7.1 */
case 'rseq':
$msg->rSeq = ScalarHeader::parse($hbody);

continue 2;

default:
$msg->extraHeaders[$hname] = Header::parse($hbody);

Expand Down Expand Up @@ -680,6 +698,14 @@ public function renderHeaders(bool $compact): string
$ret .= $this->referTo->render('Refer-To');
}

if (isset($this->rAck)) {
$ret .= $this->rAck->render('RAck');
}

if (isset($this->rSeq)) {
$ret .= $this->rSeq->render('RSeq');
}

foreach ($this->extraHeaders as $name => $header) {
$ret .= $header->render($name);
}
Expand Down
146 changes: 146 additions & 0 deletions tests/Header/RAckHeaderTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
<?php

declare(strict_types = 1);

namespace RTCKit\SIP\Header;

use RTCKit\SIP\Exception\InvalidDuplicateHeader;
use RTCKit\SIP\Exception\InvalidHeaderLineException;
use RTCKit\SIP\Exception\InvalidHeaderValue;
use RTCKit\SIP\Exception\InvalidScalarValue;
use RTCKit\SIP\Header\RAckHeader;

use PHPUnit\Framework\TestCase;

class RAckHeaderTest extends TestCase
{
public function testShouldParseWellFormedValue()
{
$rack = RAckHeader::parse(['42 7 METHOD']);

$this->assertNotNull($rack);
$this->assertInstanceOf(RAckHeader::class, $rack);
$this->assertEquals(42, $rack->rSequence);
$this->assertEquals(7, $rack->cSequence);
$this->assertEquals('METHOD', $rack->method);
}

public function testShouldNotParseMultiValue()
{
$this->expectException(InvalidDuplicateHeader::class);
RAckHeader::parse([
'63 7 METHOD',
'41 9 INVITE',
]);
}

public function testShouldNotParseNondelimitedComponents()
{
$this->expectException(InvalidHeaderLineException::class);
RAckHeader::parse(['427METHOD']);
}

public function testShouldNotParseNondelimitedComponents2()
{
$this->expectException(InvalidHeaderLineException::class);
RAckHeader::parse(['42 7METHOD']);
}

public function testShouldNotParseNegativeProvisionalSequence()
{
$this->expectException(InvalidScalarValue::class);
RAckHeader::parse(['-7 1 METHOD']);
}

public function testShouldNotParseNegativeSequence()
{
$this->expectException(InvalidScalarValue::class);
RAckHeader::parse(['7 -1 METHOD']);
}

public function testShouldNotParseOutOfBoundsProvisionalSequence()
{
$this->expectException(InvalidScalarValue::class);
RAckHeader::parse(['42949672950 1 METHOD']);
}

public function testShouldNotParseOutOfBoundsSequence()
{
$this->expectException(InvalidScalarValue::class);
RAckHeader::parse(['1 42949672950 METHOD']);
}

public function testShouldNotParseMissingMethod()
{
$this->expectException(InvalidHeaderLineException::class);
RAckHeader::parse(['7 ']);
}

public function testShouldNotParseMissingMethod2()
{
$this->expectException(InvalidHeaderLineException::class);
RAckHeader::parse(['42 1 ']);
}

public function testShouldRenderWellFormedValue()
{
$rack = new RAckHeader;
$rack->rSequence = 42;
$rack->cSequence = 7;
$rack->method = 'METHOD';

$rendered = $rack->render('RAck');

$this->assertNotNull($rendered);
$this->assertIsString($rendered);
$this->assertEquals("RAck: 42 7 METHOD\r\n", $rendered);
}

public function testShouldNotRenderMissingProvisionalSequence()
{
$rack = new RAckHeader;
$rack->method = 'METHOD';

$this->expectException(InvalidHeaderValue::class);
$rack->render('RAck');
}

public function testShouldNotRenderMissingProvisionalSequence2()
{
$rack = new RAckHeader;
$rack->cSequence = 7;
$rack->method = 'METHOD';

$this->expectException(InvalidHeaderValue::class);
$rack->render('RAck');
}

public function testShouldNotRenderMissingSequence()
{
$rack = new RAckHeader;
$rack->method = 'METHOD';

$this->expectException(InvalidHeaderValue::class);
$rack->render('RAck');
}

public function testShouldNotRenderMissingSequence2()
{
$rack = new RAckHeader;
$rack->rSequence = 42;
$rack->method = 'METHOD';

$this->expectException(InvalidHeaderValue::class);
$rack->render('RAck');
}

public function testShouldNotRenderMissingMethod()
{
$rack = new RAckHeader;
$rack->rSequence = 42;
$rack->cSequence = 7;

$this->expectException(InvalidHeaderValue::class);
$rack->render('RAck');
}
}
Loading

0 comments on commit 54af835

Please sign in to comment.