Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
A
Actor Framework
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Operations
Operations
Metrics
Environments
Analytics
Analytics
CI / CD
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
cpp-libs
Actor Framework
Commits
577d3996
Commit
577d3996
authored
Sep 17, 2020
by
Dominik Charousset
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Implement WebSocket handshake response
parent
8fde4aa7
Changes
5
Hide whitespace changes
Inline
Side-by-side
Showing
5 changed files
with
331 additions
and
52 deletions
+331
-52
libcaf_net/caf/net/receive_policy.hpp
libcaf_net/caf/net/receive_policy.hpp
+4
-0
libcaf_net/caf/net/stream_transport.hpp
libcaf_net/caf/net/stream_transport.hpp
+2
-2
libcaf_net/caf/net/web_socket.hpp
libcaf_net/caf/net/web_socket.hpp
+75
-25
libcaf_net/test/net-test.hpp
libcaf_net/test/net-test.hpp
+143
-0
libcaf_net/test/net/web_socket.cpp
libcaf_net/test/net/web_socket.cpp
+107
-25
No files found.
libcaf_net/caf/net/receive_policy.hpp
View file @
577d3996
...
@@ -38,6 +38,10 @@ struct receive_policy {
...
@@ -38,6 +38,10 @@ struct receive_policy {
return
{
size
,
size
};
return
{
size
,
size
};
}
}
static
constexpr
receive_policy
up_to
(
uint32_t
max_size
)
{
return
{
1
,
max_size
};
}
static
constexpr
receive_policy
stop
()
{
static
constexpr
receive_policy
stop
()
{
return
{
0
,
0
};
return
{
0
,
0
};
}
}
...
...
libcaf_net/caf/net/stream_transport.hpp
View file @
577d3996
...
@@ -170,9 +170,9 @@ public:
...
@@ -170,9 +170,9 @@ public:
};
};
access
<
Parent
>
this_layer
{
&
parent
,
this
};
access
<
Parent
>
this_layer
{
&
parent
,
this
};
for
(
size_t
i
=
0
;
max_read_size_
>
0
&&
i
<
max_consecutive_reads_
;
++
i
)
{
for
(
size_t
i
=
0
;
max_read_size_
>
0
&&
i
<
max_consecutive_reads_
;
++
i
)
{
CAF_ASSERT
(
m
in
_read_size_
>
read_size_
);
CAF_ASSERT
(
m
ax
_read_size_
>
read_size_
);
auto
buf
=
read_buf_
.
data
()
+
read_size_
;
auto
buf
=
read_buf_
.
data
()
+
read_size_
;
size_t
len
=
m
in
_read_size_
-
read_size_
;
size_t
len
=
m
ax
_read_size_
-
read_size_
;
CAF_LOG_DEBUG
(
CAF_ARG2
(
"missing"
,
len
));
CAF_LOG_DEBUG
(
CAF_ARG2
(
"missing"
,
len
));
auto
num_bytes
=
read
(
parent
.
template
handle
<
socket_type
>(),
auto
num_bytes
=
read
(
parent
.
template
handle
<
socket_type
>(),
make_span
(
buf
,
len
));
make_span
(
buf
,
len
));
...
...
libcaf_net/caf/net/web_socket.hpp
View file @
577d3996
...
@@ -20,8 +20,11 @@
...
@@ -20,8 +20,11 @@
#include <algorithm>
#include <algorithm>
#include "caf/detail/encode_base64.hpp"
#include "caf/detail/move_if_not_ptr.hpp"
#include "caf/detail/move_if_not_ptr.hpp"
#include "caf/error.hpp"
#include "caf/error.hpp"
#include "caf/hash/sha1.hpp"
#include "caf/net/receive_policy.hpp"
#include "caf/pec.hpp"
#include "caf/pec.hpp"
#include "caf/settings.hpp"
#include "caf/settings.hpp"
#include "caf/tag/stream_oriented.hpp"
#include "caf/tag/stream_oriented.hpp"
...
@@ -42,6 +45,24 @@ public:
...
@@ -42,6 +45,24 @@ public:
using
output_tag
=
tag
::
stream_oriented
;
using
output_tag
=
tag
::
stream_oriented
;
// -- constants --------------------------------------------------------------
static
constexpr
std
::
array
<
byte
,
4
>
end_of_header
{{
byte
{
'\r'
},
byte
{
'\n'
},
byte
{
'\r'
},
byte
{
'\n'
},
}};
// A handshake should usually fit into 200-300 Bytes. 2KB is more than enough.
static
constexpr
uint32_t
max_header_size
=
2048
;
static
constexpr
string_view
header_too_large
=
"HTTP/1.1 431 Request Header Fields Too Large
\r\n
"
"Content-Type: text/plain
\r\n
"
"
\r\n
"
"Header exceeds 2048 Bytes.
\r\n
"
;
// -- constructors, destructors, and assignment operators --------------------
// -- constructors, destructors, and assignment operators --------------------
template
<
class
...
Ts
>
template
<
class
...
Ts
>
...
@@ -53,15 +74,6 @@ public:
...
@@ -53,15 +74,6 @@ public:
// nop
// nop
}
}
// -- constants --------------------------------------------------------------
static
constexpr
std
::
array
<
byte
,
4
>
end_of_header
{{
byte
{
'\r'
},
byte
{
'\n'
},
byte
{
'\r'
},
byte
{
'\n'
},
}};
// -- properties -------------------------------------------------------------
// -- properties -------------------------------------------------------------
auto
&
upper_layer
()
noexcept
{
auto
&
upper_layer
()
noexcept
{
...
@@ -74,9 +86,10 @@ public:
...
@@ -74,9 +86,10 @@ public:
// -- initialization ---------------------------------------------------------
// -- initialization ---------------------------------------------------------
template
<
class
Parent
>
template
<
class
LowerLayer
>
error
init
(
Parent
&
,
const
settings
&
config
)
{
error
init
(
LowerLayer
&
down
,
const
settings
&
config
)
{
cfg_
=
config
;
cfg_
=
config
;
down
.
configure_read
(
net
::
receive_policy
::
up_to
(
max_header_size
));
return
none
;
return
none
;
}
}
...
@@ -84,24 +97,18 @@ public:
...
@@ -84,24 +97,18 @@ public:
template
<
class
LowerLayer
>
template
<
class
LowerLayer
>
bool
prepare_send
(
LowerLayer
&
down
)
{
bool
prepare_send
(
LowerLayer
&
down
)
{
if
(
handshake_complete_
)
return
handshake_complete_
?
upper_layer_
.
prepare_send
(
down
)
:
true
;
return
upper_layer_
.
prepare_send
(
down
);
// TODO: implement me.
return
false
;
}
}
template
<
class
LowerLayer
>
template
<
class
LowerLayer
>
bool
done_sending
(
LowerLayer
&
down
)
{
bool
done_sending
(
LowerLayer
&
down
)
{
if
(
handshake_complete_
)
return
handshake_complete_
?
upper_layer_
.
done_sending
(
down
)
:
true
;
return
upper_layer_
.
done_sending
(
down
);
// TODO: implement me.
return
false
;
}
}
template
<
class
LowerLayer
>
template
<
class
LowerLayer
>
void
abort
(
LowerLayer
&
down
,
const
error
&
reason
)
{
void
abort
(
LowerLayer
&
down
,
const
error
&
reason
)
{
if
(
handshake_complete_
)
if
(
handshake_complete_
)
return
upper_layer_
.
abort
(
down
,
reason
);
upper_layer_
.
abort
(
down
,
reason
);
}
}
template
<
class
LowerLayer
>
template
<
class
LowerLayer
>
...
@@ -111,8 +118,16 @@ public:
...
@@ -111,8 +118,16 @@ public:
// TODO: we could avoid repeated scans by using the delta parameter.
// TODO: we could avoid repeated scans by using the delta parameter.
auto
i
=
std
::
search
(
buffer
.
begin
(),
buffer
.
end
(),
auto
i
=
std
::
search
(
buffer
.
begin
(),
buffer
.
end
(),
end_of_header
.
begin
(),
end_of_header
.
end
());
end_of_header
.
begin
(),
end_of_header
.
end
());
if
(
i
==
buffer
.
end
())
if
(
i
==
buffer
.
end
())
{
if
(
buffer
.
size
()
==
max_header_size
)
{
write
(
down
,
header_too_large
);
auto
err
=
make_error
(
pec
::
too_many_characters
,
"exceeded maximum header size"
);
down
.
abort_reason
(
std
::
move
(
err
));
return
-
1
;
}
return
0
;
return
0
;
}
auto
offset
=
static_cast
<
size_t
>
(
std
::
distance
(
buffer
.
begin
(),
i
));
auto
offset
=
static_cast
<
size_t
>
(
std
::
distance
(
buffer
.
begin
(),
i
));
offset
+=
end_of_header
.
size
();
offset
+=
end_of_header
.
size
();
// Take all but the last two bytes (to avoid an empty line) as input for
// Take all but the last two bytes (to avoid an empty line) as input for
...
@@ -121,10 +136,29 @@ public:
...
@@ -121,10 +136,29 @@ public:
offset
-
2
};
offset
-
2
};
if
(
!
handle_header
(
down
,
header
))
if
(
!
handle_header
(
down
,
header
))
return
-
1
;
return
-
1
;
return
offset
+
upper_layer_
.
consume
(
down
,
buffer
.
subspan
(
offset
),
{});
ptrdiff_t
sub_result
=
0
;
if
(
offset
<
buffer
.
size
())
{
sub_result
=
upper_layer_
.
consume
(
down
,
buffer
.
subspan
(
offset
),
{});
if
(
sub_result
<
0
)
return
sub_result
;
}
return
static_cast
<
ptrdiff_t
>
(
offset
)
+
sub_result
;
}
bool
handshake_complete
()
const
noexcept
{
return
handshake_complete_
;
}
}
private:
private:
template
<
class
LowerLayer
>
static
void
write
(
LowerLayer
&
down
,
string_view
output
)
{
auto
out
=
as_bytes
(
make_span
(
output
));
down
.
begin_output
();
auto
&
buf
=
down
.
output_buffer
();
buf
.
insert
(
buf
.
end
(),
out
.
begin
(),
out
.
end
());
down
.
end_output
();
}
template
<
class
LowerLayer
>
template
<
class
LowerLayer
>
bool
handle_header
(
LowerLayer
&
down
,
string_view
input
)
{
bool
handle_header
(
LowerLayer
&
down
,
string_view
input
)
{
// Parse the first line, i.e., "METHOD REQUEST-URI VERSION".
// Parse the first line, i.e., "METHOD REQUEST-URI VERSION".
...
@@ -156,7 +190,8 @@ private:
...
@@ -156,7 +190,8 @@ private:
// Check whether the mandatory fields exist.
// Check whether the mandatory fields exist.
std
::
string
sec_key
;
std
::
string
sec_key
;
if
(
auto
skey_field
=
get_if
<
std
::
string
>
(
&
fields
,
"Sec-WebSocket-Key"
))
{
if
(
auto
skey_field
=
get_if
<
std
::
string
>
(
&
fields
,
"Sec-WebSocket-Key"
))
{
sec_key
=
detail
::
move_if_not_ptr
(
skey_field
);
auto
field_hash
=
hash
::
sha1
::
compute
(
*
skey_field
);
sec_key
=
detail
::
encode_base64
(
field_hash
);
}
else
{
}
else
{
auto
err
=
make_error
(
pec
::
missing_field
,
auto
err
=
make_error
(
pec
::
missing_field
,
"Mandatory field Sec-WebSocket-Key not found"
);
"Mandatory field Sec-WebSocket-Key not found"
);
...
@@ -169,7 +204,21 @@ private:
...
@@ -169,7 +204,21 @@ private:
return
false
;
return
false
;
}
}
// Send server handshake.
// Send server handshake.
down
.
begin_output
();
auto
&
buf
=
down
.
output_buffer
();
auto
append
=
[
&
buf
](
string_view
output
)
{
auto
out
=
as_bytes
(
make_span
(
output
));
buf
.
insert
(
buf
.
end
(),
out
.
begin
(),
out
.
end
());
};
append
(
"HTTP/1.1 101 Switching Protocols
\r\n
"
"Upgrade: websocket
\r\n
"
"Connection: Upgrade
\r\n
"
"Sec-WebSocket-Accept: "
);
append
(
sec_key
);
append
(
"
\r\n\r\n
"
);
down
.
end_output
();
// Done.
// Done.
handshake_complete_
=
true
;
return
true
;
return
true
;
}
}
...
@@ -187,8 +236,9 @@ private:
...
@@ -187,8 +236,9 @@ private:
static
string_view
trim
(
string_view
str
)
{
static
string_view
trim
(
string_view
str
)
{
str
.
remove_prefix
(
std
::
min
(
str
.
find_first_not_of
(
' '
),
str
.
size
()));
str
.
remove_prefix
(
std
::
min
(
str
.
find_first_not_of
(
' '
),
str
.
size
()));
str
.
remove_suffix
(
str
.
size
()
auto
trim_pos
=
str
.
find_last_not_of
(
' '
);
-
std
::
min
(
str
.
find_last_not_of
(
' '
),
str
.
size
()));
if
(
trim_pos
!=
str
.
npos
)
str
.
remove_suffix
(
str
.
size
()
-
(
trim_pos
+
1
));
return
str
;
return
str
;
}
}
...
...
libcaf_net/test/net-test.hpp
0 → 100644
View file @
577d3996
#pragma once
#include "caf/error.hpp"
#include "caf/net/receive_policy.hpp"
#include "caf/net/test/host_fixture.hpp"
#include "caf/settings.hpp"
#include "caf/span.hpp"
#include "caf/string_view.hpp"
#include "caf/tag/stream_oriented.hpp"
#include "caf/test/dsl.hpp"
template
<
class
UpperLayer
>
class
mock_stream_transport
{
public:
// -- member types -----------------------------------------------------------
using
output_tag
=
caf
::
tag
::
stream_oriented
;
// -- interface for the upper layer ------------------------------------------
class
access
{
public:
explicit
access
(
mock_stream_transport
*
transport
)
:
transport_
(
transport
)
{
// nop
}
void
begin_output
()
{
// nop
}
auto
&
output_buffer
()
{
return
transport_
->
output
;
}
constexpr
void
end_output
()
{
// nop
}
bool
can_send_more
()
const
noexcept
{
return
true
;
}
void
abort_reason
(
caf
::
error
reason
)
{
transport_
->
abort_reason
=
std
::
move
(
reason
);
}
void
configure_read
(
caf
::
net
::
receive_policy
policy
)
{
transport_
->
min_read_size
=
policy
.
min_size
;
transport_
->
max_read_size
=
policy
.
max_size
;
}
private:
mock_stream_transport
*
transport_
;
};
friend
class
access
;
// -- initialization ---------------------------------------------------------
caf
::
error
init
(
const
caf
::
settings
&
config
)
{
access
this_layer
{
this
};
return
upper_layer
.
init
(
this_layer
,
config
);
}
caf
::
error
init
()
{
caf
::
settings
config
;
return
init
(
config
);
}
// -- buffer management ------------------------------------------------------
void
push
(
caf
::
span
<
const
caf
::
byte
>
bytes
)
{
input
.
insert
(
input
.
begin
(),
bytes
.
begin
(),
bytes
.
end
());
}
void
push
(
caf
::
string_view
str
)
{
push
(
caf
::
as_bytes
(
caf
::
make_span
(
str
)));
}
size_t
unconsumed
()
const
noexcept
{
return
read_buf_
.
size
();
}
caf
::
string_view
output_as_str
()
const
noexcept
{
return
{
reinterpret_cast
<
const
char
*>
(
output
.
data
()),
output
.
size
()};
}
// -- event callbacks --------------------------------------------------------
ptrdiff_t
handle_input
()
{
ptrdiff_t
result
=
0
;
access
this_layer
{
this
};
while
(
max_read_size
>
0
)
{
CAF_ASSERT
(
max_read_size
>
static_cast
<
size_t
>
(
read_size_
));
size_t
len
=
max_read_size
-
static_cast
<
size_t
>
(
read_size_
);
CAF_LOG_DEBUG
(
CAF_ARG2
(
"available capacity:"
,
len
));
auto
num_bytes
=
std
::
min
(
input
.
size
(),
len
);
if
(
num_bytes
==
0
)
return
result
;
auto
delta_offset
=
static_cast
<
ptrdiff_t
>
(
read_buf_
.
size
());
read_buf_
.
insert
(
read_buf_
.
end
(),
input
.
begin
(),
input
.
begin
()
+
num_bytes
);
input
.
erase
(
input
.
begin
(),
input
.
begin
()
+
num_bytes
);
read_size_
+=
static_cast
<
ptrdiff_t
>
(
num_bytes
);
if
(
static_cast
<
size_t
>
(
read_size_
)
<
min_read_size
)
return
result
;
auto
delta
=
make_span
(
read_buf_
.
data
()
+
delta_offset
,
read_size_
-
delta_offset
);
auto
consumed
=
upper_layer
.
consume
(
this_layer
,
caf
::
make_span
(
read_buf_
),
delta
);
if
(
consumed
>
0
)
{
result
+=
static_cast
<
ptrdiff_t
>
(
consumed
);
read_buf_
.
erase
(
read_buf_
.
begin
(),
read_buf_
.
begin
()
+
consumed
);
read_size_
-=
consumed
;
}
else
if
(
consumed
<
0
)
{
if
(
!
abort_reason
)
abort_reason
=
caf
::
sec
::
runtime_error
;
upper_layer
.
abort
(
this_layer
,
abort_reason
);
return
-
1
;
}
}
return
result
;
}
// -- member variables -------------------------------------------------------
caf
::
error
abort_reason
;
UpperLayer
upper_layer
;
std
::
vector
<
caf
::
byte
>
output
;
std
::
vector
<
caf
::
byte
>
input
;
uint32_t
min_read_size
=
0
;
uint32_t
max_read_size
=
0
;
private:
std
::
vector
<
caf
::
byte
>
read_buf_
;
ptrdiff_t
read_size_
=
0
;
};
libcaf_net/test/net/web_socket.cpp
View file @
577d3996
...
@@ -20,8 +20,7 @@
...
@@ -20,8 +20,7 @@
#include "caf/net/web_socket.hpp"
#include "caf/net/web_socket.hpp"
#include "caf/net/test/host_fixture.hpp"
#include "net-test.hpp"
#include "caf/test/dsl.hpp"
#include "caf/net/multiplexer.hpp"
#include "caf/net/multiplexer.hpp"
#include "caf/net/socket_manager.hpp"
#include "caf/net/socket_manager.hpp"
...
@@ -29,16 +28,22 @@
...
@@ -29,16 +28,22 @@
#include "caf/net/stream_transport.hpp"
#include "caf/net/stream_transport.hpp"
using
namespace
caf
;
using
namespace
caf
;
using
namespace
std
::
literals
::
string_literals
;
namespace
{
namespace
{
using
byte_span
=
span
<
const
byte
>
;
using
byte_span
=
span
<
const
byte
>
;
struct
app
{
using
svec
=
std
::
vector
<
std
::
string
>
;
struct
app_t
{
std
::
vector
<
std
::
string
>
lines
;
std
::
vector
<
std
::
string
>
lines
;
settings
cfg
;
template
<
class
LowerLayer
>
template
<
class
LowerLayer
>
error
init
(
LowerLayer
&
,
const
settings
&
)
{
error
init
(
LowerLayer
&
,
const
settings
&
init_cfg
)
{
cfg
=
init_cfg
;
return
none
;
return
none
;
}
}
...
@@ -63,11 +68,12 @@ struct app {
...
@@ -63,11 +68,12 @@ struct app {
auto
e
=
buffer
.
end
();
auto
e
=
buffer
.
end
();
if
(
auto
i
=
std
::
find
(
buffer
.
begin
(),
e
,
nl
);
i
!=
e
)
{
if
(
auto
i
=
std
::
find
(
buffer
.
begin
(),
e
,
nl
);
i
!=
e
)
{
std
::
string
str
;
std
::
string
str
;
auto
string_size
=
static_cast
<
size_t
>
(
std
::
distance
(
buffer
.
begin
(),
e
));
auto
string_size
=
static_cast
<
size_t
>
(
std
::
distance
(
buffer
.
begin
(),
i
));
str
.
reserve
(
string_size
);
str
.
reserve
(
string_size
);
auto
num_bytes
=
string_size
+
1
;
// Also consume the newline character.
auto
num_bytes
=
string_size
+
1
;
// Also consume the newline character.
std
::
transform
(
buffer
.
begin
(),
i
,
std
::
back_inserter
(
str
),
std
::
transform
(
buffer
.
begin
(),
i
,
std
::
back_inserter
(
str
),
[](
byte
x
)
{
return
static_cast
<
char
>
(
x
);
});
[](
byte
x
)
{
return
static_cast
<
char
>
(
x
);
});
lines
.
emplace_back
(
std
::
move
(
str
));
return
num_bytes
+
consume
(
down
,
buffer
.
subspan
(
num_bytes
),
{});
return
num_bytes
+
consume
(
down
,
buffer
.
subspan
(
num_bytes
),
{});
}
}
return
0
;
return
0
;
...
@@ -77,37 +83,113 @@ struct app {
...
@@ -77,37 +83,113 @@ struct app {
struct
fixture
:
host_fixture
{
struct
fixture
:
host_fixture
{
fixture
()
{
fixture
()
{
using
namespace
caf
::
net
;
using
namespace
caf
::
net
;
mpx
=
std
::
make_shared
<
multiplexer
>
();
ws
=
std
::
addressof
(
transport
.
upper_layer
);
mpx
->
set_thread_id
();
app
=
std
::
addressof
(
ws
->
upper_layer
());
std
::
tie
(
sock
.
self
,
sock
.
mgr
)
=
unbox
(
make_stream_socket_pair
());
if
(
auto
err
=
transport
.
init
())
auto
ptr
=
make_socket_manager
<
app
,
web_socket
,
stream_transport
>
(
sock
.
mgr
,
CAF_FAIL
(
"failed to initialize mock transport: "
<<
err
);
mpx
);
settings
cfg
;
if
(
auto
err
=
ptr
->
init
(
cfg
))
CAF_FAIL
(
"initializing the socket manager failed: "
<<
err
);
mgr
=
ptr
;
}
~
fixture
()
{
close
(
sock
.
self
);
}
}
net
::
multiplexer_ptr
mpx
;
mock_stream_transport
<
net
::
web_socket
<
app_t
>>
transport
;
net
::
socket_manager_ptr
mgr
;
net
::
web_socket
<
app_t
>*
ws
;
struct
{
app_t
*
app
;
net
::
stream_socket
self
;
net
::
stream_socket
mgr
;
}
sock
;
};
};
constexpr
string_view
opening_handshake
=
"GET /chat HTTP/1.1
\r\n
"
"Host: server.example.com
\r\n
"
"Upgrade: websocket
\r\n
"
"Connection: Upgrade
\r\n
"
"Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
\r\n
"
"Origin: http://example.com
\r\n
"
"Sec-WebSocket-Protocol: chat, superchat
\r\n
"
"Sec-WebSocket-Version: 13
\r\n
"
"
\r\n
"
;
}
// namespace
}
// namespace
#define CHECK_SETTING(key, expected_value) \
if (CAF_CHECK(holds_alternative<std::string>(app->cfg, key))) \
CAF_CHECK_EQUAL(get<std::string>(app->cfg, key), expected_value);
CAF_TEST_FIXTURE_SCOPE
(
web_socket_tests
,
fixture
)
CAF_TEST_FIXTURE_SCOPE
(
web_socket_tests
,
fixture
)
CAF_TEST
(
todo
)
{
CAF_TEST
(
applications
receive
handshake
data
via
config
)
{
transport
.
push
(
opening_handshake
);
{
auto
consumed
=
transport
.
handle_input
();
if
(
consumed
<
0
)
CAF_FAIL
(
"error handling input: "
<<
transport
.
abort_reason
);
CAF_CHECK_EQUAL
(
consumed
,
static_cast
<
ptrdiff_t
>
(
opening_handshake
.
size
()));
}
CAF_CHECK_EQUAL
(
transport
.
input
.
size
(),
0u
);
CAF_CHECK_EQUAL
(
transport
.
unconsumed
(),
0u
);
CAF_CHECK
(
ws
->
handshake_complete
());
CHECK_SETTING
(
"web-socket.method"
,
"GET"
);
CHECK_SETTING
(
"web-socket.request-uri"
,
"/chat"
);
CHECK_SETTING
(
"web-socket.http-version"
,
"HTTP/1.1"
);
CHECK_SETTING
(
"web-socket.fields.Host"
,
"server.example.com"
);
CHECK_SETTING
(
"web-socket.fields.Upgrade"
,
"websocket"
);
CHECK_SETTING
(
"web-socket.fields.Connection"
,
"Upgrade"
);
CHECK_SETTING
(
"web-socket.fields.Origin"
,
"http://example.com"
);
CHECK_SETTING
(
"web-socket.fields.Sec-WebSocket-Protocol"
,
"chat, superchat"
);
CHECK_SETTING
(
"web-socket.fields.Sec-WebSocket-Version"
,
"13"
);
CHECK_SETTING
(
"web-socket.fields.Sec-WebSocket-Key"
,
"dGhlIHNhbXBsZSBub25jZQ=="
);
}
CAF_TEST
(
the
server
responds
with
an
HTTP
response
on
success
)
{
transport
.
push
(
opening_handshake
);
CAF_CHECK_EQUAL
(
transport
.
handle_input
(),
static_cast
<
ptrdiff_t
>
(
opening_handshake
.
size
()));
CAF_CHECK
(
ws
->
handshake_complete
());
CAF_CHECK
(
transport
.
output_as_str
(),
"HTTP/1.1 101 Switching Protocols
\r\n
"
"Upgrade: websocket
\r\n
"
"Connection: Upgrade
\r\n
"
"Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=
\r\n\r\n
"
);
}
CAF_TEST
(
handshakes
may
arrive
in
chunks
)
{
svec
bufs
;
size_t
chunk_size
=
opening_handshake
.
size
()
/
3
;
auto
i
=
opening_handshake
.
begin
();
bufs
.
emplace_back
(
i
,
i
+
chunk_size
);
i
+=
chunk_size
;
bufs
.
emplace_back
(
i
,
i
+
chunk_size
);
i
+=
chunk_size
;
bufs
.
emplace_back
(
i
,
opening_handshake
.
end
());
transport
.
push
(
bufs
[
0
]);
CAF_CHECK_EQUAL
(
transport
.
handle_input
(),
0u
);
CAF_CHECK
(
!
ws
->
handshake_complete
());
transport
.
push
(
bufs
[
1
]);
CAF_CHECK_EQUAL
(
transport
.
handle_input
(),
0u
);
CAF_CHECK
(
!
ws
->
handshake_complete
());
transport
.
push
(
bufs
[
2
]);
CAF_CHECK_EQUAL
(
transport
.
handle_input
(),
opening_handshake
.
size
());
CAF_CHECK
(
ws
->
handshake_complete
());
}
CAF_TEST
(
data
may
follow
the
handshake
immediately
)
{
std
::
string
buf
{
opening_handshake
.
begin
(),
opening_handshake
.
end
()};
buf
+=
"Hello WebSocket!
\n
"
;
buf
+=
"Bye WebSocket!
\n
"
;
transport
.
push
(
buf
);
CAF_CHECK_EQUAL
(
transport
.
handle_input
(),
static_cast
<
ptrdiff_t
>
(
buf
.
size
()));
CAF_CHECK
(
ws
->
handshake_complete
());
CAF_CHECK_EQUAL
(
app
->
lines
,
svec
({
"Hello WebSocket!"
,
"Bye WebSocket!"
}));
}
CAF_TEST
(
data
may
arrive
later
)
{
transport
.
push
(
opening_handshake
);
CAF_CHECK_EQUAL
(
transport
.
handle_input
(),
static_cast
<
ptrdiff_t
>
(
opening_handshake
.
size
()));
CAF_CHECK
(
ws
->
handshake_complete
());
auto
buf
=
"Hello WebSocket!
\n
Bye WebSocket!
\n
"
s
;
transport
.
push
(
buf
);
CAF_CHECK_EQUAL
(
transport
.
handle_input
(),
static_cast
<
ptrdiff_t
>
(
buf
.
size
()));
CAF_CHECK_EQUAL
(
app
->
lines
,
svec
({
"Hello WebSocket!"
,
"Bye WebSocket!"
}));
}
}
CAF_TEST_FIXTURE_SCOPE_END
()
CAF_TEST_FIXTURE_SCOPE_END
()
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