Skip to content
GitLab
Projects
Groups
Snippets
Help
Loading...
Help
What's new
7
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
Open sidebar
ISC Open Source Projects
Kea
Commits
82a6390b
Commit
82a6390b
authored
Jul 03, 2017
by
Marcin Siodelski
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
[5318] Added unit tests for long connections in DHCPv6.
parent
c932ebf8
Changes
2
Hide whitespace changes
Inline
Side-by-side
Showing
2 changed files
with
240 additions
and
2 deletions
+240
-2
src/bin/dhcp4/tests/ctrl_dhcp4_srv_unittest.cc
src/bin/dhcp4/tests/ctrl_dhcp4_srv_unittest.cc
+2
-2
src/bin/dhcp6/tests/ctrl_dhcp6_srv_unittest.cc
src/bin/dhcp6/tests/ctrl_dhcp6_srv_unittest.cc
+238
-0
No files found.
src/bin/dhcp4/tests/ctrl_dhcp4_srv_unittest.cc
View file @
82a6390b
...
...
@@ -327,8 +327,8 @@ public:
}
/// @brief Command handler which generates long response
static
ConstElementPtr
longResponseHandler
(
const
std
::
string
&
,
const
ConstElementPtr
&
)
{
static
ConstElementPtr
longResponseHandler
(
const
std
::
string
&
,
const
ConstElementPtr
&
)
{
ElementPtr
arguments
=
Element
::
createList
();
std
::
string
arg
=
"responseresponseresponseresponseresponseresponse"
"response"
;
...
...
src/bin/dhcp6/tests/ctrl_dhcp6_srv_unittest.cc
View file @
82a6390b
...
...
@@ -31,6 +31,8 @@
#include <sys/ioctl.h>
#include <cstdlib>
#include <thread>
using
namespace
std
;
using
namespace
isc
::
asiolink
;
using
namespace
isc
::
config
;
...
...
@@ -43,7 +45,31 @@ using namespace isc::test;
namespace
{
/// @brief Simple RAII class which stops IO service upon destruction
/// of the object.
class
IOServiceWork
{
public:
/// @brief Constructor.
///
/// @param io_service Pointer to the IO service to be stopped.
IOServiceWork
(
const
IOServicePtr
&
io_service
)
:
io_service_
(
io_service
)
{
}
/// @brief Destructor.
///
/// Stops IO service.
~
IOServiceWork
()
{
io_service_
->
stop
();
}
private:
/// @brief Pointer to the IO service to be stopped upon destruction.
IOServicePtr
io_service_
;
};
class
NakedControlledDhcpv6Srv
:
public
ControlledDhcpv6Srv
{
// "Naked" DHCPv6 server, exposes internal fields
...
...
@@ -306,6 +332,18 @@ public:
ADD_FAILURE
()
<<
"Invalid expected status: "
<<
exp_status
;
}
}
/// @brief Command handler which generates long response
static
ConstElementPtr
longResponseHandler
(
const
std
::
string
&
,
const
ConstElementPtr
&
)
{
ElementPtr
arguments
=
Element
::
createList
();
std
::
string
arg
=
"responseresponseresponseresponseresponseresponse"
"response"
;
for
(
unsigned
i
=
0
;
i
<
8000
;
++
i
)
{
arguments
->
add
(
Element
::
create
(
arg
));
}
return
(
createAnswer
(
0
,
arguments
));
}
};
...
...
@@ -1131,5 +1169,205 @@ TEST_F(CtrlChannelDhcpv6SrvTest, concurrentConnections) {
ASSERT_NO_THROW
(
getIOService
()
->
poll
());
}
// This test verifies that the server can receive and process a large command.
TEST_F
(
CtrlChannelDhcpv6SrvTest
,
longCommand
)
{
createUnixChannelServer
();
std
::
string
response
;
std
::
thread
th
([
this
,
&
response
]()
{
// IO service will be stopped automatically when this object goes
// out of scope and is destroyed. This is useful because we use
// asserts which may break the thread in various exit points.
IOServiceWork
work
(
getIOService
());
// Create client which we will use to send command to the server.
boost
::
scoped_ptr
<
UnixControlClient
>
client
(
new
UnixControlClient
());
ASSERT_TRUE
(
client
);
// Connect to the server. This will trigger acceptor handler on the
// server side and create a new connection.
ASSERT_TRUE
(
client
->
connectToServer
(
socket_path_
));
// This counter will hold the number of bytes transferred to the server
// so far.
size_t
bytes_transferred
=
0
;
// This is the desired size of the command sent to the server (1MB). The
// actual size sent will be slightly greater than that.
const
size_t
command_size
=
1024
*
1000
;
bool
first_payload
=
true
;
// If we still haven't sent the entire command, continue sending.
while
(
bytes_transferred
<
command_size
)
{
// We're sending command 'foo' with arguments being a list of
// strings. If this is the first transmission, send command name
// and open the arguments list.
if
(
bytes_transferred
==
0
)
{
std
::
string
preamble
=
"{
\"
command
\"
:
\"
foo
\"
,
\"
arguments
\"
: [ "
;
ASSERT_TRUE
(
client
->
sendCommand
(
preamble
));
// Store the number of bytes sent.
bytes_transferred
+=
preamble
.
size
();
}
else
{
// We have already transmitted command name and arguments. Now
// we send the list of 'blabla' strings.
std
::
ostringstream
payload
;
// If this is not the first parameter in on the list it must be
// prefixed with a comma.
if
(
!
first_payload
)
{
payload
<<
", "
;
}
first_payload
=
false
;
payload
<<
"
\"
blablablablablablablablablablablablablablablabla
\"
"
;
// If we have hit the limit of the command size, close braces to
// get appropriate JSON.
if
(
bytes_transferred
+
payload
.
tellp
()
>
command_size
)
{
payload
<<
"] }"
;
}
// Send the payload.
ASSERT_TRUE
(
client
->
sendCommand
(
payload
.
str
()));
// Update the number of bytes sent.
bytes_transferred
+=
payload
.
tellp
();
}
}
// Set timeout to 5 seconds to allow the time for the server to send
// a response.
const
unsigned
int
timeout
=
5
;
ASSERT_TRUE
(
client
->
getResponse
(
response
,
timeout
));
// We're done. Close the connection to the server.
client
->
disconnectFromServer
();
});
// Run the server until the command has been processed and response
// received.
getIOService
()
->
run
();
// Wait for the thread to complete.
th
.
join
();
EXPECT_EQ
(
"{
\"
result
\"
: 2,
\"
text
\"
:
\"
'foo' command not supported.
\"
}"
,
response
);
}
// This test verifies that the server can send long response to the client.
TEST_F
(
CtrlChannelDhcpv6SrvTest
,
longResponse
)
{
// We need to generate large response. The simplest way is to create
// a command and a handler which will generate some static response
// of a desired size.
ASSERT_NO_THROW
(
CommandMgr
::
instance
().
registerCommand
(
"foo"
,
boost
::
bind
(
&
CtrlChannelDhcpv6SrvTest
::
longResponseHandler
,
_1
,
_2
));
);
createUnixChannelServer
();
// The UnixControlClient doesn't have any means to check that the entire
// response has been received. What we want to do is to generate a
// reference response using our command handler and then compare
// what we have received over the unix domain socket with this reference
// response to figure out when to stop receiving.
std
::
string
reference_response
=
longResponseHandler
(
"foo"
,
ConstElementPtr
())
->
str
();
// In this stream we're going to collect out partial responses.
std
::
ostringstream
response
;
// The client is synchronous so it is useful to run it in a thread.
std
::
thread
th
([
this
,
&
response
,
reference_response
]()
{
// IO service will be stopped automatically when this object goes
// out of scope and is destroyed. This is useful because we use
// asserts which may break the thread in various exit points.
IOServiceWork
work
(
getIOService
());
// Remember the response size so as we know when we should stop
// receiving.
const
size_t
long_response_size
=
reference_response
.
size
();
// Create the client and connect it to the server.
boost
::
scoped_ptr
<
UnixControlClient
>
client
(
new
UnixControlClient
());
ASSERT_TRUE
(
client
);
ASSERT_TRUE
(
client
->
connectToServer
(
socket_path_
));
// Send the stub command.
std
::
string
command
=
"{
\"
command
\"
:
\"
foo
\"
,
\"
arguments
\"
: { } }"
;
ASSERT_TRUE
(
client
->
sendCommand
(
command
));
// Keep receiving response data until we have received the full answer.
while
(
response
.
tellp
()
<
long_response_size
)
{
std
::
string
partial
;
const
unsigned
int
timeout
=
5
;
ASSERT_TRUE
(
client
->
getResponse
(
partial
,
5
));
response
<<
partial
;
}
// We have received the entire response, so close the connection and
// stop the IO service.
client
->
disconnectFromServer
();
});
// Run the server until the entire response has been received.
getIOService
()
->
run
();
// Wait for the thread to complete.
th
.
join
();
// Make sure we have received correct response.
EXPECT_EQ
(
reference_response
,
response
.
str
());
}
// This test verifies that the server signals timeout if the transmission
// takes too long.
TEST_F
(
CtrlChannelDhcpv6SrvTest
,
connectionTimeout
)
{
createUnixChannelServer
();
// Server's response will be assigned to this variable.
std
::
string
response
;
// It is useful to create a thread and run the server and the client
// at the same time and independently.
std
::
thread
th
([
this
,
&
response
]()
{
// IO service will be stopped automatically when this object goes
// out of scope and is destroyed. This is useful because we use
// asserts which may break the thread in various exit points.
IOServiceWork
work
(
getIOService
());
// Create the client and connect it to the server.
boost
::
scoped_ptr
<
UnixControlClient
>
client
(
new
UnixControlClient
());
ASSERT_TRUE
(
client
);
ASSERT_TRUE
(
client
->
connectToServer
(
socket_path_
));
// Send partial command. The server will be waiting for the remaining
// part to be sent and will eventually signal a timeout.
std
::
string
command
=
"{
\"
command
\"
:
\"
foo
\"
"
;
ASSERT_TRUE
(
client
->
sendCommand
(
command
));
// Let's wait up to 10s for the server's response. The response
// should arrive sooner assuming that the timeout mechanism for
// the server is working properly.
const
unsigned
int
timeout
=
10
;
ASSERT_TRUE
(
client
->
getResponse
(
response
,
10
));
// Explicitly close the client's connection.
client
->
disconnectFromServer
();
});
// Run the server until stopped.
getIOService
()
->
run
();
// Wait for the thread to return.
th
.
join
();
// Check that the server has signalled a timeout.
EXPECT_EQ
(
"{
\"
result
\"
: 1,
\"
text
\"
:
\"
Connection over control channel"
" timed out
\"
}"
,
response
);
}
}
// End of anonymous namespace
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment