App-OpenHAP

 view release on metacpan or  search on metacpan

t/protocol/http.t  view on Meta::CPAN

	my $custom = Protocol::HAP::HTTP::build_response(
		status      => 470,
		status_text => 'Connection Authorization Required',
	);
	like( $custom, qr{^HTTP/1\.1 470 Connection Authorization Required\r\n},
		'a caller can name a reason the codec does not know' );
	like( Protocol::HAP::HTTP::build_response( status => 470 ),
		qr{470 Unknown}, 'and an unknown code alone reads as Unknown' );

	# The header order is stable, so a conformance test can compare
	# a response byte for byte
	my %args = (
		status  => 200,
		headers => { Zulu => 1, Alpha => 2, Mike => 3 },
	);
	is( Protocol::HAP::HTTP::build_response(%args),
		Protocol::HAP::HTTP::build_response(%args),
		'the same arguments give the same bytes' );
	like(
		Protocol::HAP::HTTP::build_response(%args),
		qr{Alpha: 2\r\nContent-Length: 0\r\nMike: 3\r\nZulu: 1},
		'the headers are sorted'
	);
};

subtest 'status_text' => sub {
	is( Protocol::HAP::HTTP::status_text(200), 'OK', 'the text of 200' );
	is( Protocol::HAP::HTTP::status_text(404), 'Not Found', 'the text of 404' );
	is( Protocol::HAP::HTTP::status_text(413),
		'Content Too Large', 'the text of 413' );
	is( Protocol::HAP::HTTP::status_text(999), 'Unknown', 'an unknown code' );
};

# A stream socket gives a reader whatever arrived, which is not a
# message. Everything below is the framing that a server needs to not
# drop or mangle a request.
subtest 'framing: a message split across reads' => sub {
	my $whole =
	      "POST /characteristics HTTP/1.1\r\n"
	    . "Content-Length: 13\r\n"
	    . "\r\n"
	    . '{"test":true}';

	# One byte at a time. Only the last one completes the message.
	my $buffer = '';
	my @complete;
	for my $byte ( split //, $whole ) {
		$buffer .= $byte;
		push @complete, Protocol::HAP::HTTP::message_complete($buffer);
	}

	is( scalar( grep { $_ } @complete ), 1, 'the message completes once' );
	is( $complete[-1], length($whole), 'at exactly its own length' );

	# Content-Length is found wherever it sits in the block. A
	# pattern anchored with $ under /m stops at the \r of the line
	# terminator, so it would match only the last header and every
	# real response would frame short.
	my $middle =
	      "HTTP/1.1 200 OK\r\n"
	    . "Connection: keep-alive\r\n"
	    . "Content-Length: 5\r\n"
	    . "Content-Type: text/plain\r\n"
	    . "\r\n" . 'hello';
	is( Protocol::HAP::HTTP::message_complete($middle),
		length($middle), 'a header that is not the last one is read' );

	# A message whose headers arrived but whose body has not
	my $headers_only = substr $whole, 0, index( $whole, "\r\n\r\n" ) + 4;
	is( Protocol::HAP::HTTP::message_complete($headers_only),
		0, 'headers with a declared body are not a whole message' );

	my $parsed = Protocol::HAP::HTTP::parse_request($whole);
	is( $parsed->{body}, '{"test":true}', 'and the whole one parses' );
};

subtest 'framing: two messages in one read' => sub {
	my $first =
	    "GET /accessories HTTP/1.1\r\nHost: a\r\n\r\n";
	my $second =
	      "PUT /characteristics HTTP/1.1\r\n"
	    . "Content-Length: 2\r\n"
	    . "\r\n" . '{}';

	my $buffer = $first . $second;

	my $length = Protocol::HAP::HTTP::message_complete($buffer);
	is( $length, length($first), 'the first message ends where it ends' );

	my $one = substr $buffer, 0, $length, '';
	is( Protocol::HAP::HTTP::parse_request($one)->{path},
		'/accessories', 'and it parses' );

	$length = Protocol::HAP::HTTP::message_complete($buffer);
	is( $length, length($second), 'the second message follows' );

	my $two = substr $buffer, 0, $length, '';
	is( Protocol::HAP::HTTP::parse_request($two)->{path},
		'/characteristics', 'and it parses too' );
	is( $buffer, '', 'nothing is left over' );

	is( Protocol::HAP::HTTP::message_complete($buffer),
		0, 'an empty buffer holds no message' );
};

subtest 'framing: a message over the limit' => sub {
	my $body    = 'x' x 200;
	my $request =
	      "POST / HTTP/1.1\r\n"
	    . 'Content-Length: '
	    . length($body)
	    . "\r\n\r\n"
	    . $body;

	ok( Protocol::HAP::HTTP::message_complete( $request, max_size => 1000 ),
		'inside the limit it completes' );

	is( Protocol::HAP::HTTP::message_complete( $request, max_size => 100 ),
		undef, 'over the limit it is refused' );

	# The refusal comes from the declared length, before the body



( run in 1.755 second using v1.01-cache-2.11-cpan-14f38c9f855 )