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
d2830d80
Commit
d2830d80
authored
Feb 13, 2022
by
Dominik Charousset
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Implement scaffolding for an HTTP transport
parent
1f3cc4e7
Changes
26
Hide whitespace changes
Inline
Side-by-side
Showing
26 changed files
with
1579 additions
and
155 deletions
+1579
-155
libcaf_net/CMakeLists.txt
libcaf_net/CMakeLists.txt
+7
-0
libcaf_net/caf/net/consumer_adapter.hpp
libcaf_net/caf/net/consumer_adapter.hpp
+3
-1
libcaf_net/caf/net/http/context.hpp
libcaf_net/caf/net/http/context.hpp
+12
-0
libcaf_net/caf/net/http/header.hpp
libcaf_net/caf/net/http/header.hpp
+95
-0
libcaf_net/caf/net/http/header_fields_map.hpp
libcaf_net/caf/net/http/header_fields_map.hpp
+15
-0
libcaf_net/caf/net/http/method.hpp
libcaf_net/caf/net/http/method.hpp
+69
-0
libcaf_net/caf/net/http/server.hpp
libcaf_net/caf/net/http/server.hpp
+345
-0
libcaf_net/caf/net/http/status.hpp
libcaf_net/caf/net/http/status.hpp
+188
-0
libcaf_net/caf/net/http/v1.hpp
libcaf_net/caf/net/http/v1.hpp
+38
-0
libcaf_net/caf/net/hypertext_oriented_layer_ptr.hpp
libcaf_net/caf/net/hypertext_oriented_layer_ptr.hpp
+189
-0
libcaf_net/caf/net/length_prefix_framing.hpp
libcaf_net/caf/net/length_prefix_framing.hpp
+4
-3
libcaf_net/caf/net/message_flow_bridge.hpp
libcaf_net/caf/net/message_flow_bridge.hpp
+28
-14
libcaf_net/caf/net/message_oriented_layer_ptr.hpp
libcaf_net/caf/net/message_oriented_layer_ptr.hpp
+3
-3
libcaf_net/caf/net/socket_manager.hpp
libcaf_net/caf/net/socket_manager.hpp
+1
-0
libcaf_net/caf/net/stream_transport.hpp
libcaf_net/caf/net/stream_transport.hpp
+0
-4
libcaf_net/caf/net/web_socket/client.hpp
libcaf_net/caf/net/web_socket/client.hpp
+4
-5
libcaf_net/caf/net/web_socket/handshake.hpp
libcaf_net/caf/net/web_socket/handshake.hpp
+0
-12
libcaf_net/caf/net/web_socket/server.hpp
libcaf_net/caf/net/web_socket/server.hpp
+55
-71
libcaf_net/caf/net/web_socket/status.hpp
libcaf_net/caf/net/web_socket/status.hpp
+1
-0
libcaf_net/caf/tag/hypertext_oriented.hpp
libcaf_net/caf/tag/hypertext_oriented.hpp
+12
-0
libcaf_net/src/net/http/header.cpp
libcaf_net/src/net/http/header.cpp
+181
-0
libcaf_net/src/net/http/method.cpp
libcaf_net/src/net/http/method.cpp
+29
-0
libcaf_net/src/net/http/status.cpp
libcaf_net/src/net/http/status.cpp
+103
-0
libcaf_net/src/net/http/v1.cpp
libcaf_net/src/net/http/v1.cpp
+89
-0
libcaf_net/src/net/web_socket/handshake.cpp
libcaf_net/src/net/web_socket/handshake.cpp
+0
-42
libcaf_net/test/net/http/server.cpp
libcaf_net/test/net/http/server.cpp
+108
-0
No files found.
libcaf_net/CMakeLists.txt
View file @
d2830d80
...
@@ -16,6 +16,8 @@ caf_incubator_add_component(
...
@@ -16,6 +16,8 @@ caf_incubator_add_component(
net.basp.connection_state
net.basp.connection_state
net.basp.ec
net.basp.ec
net.basp.message_type
net.basp.message_type
net.http.method
net.http.status
net.operation
net.operation
net.web_socket.status
net.web_socket.status
HEADERS
HEADERS
...
@@ -31,6 +33,10 @@ caf_incubator_add_component(
...
@@ -31,6 +33,10 @@ caf_incubator_add_component(
src/multiplexer.cpp
src/multiplexer.cpp
src/net/abstract_actor_shell.cpp
src/net/abstract_actor_shell.cpp
src/net/actor_shell.cpp
src/net/actor_shell.cpp
src/net/http/header.cpp
src/net/http/method.cpp
src/net/http/status.cpp
src/net/http/v1.cpp
src/net/middleman.cpp
src/net/middleman.cpp
src/net/middleman_backend.cpp
src/net/middleman_backend.cpp
src/net/packet_writer.cpp
src/net/packet_writer.cpp
...
@@ -57,6 +63,7 @@ caf_incubator_add_component(
...
@@ -57,6 +63,7 @@ caf_incubator_add_component(
multiplexer
multiplexer
net.actor_shell
net.actor_shell
net.consumer_adapter
net.consumer_adapter
net.http.server
net.length_prefix_framing
net.length_prefix_framing
net.operation
net.operation
net.producer_adapter
net.producer_adapter
...
...
libcaf_net/caf/net/consumer_adapter.hpp
View file @
d2830d80
...
@@ -86,8 +86,10 @@ private:
...
@@ -86,8 +86,10 @@ private:
}
}
void
on_wakeup
()
{
void
on_wakeup
()
{
if
(
buf_
&&
buf_
->
has_consumer_event
())
if
(
buf_
&&
buf_
->
has_consumer_event
())
{
puts
(
"WAKEUP"
);
mgr_
->
mpx
().
register_writing
(
mgr_
);
mgr_
->
mpx
().
register_writing
(
mgr_
);
}
}
}
intrusive_ptr
<
socket_manager
>
mgr_
;
intrusive_ptr
<
socket_manager
>
mgr_
;
...
...
libcaf_net/caf/net/http/context.hpp
0 → 100644
View file @
d2830d80
// This file is part of CAF, the C++ Actor Framework. See the file LICENSE in
// the main distribution directory for license terms and copyright or visit
// https://github.com/actor-framework/actor-framework/blob/master/LICENSE.
#pragma once
namespace
caf
::
net
::
http
{
/// Stores context information for a request. For HTTP/2, this is the stream ID.
struct
context
{};
}
// namespace caf::net::http
libcaf_net/caf/net/http/header.hpp
0 → 100644
View file @
d2830d80
// This file is part of CAF, the C++ Actor Framework. See the file LICENSE in
// the main distribution directory for license terms and copyright or visit
// https://github.com/actor-framework/actor-framework/blob/master/LICENSE.
#pragma once
#include "caf/config_value.hpp"
#include "caf/detail/net_export.hpp"
#include "caf/net/http/header_fields_map.hpp"
#include "caf/net/http/method.hpp"
#include "caf/net/http/status.hpp"
#include "caf/string_view.hpp"
#include "caf/uri.hpp"
#include <vector>
namespace
caf
::
net
::
http
{
class
CAF_NET_EXPORT
header
{
public:
header
()
=
default
;
header
(
header
&&
)
=
default
;
header
&
operator
=
(
header
&&
)
=
default
;
header
(
const
header
&
);
header
&
operator
=
(
const
header
&
);
void
assign
(
const
header
&
);
http
::
method
method
()
const
noexcept
{
return
method_
;
}
string_view
path
()
const
noexcept
{
return
uri_
.
path
();
}
const
uri
::
query_map
&
query
()
const
noexcept
{
return
uri_
.
query
();
}
string_view
fragment
()
const
noexcept
{
return
uri_
.
fragment
();
}
string_view
version
()
const
noexcept
{
return
version_
;
}
const
header_fields_map
&
fields
()
const
noexcept
{
return
fields_
;
}
string_view
field
(
string_view
key
)
const
noexcept
{
if
(
auto
i
=
fields_
.
find
(
key
);
i
!=
fields_
.
end
())
return
i
->
second
;
else
return
{};
}
template
<
class
T
>
optional
<
T
>
field_as
(
string_view
key
)
const
noexcept
{
if
(
auto
i
=
fields_
.
find
(
key
);
i
!=
fields_
.
end
())
{
caf
::
config_value
val
{
to_string
(
i
->
second
)};
if
(
auto
res
=
caf
::
get_as
<
T
>
(
val
))
return
std
::
move
(
*
res
);
else
return
{};
}
else
{
return
{};
}
}
bool
valid
()
const
noexcept
{
return
!
raw_
.
empty
();
}
std
::
pair
<
status
,
string_view
>
parse
(
string_view
raw
);
bool
chunked_transfer_encoding
()
const
;
optional
<
size_t
>
content_length
()
const
;
private:
std
::
vector
<
char
>
raw_
;
http
::
method
method_
;
uri
uri_
;
string_view
version_
;
header_fields_map
fields_
;
};
}
// namespace caf::net::http
libcaf_net/caf/net/http/header_fields_map.hpp
0 → 100644
View file @
d2830d80
// This file is part of CAF, the C++ Actor Framework. See the file LICENSE in
// the main distribution directory for license terms and copyright or visit
// https://github.com/actor-framework/actor-framework/blob/master/LICENSE.
#pragma once
#include "caf/detail/unordered_flat_map.hpp"
#include "caf/string_view.hpp"
namespace
caf
::
net
::
http
{
/// Convenience type alias for a key-value map storing header fields.
using
header_fields_map
=
detail
::
unordered_flat_map
<
string_view
,
string_view
>
;
}
// namespace caf::net::http
libcaf_net/caf/net/http/method.hpp
0 → 100644
View file @
d2830d80
// This file is part of CAF, the C++ Actor Framework. See the file LICENSE in
// the main distribution directory for license terms and copyright or visit
// https://github.com/actor-framework/actor-framework/blob/master/LICENSE.
#pragma once
#include "caf/default_enum_inspect.hpp"
#include "caf/detail/net_export.hpp"
#include "caf/string_view.hpp"
#include <cstdint>
#include <string>
#include <type_traits>
namespace
caf
::
net
::
http
{
/// Methods as defined by RFC 7231.
enum
class
method
:
uint8_t
{
/// Requests transfer of a current selected representation for the target
/// resource.
get
,
/// Identical to GET except that the server MUST NOT send a message body in
/// the response. The server SHOULD send the same header fields in response to
/// a HEAD request as it would have sent if the request had been a GET, except
/// that the payload header fields (Section 3.3) MAY be omitted.
head
,
/// Requests that the target resource process the representation enclosed in
/// the request according to the resource's own specific semantics.
post
,
/// Requests that the state of the target resource be created or replaced with
/// the state defined by the representation enclosed in the request message
/// payload.
put
,
/// Requests that the origin server remove the association between the target
/// resource and its current functionality.
del
,
/// Requests that the recipient establish a tunnel to the destination origin
/// server identified by the request-target and, if successful, thereafter
/// restrict its behavior to blind forwarding of packets, in both directions,
/// until the tunnel is closed.
connect
,
/// Requests information about the communication options available for the
/// target resource, at either the origin server or an intervening
/// intermediary.
options
,
/// Requests a remote, application-level loop-back of the request message.
trace
,
};
/// @relates method
CAF_NET_EXPORT
std
::
string
to_string
(
method
);
/// Converts @p x to the RFC string representation, i.e., all-uppercase.
/// @relates method
CAF_NET_EXPORT
std
::
string
to_rfc_string
(
method
x
);
/// @relates method
CAF_NET_EXPORT
bool
from_string
(
string_view
,
method
&
);
/// @relates method
CAF_NET_EXPORT
bool
from_integer
(
std
::
underlying_type_t
<
method
>
,
method
&
);
/// @relates method
template
<
class
Inspector
>
bool
inspect
(
Inspector
&
f
,
method
&
x
)
{
return
default_enum_inspect
(
f
,
x
);
}
}
// namespace caf::net::http
libcaf_net/caf/net/http/server.hpp
0 → 100644
View file @
d2830d80
// This file is part of CAF, the C++ Actor Framework. See the file LICENSE in
// the main distribution directory for license terms and copyright or visit
// https://github.com/actor-framework/actor-framework/blob/master/LICENSE.
#pragma once
#include "caf/byte_span.hpp"
#include "caf/detail/append_hex.hpp"
#include "caf/detail/unordered_flat_map.hpp"
#include "caf/error.hpp"
#include "caf/logger.hpp"
#include "caf/net/connection_acceptor.hpp"
#include "caf/net/fwd.hpp"
#include "caf/net/http/context.hpp"
#include "caf/net/http/header.hpp"
#include "caf/net/http/header_fields_map.hpp"
#include "caf/net/http/status.hpp"
#include "caf/net/http/v1.hpp"
#include "caf/net/hypertext_oriented_layer_ptr.hpp"
#include "caf/net/message_flow_bridge.hpp"
#include "caf/net/multiplexer.hpp"
#include "caf/net/receive_policy.hpp"
#include "caf/net/socket_manager.hpp"
#include "caf/pec.hpp"
#include "caf/settings.hpp"
#include "caf/tag/hypertext_oriented.hpp"
#include "caf/tag/stream_oriented.hpp"
#include <algorithm>
namespace
caf
::
net
::
http
{
/// Implements the server part for the HTTP Protocol as defined in RFC 7231.
template
<
class
UpperLayer
>
class
server
{
public:
// -- member types -----------------------------------------------------------
using
input_tag
=
tag
::
stream_oriented
;
using
output_tag
=
tag
::
hypertext_oriented
;
using
header_fields_type
=
header_fields_map
;
using
status_code_type
=
status
;
using
context_type
=
context
;
using
header_type
=
header
;
enum
class
mode
{
read_header
,
read_payload
,
read_chunks
,
};
// -- constants --------------------------------------------------------------
/// Default maximum size for incoming HTTP requests: 64KiB.
static
constexpr
uint32_t
default_max_request_size
=
65'536
;
// -- constructors, destructors, and assignment operators --------------------
template
<
class
...
Ts
>
explicit
server
(
Ts
&&
...
xs
)
:
upper_layer_
(
std
::
forward
<
Ts
>
(
xs
)...)
{
// nop
}
// -- initialization ---------------------------------------------------------
template
<
class
LowerLayerPtr
>
error
init
(
socket_manager
*
owner
,
LowerLayerPtr
down
,
const
settings
&
cfg
)
{
owner_
=
owner
;
if
(
auto
err
=
upper_layer_
.
init
(
owner
,
this_layer_ptr
(
down
),
cfg
))
return
err
;
if
(
auto
max_size
=
get_as
<
uint32_t
>
(
cfg
,
"http.max-request-size"
))
max_request_size_
=
*
max_size
;
down
->
configure_read
(
receive_policy
::
up_to
(
max_request_size_
));
return
none
;
}
// -- properties -------------------------------------------------------------
auto
&
upper_layer
()
noexcept
{
return
upper_layer_
;
}
const
auto
&
upper_layer
()
const
noexcept
{
return
upper_layer_
;
}
// -- interface for the upper layer ------------------------------------------
template
<
class
LowerLayerPtr
>
static
bool
can_send_more
(
LowerLayerPtr
down
)
noexcept
{
return
down
->
can_send_more
();
}
template
<
class
LowerLayerPtr
>
static
void
suspend_reading
(
LowerLayerPtr
down
)
{
down
->
configure_read
(
receive_policy
::
stop
());
}
template
<
class
LowerLayerPtr
>
static
auto
handle
(
LowerLayerPtr
down
)
noexcept
{
return
down
->
handle
();
}
template
<
class
LowerLayerPtr
>
bool
send_header
(
LowerLayerPtr
down
,
context
,
status
code
,
const
header_fields_map
&
fields
)
{
down
->
begin_output
();
v1
::
write_header
(
code
,
fields
,
down
->
output_buffer
());
down
->
end_output
();
return
true
;
}
template
<
class
LowerLayerPtr
>
bool
send_payload
(
LowerLayerPtr
down
,
context
,
const_byte_span
bytes
)
{
down
->
begin_output
();
auto
&
buf
=
down
->
output_buffer
();
buf
.
insert
(
buf
.
end
(),
bytes
.
begin
(),
bytes
.
end
());
down
->
end_output
();
return
true
;
}
template
<
class
LowerLayerPtr
>
bool
send_chunk
(
LowerLayerPtr
down
,
context
,
const_byte_span
bytes
)
{
down
->
begin_output
();
auto
&
buf
=
down
->
output_buffer
();
auto
size
=
bytes
.
size
();
detail
::
append_hex
(
buf
,
&
size
,
sizeof
(
size
));
buf
.
emplace_back
(
byte
{
'\r'
});
buf
.
emplace_back
(
byte
{
'\n'
});
buf
.
insert
(
buf
.
end
(),
bytes
.
begin
(),
bytes
.
end
());
buf
.
emplace_back
(
byte
{
'\r'
});
buf
.
emplace_back
(
byte
{
'\n'
});
return
down
->
end_output
();
}
template
<
class
LowerLayerPtr
>
bool
send_end_of_chunks
(
LowerLayerPtr
down
,
context
)
{
string_view
str
=
"0
\r\n\r\n
"
;
auto
bytes
=
as_bytes
(
make_span
(
str
));
down
->
begin_output
();
auto
&
buf
=
down
->
output_buffer
();
buf
.
insert
(
buf
.
end
(),
bytes
.
begin
(),
bytes
.
end
());
return
down
->
end_chunk
();
}
template
<
class
LowerLayerPtr
>
void
fin
(
LowerLayerPtr
,
context
)
{
// nop
}
template
<
class
LowerLayerPtr
>
static
void
abort_reason
(
LowerLayerPtr
down
,
error
reason
)
{
return
down
->
abort_reason
(
std
::
move
(
reason
));
}
template
<
class
LowerLayerPtr
>
static
const
error
&
abort_reason
(
LowerLayerPtr
down
)
{
return
down
->
abort_reason
();
}
// -- interface for the lower layer ------------------------------------------
template
<
class
LowerLayerPtr
>
bool
prepare_send
(
LowerLayerPtr
down
)
{
return
upper_layer_
.
prepare_send
(
this_layer_ptr
(
down
));
}
template
<
class
LowerLayerPtr
>
bool
done_sending
(
LowerLayerPtr
down
)
{
return
upper_layer_
.
prepare_send
(
this_layer_ptr
(
down
));
}
template
<
class
LowerLayerPtr
>
void
continue_reading
(
LowerLayerPtr
down
)
{
down
->
configure_read
(
receive_policy
::
up_to
(
max_request_size_
));
}
template
<
class
LowerLayerPtr
>
void
abort
(
LowerLayerPtr
down
,
const
error
&
reason
)
{
upper_layer_
.
abort
(
this_layer_ptr
(
down
),
reason
);
}
template
<
class
LowerLayerPtr
>
ptrdiff_t
consume
(
LowerLayerPtr
down
,
byte_span
input
,
byte_span
)
{
using
namespace
literals
;
CAF_LOG_TRACE
(
CAF_ARG2
(
"socket"
,
down
->
handle
().
id
)
<<
CAF_ARG2
(
"bytes"
,
input
.
size
()));
ptrdiff_t
consumed
=
0
;
for
(;;)
{
switch
(
mode_
)
{
case
mode
:
:
read_header
:
{
auto
[
hdr
,
remainder
]
=
v1
::
split_header
(
input
);
if
(
hdr
.
empty
())
{
if
(
input
.
size
()
>=
max_request_size_
)
{
write_response
(
down
,
status
::
request_header_fields_too_large
,
"Header exceeds maximum size."
);
auto
err
=
make_error
(
pec
::
too_many_characters
,
"exceeded maximum request size"
);
down
->
abort_reason
(
std
::
move
(
err
));
return
-
1
;
}
else
{
return
consumed
;
}
}
else
if
(
!
handle_header
(
down
,
hdr
))
{
return
-
1
;
}
else
{
// Prepare for next loop iteration.
consumed
+=
static_cast
<
ptrdiff_t
>
(
hdr
.
size
());
input
=
remainder
;
// Transition to the next mode.
if
(
hdr_
.
chunked_transfer_encoding
())
{
mode_
=
mode
::
read_chunks
;
}
else
if
(
auto
len
=
hdr_
.
content_length
())
{
// Protect against payloads that exceed the maximum size.
if
(
*
len
>=
max_request_size_
)
{
write_response
(
down
,
status
::
payload_too_large
,
"Payload exceeds maximum size."
);
auto
err
=
make_error
(
sec
::
invalid_argument
,
"exceeded maximum payload size"
);
down
->
abort_reason
(
std
::
move
(
err
));
return
-
1
;
}
// Transition to read_payload mode and continue.
payload_len_
=
*
len
;
mode_
=
mode
::
read_payload
;
}
else
{
// TODO: we may *still* have a payload since HTTP can omit the
// Content-Length field and simply close the connection
// after the payload.
if
(
!
invoke_upper_layer
(
down
,
const_byte_span
{}))
return
-
1
;
}
}
break
;
}
case
mode
:
:
read_payload
:
{
if
(
input
.
size
()
>=
payload_len_
)
{
if
(
!
invoke_upper_layer
(
down
,
input
.
subspan
(
0
,
payload_len_
)))
return
-
1
;
consumed
+=
static_cast
<
ptrdiff_t
>
(
payload_len_
);
mode_
=
mode
::
read_header
;
}
else
{
// Wait for more data.
return
consumed
;
}
break
;
}
case
mode
:
:
read_chunks
:
{
// TODO: implement me
write_response
(
down
,
status
::
not_implemented
,
"Chunked transfer not implemented yet."
);
auto
err
=
make_error
(
sec
::
invalid_argument
,
"exceeded maximum payload size"
);
down
->
abort_reason
(
std
::
move
(
err
));
return
-
1
;
}
}
}
}
private:
// -- implementation details -------------------------------------------------
template
<
class
LowerLayerPtr
>
auto
this_layer_ptr
(
LowerLayerPtr
down
)
{
return
make_hypertext_oriented_layer_ptr
(
this
,
down
);
}
template
<
class
LowerLayerPtr
>
void
write_response
(
LowerLayerPtr
down
,
status
code
,
string_view
content
)
{
down
->
begin_output
();
v1
::
write_response
(
code
,
"text/plain"
,
content
,
down
->
output_buffer
());
down
->
end_output
();
}
template
<
class
LowerLayerPtr
>
bool
invoke_upper_layer
(
LowerLayerPtr
down
,
const_byte_span
payload
)
{
if
(
!
upper_layer_
.
consume
(
this_layer_ptr
(
down
),
context
{},
hdr_
,
payload
))
{
if
(
!
down
->
abort_reason
())
{
down
->
abort_reason
(
make_error
(
sec
::
unsupported_operation
,
"requested method not implemented yet"
));
}
return
false
;
}
return
true
;
}
// -- HTTP request processing ------------------------------------------------
template
<
class
LowerLayerPtr
>
bool
handle_header
(
LowerLayerPtr
down
,
string_view
http
)
{
// Parse the header and reject invalid inputs.
auto
[
code
,
msg
]
=
hdr_
.
parse
(
http
);
if
(
code
!=
status
::
ok
)
{
write_response
(
down
,
code
,
msg
);
down
->
abort_reason
(
make_error
(
pec
::
invalid_argument
,
"malformed header"
));
return
false
;
}
else
{
return
true
;
}
}
/// Stores the upper layer.
UpperLayer
upper_layer_
;
/// Stores a pointer to the owning manager for the delayed initialization.
socket_manager
*
owner_
=
nullptr
;
/// Buffer for re-using memory.
header
hdr_
;
/// Stores whether we are currently waiting for the payload.
mode
mode_
=
mode
::
read_header
;
/// Stores the expected payload size when in read_payload mode.
size_t
payload_len_
=
0
;
/// Maximum size for incoming HTTP requests.
uint32_t
max_request_size_
=
default_max_request_size
;
};
/// Creates a WebSocket server on the connected socket `fd`.
/// @param mpx The multiplexer that takes ownership of the socket.
/// @param fd A connected stream socket.
/// @param in Inputs for writing to the socket.
/// @param out Outputs from the socket.
/// @param trait Converts between the native and the wire format.
template
<
template
<
class
>
class
Transport
=
stream_transport
,
class
Socket
,
class
T
,
class
Trait
>
socket_manager_ptr
make_server
(
multiplexer
&
mpx
,
Socket
fd
,
async
::
consumer_resource
<
T
>
in
,
async
::
producer_resource
<
T
>
out
,
Trait
trait
)
{
using
app_t
=
message_flow_bridge
<
T
,
Trait
,
tag
::
mixed_message_oriented
>
;
using
stack_t
=
Transport
<
server
<
app_t
>>
;
auto
mgr
=
make_socket_manager
<
stack_t
>
(
fd
,
&
mpx
,
std
::
move
(
in
),
std
::
move
(
out
),
std
::
move
(
trait
));
return
mgr
;
}
}
// namespace caf::net::http
libcaf_net/caf/net/http/status.hpp
0 → 100644
View file @
d2830d80
// This file is part of CAF, the C++ Actor Framework. See the file LICENSE in
// the main distribution directory for license terms and copyright or visit
// https://github.com/actor-framework/actor-framework/blob/master/LICENSE.
#pragma once
#include "caf/default_enum_inspect.hpp"
#include "caf/detail/net_export.hpp"
#include "caf/string_view.hpp"
#include <cstdint>
#include <string>
#include <type_traits>
namespace
caf
::
net
::
http
{
/// Status codes as defined by RFC 7231 and RFC 6585.
enum
class
status
:
uint16_t
{
/// Indicates that the initial part of a request has been received and has not
/// yet been rejected by the server. The server intends to send a final
/// response after the request has been fully received and acted upon.
continue_request
=
100
,
/// Indicates that the server understands and is willing to comply with the
/// client's request for a change in the application protocol being used on
/// this connection.
switching_protocols
=
101
,
/// Indicates that the request has succeeded.
ok
=
200
,
/// Indicates that the request has been fulfilled and has resulted in one or
/// more new resources being created.
created
=
201
,
/// Indicates that the request has been accepted for processing, but the
/// processing has not been completed.
accepted
=
202
,
/// Indicates that the request was successful but the enclosed payload has
/// been modified from that of the origin server's 200 (OK) response by a
/// transforming proxy
non_authoritative_information
=
203
,
/// Indicates that the server has successfully fulfilled the request and that
/// there is no additional content to send in the response payload body.
no_content
=
204
,
/// Indicates that the server has fulfilled the request and desires that the
/// user agent reset the "document view".
reset_content
=
205
,
/// Indicates that the server is successfully fulfilling a range request for
/// the target resource by transferring one or more parts of the selected
/// representation that correspond to the satisfiable ranges found in the
/// request's Range header field.
partial_content
=
206
,
/// Indicates that the target resource has more than one representation and
/// information about the alternatives is being provided so that the user (or
/// user agent) can select a preferred representation.
multiple_choices
=
300
,
/// Indicates that the target resource has been assigned a new permanent URI.
moved_permanently
=
301
,
/// Indicates that the target resource resides temporarily under a different
/// URI.
found
=
302
,
/// Indicates that the server is redirecting the user agent to a different
/// resource, as indicated by a URI in the Location header field, which is
/// intended to provide an indirect response to the original request.
see_other
=
303
,
/// Indicates that a conditional GET or HEAD request has been received and
/// would have resulted in a 200 (OK) response if it were not for the fact
/// that the condition evaluated to false.
not_modified
=
304
,
/// Deprecated.
use_proxy
=
305
,
/// No longer valid.
temporary_redirect
=
307
,
/// Indicates that the server cannot or will not process the request due to
/// something that is perceived to be a client error (e.g., malformed request
/// syntax, invalid request message framing, or deceptive request routing).
bad_request
=
400
,
/// Indicates that the request has not been applied because it lacks valid
/// authentication credentials for the target resource.
unauthorized
=
401
,
/// Reserved for future use.
payment_required
=
402
,
/// Indicates that the server understood the request but refuses to authorize
/// it.
forbidden
=
403
,
/// Indicates that the origin server did not find a current representation for
/// the target resource or is not willing to disclose that one exists.
not_found
=
404
,
/// Indicates that the method received in the request-line is known by the
/// origin server but not supported by the target resource.
method_not_allowed
=
405
,
/// Indicates that the target resource does not have a current representation
/// that would be acceptable to the user agent, according to the proactive
/// negotiation header fields received in the request (Section 5.3), and the
/// server is unwilling to supply a default representation.
not_acceptable
=
406
,
/// Similar to 401 (Unauthorized), but it indicates that the client needs to
/// authenticate itself in order to use a proxy.
proxy_authentication_required
=
407
,
/// Indicates that the server did not receive a complete request message
/// within the time that it was prepared to wait.
request_timeout
=
408
,
/// Indicates that the request could not be completed due to a conflict with
/// the current state of the target resource. This code is used in situations
/// where the user might be able to resolve the conflict and resubmit the
/// request.
conflict
=
409
,
/// Indicates that access to the target resource is no longer available at the
/// origin server and that this condition is likely to be permanent.
gone
=
410
,
/// Indicates that the server refuses to accept the request without a defined
/// Content-Length.
length_required
=
411
,
/// Indicates that one or more conditions given in the request header fields
/// evaluated to false when tested on the server.
precondition_failed
=
412
,
/// Indicates that the server is refusing to process a request because the
/// request payload is larger than the server is willing or able to process.
payload_too_large
=
413
,
/// Indicates that the server is refusing to service the request because the
/// request-target is longer than the server is willing to interpret.
uri_too_long
=
414
,
/// Indicates that the origin server is refusing to service the request
/// because the payload is in a format not supported by this method on the
/// target resource.
unsupported_media_type
=
415
,
/// Indicates that none of the ranges in the request's Range header field
/// overlap the current extent of the selected resource or that the set of
/// ranges requested has been rejected due to invalid ranges or an excessive
/// request of small or overlapping ranges.
range_not_satisfiable
=
416
,
/// Indicates that the expectation given in the request's Expect header field
/// could not be met by at least one of the inbound servers.
expectation_failed
=
417
,
/// Indicates that the server refuses to perform the request using the current
/// protocol but might be willing to do so after the client upgrades to a
/// different protocol.
upgrade_required
=
426
,
/// Indicates that the origin server requires the request to be conditional.
precondition_required
=
428
,
/// Indicates that the user has sent too many requests in a given amount of
/// time ("rate limiting").
too_many_requests
=
429
,
/// Indicates that the server is unwilling to process the request because its
/// header fields are too large.
request_header_fields_too_large
=
431
,
/// Indicates that the server encountered an unexpected condition that
/// prevented it from fulfilling the request.
internal_server_error
=
500
,
/// Indicates that the server does not support the functionality required to
/// fulfill the request.
not_implemented
=
501
,
/// Indicates that the server, while acting as a gateway or proxy, received an
/// invalid response from an inbound server it accessed while attempting to
/// fulfill the request.
bad_gateway
=
502
,
/// Indicates that the server is currently unable to handle the request due to
/// a temporary overload or scheduled maintenance, which will likely be
/// alleviated after some delay.
service_unavailable
=
503
,
/// Indicates that the server, while acting as a gateway or proxy, did not
/// receive a timely response from an upstream server it needed to access in
/// order to complete the request.
gateway_timeout
=
504
,
/// Indicates that the server does not support, or refuses to support, the
/// major version of HTTP that was used in the request message.
http_version_not_supported
=
505
,
/// Indicates that the client needs to authenticate to gain network access.
network_authentication_required
=
511
,
};
/// Returns the recommended response phrase to a status code.
/// @relates status
CAF_NET_EXPORT
string_view
phrase
(
status
);
/// @relates status
CAF_NET_EXPORT
std
::
string
to_string
(
status
);
/// @relates status
CAF_NET_EXPORT
bool
from_string
(
string_view
,
status
&
);
/// @relates status
CAF_NET_EXPORT
bool
from_integer
(
std
::
underlying_type_t
<
status
>
,
status
&
);
/// @relates status
template
<
class
Inspector
>
bool
inspect
(
Inspector
&
f
,
status
&
x
)
{
return
default_enum_inspect
(
f
,
x
);
}
}
// namespace caf::net::http
libcaf_net/caf/net/http/v1.hpp
0 → 100644
View file @
d2830d80
// This file is part of CAF, the C++ Actor Framework. See the file LICENSE in
// the main distribution directory for license terms and copyright or visit
// https://github.com/actor-framework/actor-framework/blob/master/LICENSE.
#pragma once
#include "caf/byte_span.hpp"
#include "caf/detail/net_export.hpp"
#include "caf/net/http/header_fields_map.hpp"
#include "caf/net/http/status.hpp"
#include "caf/string_view.hpp"
#include <utility>
namespace
caf
::
net
::
http
::
v1
{
/// Tries splitting the given byte span into an HTTP header (`first`) and a
/// remainder (`second`). Returns an empty @ref string_view as `first` for
/// incomplete HTTP headers.
CAF_NET_EXPORT
std
::
pair
<
string_view
,
byte_span
>
split_header
(
byte_span
bytes
);
/// Writes an HTTP header to the buffer.
CAF_NET_EXPORT
void
write_header
(
status
code
,
const
header_fields_map
&
fields
,
byte_buffer
&
buf
);
/// Writes a complete HTTP response to the buffer. Automatically sets
/// Content-Type and Content-Length header fields.
CAF_NET_EXPORT
void
write_response
(
status
code
,
string_view
content_type
,
string_view
content
,
byte_buffer
&
buf
);
/// Writes a complete HTTP response to the buffer. Automatically sets
/// Content-Type and Content-Length header fields followed by the user-defined
/// @p fields.
CAF_NET_EXPORT
void
write_response
(
status
code
,
string_view
content_type
,
string_view
content
,
const
header_fields_map
&
fields
,
byte_buffer
&
buf
);
}
// namespace caf::net::http::v1
libcaf_net/caf/net/hypertext_oriented_layer_ptr.hpp
0 → 100644
View file @
d2830d80
// This file is part of CAF, the C++ Actor Framework. See the file LICENSE in
// the main distribution directory for license terms and copyright or visit
// https://github.com/actor-framework/actor-framework/blob/master/LICENSE.
#pragma once
#include "caf/byte_span.hpp"
#include "caf/error.hpp"
#include "caf/fwd.hpp"
#include "caf/net/fwd.hpp"
namespace
caf
::
net
{
/// Wraps a pointer to a hypertext-oriented layer with a pointer to its lower
/// layer. Both pointers are then used to implement the interface required for a
/// hypertext-oriented layer when calling into its upper layer.
template
<
class
Layer
,
class
LowerLayerPtr
>
class
hypertext_oriented_layer_ptr
{
public:
using
context_type
=
typename
Layer
::
context_type
;
using
status_code_type
=
typename
Layer
::
status_code_type
;
using
header_fields_type
=
typename
Layer
::
header_fields_type
;
class
access
{
public:
access
(
Layer
*
layer
,
LowerLayerPtr
down
)
:
lptr_
(
layer
),
llptr_
(
down
)
{
// nop
}
/// Queries whether the underlying transport can send additional data.
bool
can_send_more
()
const
noexcept
{
return
lptr_
->
can_send_more
(
llptr_
);
}
/// Asks the underlying transport to stop receiving additional data until
/// resumed.
void
suspend_reading
()
{
return
lptr_
->
suspend_reading
(
llptr_
);
}
/// Returns the socket handle.
auto
handle
()
const
noexcept
{
return
lptr_
->
handle
(
llptr_
);
}
/// Sends a response header for answering the request identified by the
/// context.
/// @param context Identifies which request this response belongs to.
/// @param code Indicates either success or failure to the client.
/// @param fields Various informational fields for the client. When sending
/// a payload afterwards, the fields should at least include
/// the content length.
bool
send_header
(
context_type
context
,
status_code_type
code
,
const
header_fields_type
&
fields
)
{
return
lptr_
->
send_header
(
llptr_
,
context
,
code
,
fields
);
}
/// Sends a payload to the client. Must follow a header.
/// @param context Identifies which request this response belongs to.
/// @param bytes Arbitrary data for the client.
/// @pre `bytes.size() > 0`
[[
nodiscard
]]
bool
send_payload
(
context_type
context
,
const_byte_span
bytes
)
{
return
lptr_
->
send_payload
(
llptr_
,
context
,
bytes
);
}
/// Sends a single chunk of arbitrary data. The chunks must follow a header.
/// @pre `bytes.size() > 0`
[[
nodiscard
]]
bool
send_chunk
(
context_type
context
,
const_byte_span
bytes
)
{
return
lptr_
->
send_chunk
(
llptr_
,
context
,
bytes
);
}
/// Informs the client that the transfer completed, i.e., that the server
/// will not send additional chunks.
[[
nodiscard
]]
bool
send_end_of_chunks
(
context_type
context
)
{
return
lptr_
->
send_end_of_chunks
(
llptr_
,
context
);
}
/// Convenience function for completing a request 'raw' (without adding
/// additional header fields) in a single function call. Calls
/// `send_header`, `send_payload` and `fin`.
bool
send_raw_response
(
context_type
context
,
status_code_type
code
,
const
header_fields_type
&
fields
,
const_byte_span
content
)
{
if
(
lptr_
->
send_header
(
llptr_
,
context
,
code
,
fields
)
&&
(
content
.
empty
()
||
lptr_
->
send_payload
(
llptr_
,
context
,
content
)))
{
lptr_
->
fin
(
llptr_
,
context
);
return
true
;
}
else
{
return
false
;
}
}
/// Convenience function for completing a request in a single function call.
/// Automatically sets the header fields 'Content-Type' and
/// 'Content-Length'. Calls `send_header`, `send_payload` and `fin`.
bool
send_response
(
context_type
context
,
status_code_type
code
,
header_fields_type
fields
,
string_view
content_type
,
const_byte_span
content
)
{
std
::
string
len
;
if
(
!
content
.
empty
())
{
auto
len
=
std
::
to_string
(
content
.
size
());
fields
.
emplace
(
"Content-Length"
,
len
);
fields
.
emplace
(
"Content-Type"
,
content_type
);
}
return
send_raw_response
(
context
,
code
,
fields
,
content
);
}
/// Convenience function for completing a request in a single function call.
/// Automatically sets the header fields 'Content-Type' and
/// 'Content-Length'. Calls `send_header`, `send_payload` and `fin`.
bool
send_response
(
context_type
context
,
status_code_type
code
,
string_view
content_type
,
const_byte_span
content
)
{
std
::
string
len
;
header_fields_type
fields
;
if
(
!
content
.
empty
())
{
auto
len
=
std
::
to_string
(
content
.
size
());
fields
.
emplace
(
"Content-Type"
,
content_type
);
fields
.
emplace
(
"Content-Length"
,
len
);
}
return
send_raw_response
(
context
,
code
,
fields
,
content
);
}
bool
send_response
(
context_type
context
,
status_code_type
code
,
header_fields_type
fields
,
string_view
content_type
,
string_view
content
)
{
return
send_response
(
context
,
code
,
content_type
,
std
::
move
(
fields
),
as_bytes
(
make_span
(
content
)));
}
bool
send_response
(
context_type
context
,
status_code_type
code
,
string_view
content_type
,
string_view
content
)
{
return
send_response
(
context
,
code
,
content_type
,
as_bytes
(
make_span
(
content
)));
}
void
fin
(
context_type
context
)
{
lptr_
->
fin
(
llptr_
,
context
);
}
/// Sets an abort reason on the transport.
void
abort_reason
(
error
reason
)
{
return
lptr_
->
abort_reason
(
llptr_
,
std
::
move
(
reason
));
}
/// Returns the current abort reason on the transport or a
/// default-constructed error is no error occurred yet.
const
error
&
abort_reason
()
{
return
lptr_
->
abort_reason
(
llptr_
);
}
private:
Layer
*
lptr_
;
LowerLayerPtr
llptr_
;
};
hypertext_oriented_layer_ptr
(
Layer
*
layer
,
LowerLayerPtr
down
)
:
access_
(
layer
,
down
)
{
// nop
}
hypertext_oriented_layer_ptr
(
const
hypertext_oriented_layer_ptr
&
)
=
default
;
explicit
operator
bool
()
const
noexcept
{
return
true
;
}
access
*
operator
->
()
const
noexcept
{
return
&
access_
;
}
access
&
operator
*
()
const
noexcept
{
return
access_
;
}
private:
mutable
access
access_
;
};
template
<
class
Layer
,
class
LowerLayerPtr
>
auto
make_hypertext_oriented_layer_ptr
(
Layer
*
this_layer
,
LowerLayerPtr
down
)
{
using
result_t
=
hypertext_oriented_layer_ptr
<
Layer
,
LowerLayerPtr
>
;
return
result_t
{
this_layer
,
down
};
}
}
// namespace caf::net
libcaf_net/caf/net/length_prefix_framing.hpp
View file @
d2830d80
...
@@ -111,6 +111,7 @@ public:
...
@@ -111,6 +111,7 @@ public:
if
(
msg_size
>
0
&&
static_cast
<
size_t
>
(
msg_size
)
<
max_message_length
)
{
if
(
msg_size
>
0
&&
static_cast
<
size_t
>
(
msg_size
)
<
max_message_length
)
{
auto
u32_size
=
to_network_order
(
static_cast
<
uint32_t
>
(
msg_size
));
auto
u32_size
=
to_network_order
(
static_cast
<
uint32_t
>
(
msg_size
));
memcpy
(
std
::
addressof
(
*
msg_begin
),
&
u32_size
,
4
);
memcpy
(
std
::
addressof
(
*
msg_begin
),
&
u32_size
,
4
);
down
->
end_output
();
return
true
;
return
true
;
}
else
{
}
else
{
auto
err
=
make_error
(
sec
::
runtime_error
,
auto
err
=
make_error
(
sec
::
runtime_error
,
...
@@ -256,9 +257,9 @@ error run_with_length_prefix_framing(multiplexer& mpx, Socket fd,
...
@@ -256,9 +257,9 @@ error run_with_length_prefix_framing(multiplexer& mpx, Socket fd,
async
::
consumer_resource
<
T
>
in
,
async
::
consumer_resource
<
T
>
in
,
async
::
producer_resource
<
T
>
out
,
async
::
producer_resource
<
T
>
out
,
Trait
trait
)
{
Trait
trait
)
{
using
app_t
=
length_prefix_framing
<
message_flow_bridge
<
T
,
Trait
>>
;
using
app_t
=
Transport
<
length_prefix_framing
<
message_flow_bridge
<
T
,
Trait
>
>>
;
auto
mgr
=
make_socket_manager
<
app_t
,
Transport
>
(
fd
,
&
mpx
,
std
::
move
(
trait
));
auto
mgr
=
make_socket_manager
<
app_t
>
(
fd
,
&
mpx
,
std
::
move
(
in
),
std
::
move
(
out
),
mgr
->
top_layer
().
connect_flows
(
mgr
.
get
(),
std
::
move
(
in
),
std
::
move
(
ou
t
));
std
::
move
(
trai
t
));
return
mgr
->
init
(
cfg
);
return
mgr
->
init
(
cfg
);
}
}
...
...
libcaf_net/caf/net/message_flow_bridge.hpp
View file @
d2830d80
...
@@ -41,29 +41,36 @@ public:
...
@@ -41,29 +41,36 @@ public:
using
producer_resource_t
=
async
::
producer_resource
<
T
>
;
using
producer_resource_t
=
async
::
producer_resource
<
T
>
;
explicit
message_flow_bridge
(
Trait
trait
)
:
trait_
(
std
::
move
(
trait
))
{
message_flow_bridge
(
consumer_resource_t
in_res
,
producer_resource_t
out_res
,
Trait
trait
)
:
in_res_
(
std
::
move
(
in_res
)),
out_res_
(
std
::
move
(
out_res
)),
trait_
(
std
::
move
(
trait
))
{
// nop
// nop
}
}
void
connect_flows
(
net
::
socket_manager
*
mgr
,
consumer_resource_t
in
,
explicit
message_flow_bridge
(
Trait
trait
)
:
trait_
(
std
::
move
(
trait
))
{
producer_resource_t
out
)
{
// nop
in_
=
consumer_adapter
<
buffer_type
>::
try_open
(
mgr
,
in
);
out_
=
producer_adapter
<
buffer_type
>::
try_open
(
mgr
,
out
);
}
}
template
<
class
LowerLayerPtr
>
template
<
class
LowerLayerPtr
>
error
error
init
(
net
::
socket_manager
*
mgr
,
LowerLayerPtr
,
const
settings
&
cfg
)
{
init
(
net
::
socket_manager
*
mgr
,
LowerLayerPtr
down
,
const
settings
&
cfg
)
{
mgr_
=
mgr
;
mgr_
=
mgr
;
if
constexpr
(
caf
::
detail
::
has_init_v
<
Trait
>
)
{
if
constexpr
(
caf
::
detail
::
has_init_v
<
Trait
>
)
{
if
(
auto
err
=
init_res
(
trait_
.
init
(
cfg
)))
if
(
auto
err
=
init_res
(
trait_
.
init
(
cfg
)))
return
err
;
return
err
;
}
}
if
(
in_res_
)
{
in_
=
consumer_adapter
<
buffer_type
>::
try_open
(
mgr
,
in_res_
);
in_res_
=
nullptr
;
}
if
(
out_res_
)
{
out_
=
producer_adapter
<
buffer_type
>::
try_open
(
mgr
,
out_res_
);
out_res_
=
nullptr
;
}
if
(
!
in_
&&
!
out_
)
if
(
!
in_
&&
!
out_
)
return
make_error
(
sec
::
cannot_open_resource
,
return
make_error
(
sec
::
cannot_open_resource
,
"flow bridge cannot run without at least one resource"
);
"flow bridge cannot run without at least one resource"
);
if
(
!
out_
)
down
->
suspend_reading
();
return
none
;
return
none
;
}
}
...
@@ -77,12 +84,12 @@ public:
...
@@ -77,12 +84,12 @@ public:
static_assert
(
std
::
is_same_v
<
Tag
,
tag
::
mixed_message_oriented
>
);
static_assert
(
std
::
is_same_v
<
Tag
,
tag
::
mixed_message_oriented
>
);
if
(
trait_
.
converts_to_binary
(
item
))
{
if
(
trait_
.
converts_to_binary
(
item
))
{
down
->
begin_binary_message
();
down
->
begin_binary_message
();
auto
&
b
uf
=
down
->
binary_message_buffer
();
auto
&
b
ytes
=
down
->
binary_message_buffer
();
return
trait_
.
convert
(
item
,
b
uf
)
&&
down
->
end_binary_message
();
return
trait_
.
convert
(
item
,
b
ytes
)
&&
down
->
end_binary_message
();
}
else
{
}
else
{
down
->
begin_text_message
();
down
->
begin_text_message
();
auto
&
buf
=
down
->
text_message_buffer
();
auto
&
text
=
down
->
text_message_buffer
();
return
trait_
.
convert
(
item
,
buf
)
&&
down
->
end_text_message
();
return
trait_
.
convert
(
item
,
text
)
&&
down
->
end_text_message
();
}
}
}
}
}
}
...
@@ -208,7 +215,8 @@ private:
...
@@ -208,7 +215,8 @@ private:
}
}
error
init_res
(
consumer_resource_t
in
,
producer_resource_t
out
)
{
error
init_res
(
consumer_resource_t
in
,
producer_resource_t
out
)
{
connect_flows
(
mgr_
,
std
::
move
(
in
),
std
::
move
(
out
));
in_res_
=
std
::
move
(
in
);
out_res_
=
std
::
move
(
out
);
return
caf
::
none
;
return
caf
::
none
;
}
}
...
@@ -241,6 +249,12 @@ private:
...
@@ -241,6 +249,12 @@ private:
/// Converts between raw bytes and items.
/// Converts between raw bytes and items.
Trait
trait_
;
Trait
trait_
;
/// Discarded after initialization.
consumer_resource_t
in_res_
;
/// Discarded after initialization.
producer_resource_t
out_res_
;
};
};
}
// namespace caf::net
}
// namespace caf::net
libcaf_net/caf/net/message_oriented_layer_ptr.hpp
View file @
d2830d80
...
@@ -10,9 +10,9 @@
...
@@ -10,9 +10,9 @@
namespace
caf
::
net
{
namespace
caf
::
net
{
/// Wraps a pointer to a m
ixed-message-oriented layer with a pointer to its
/// Wraps a pointer to a m
essage-oriented layer with a pointer to its lower
/// l
ower layer. Both pointers are then used to implement the interface required
/// l
ayer. Both pointers are then used to implement the interface required for a
///
for a mixed-
message-oriented layer when calling into its upper layer.
/// message-oriented layer when calling into its upper layer.
template
<
class
Layer
,
class
LowerLayerPtr
>
template
<
class
Layer
,
class
LowerLayerPtr
>
class
message_oriented_layer_ptr
{
class
message_oriented_layer_ptr
{
public:
public:
...
...
libcaf_net/caf/net/socket_manager.hpp
View file @
d2830d80
...
@@ -18,6 +18,7 @@
...
@@ -18,6 +18,7 @@
#include "caf/net/socket.hpp"
#include "caf/net/socket.hpp"
#include "caf/net/typed_actor_shell.hpp"
#include "caf/net/typed_actor_shell.hpp"
#include "caf/ref_counted.hpp"
#include "caf/ref_counted.hpp"
#include "caf/sec.hpp"
#include "caf/tag/io_event_oriented.hpp"
#include "caf/tag/io_event_oriented.hpp"
namespace
caf
::
net
{
namespace
caf
::
net
{
...
...
libcaf_net/caf/net/stream_transport.hpp
View file @
d2830d80
...
@@ -43,10 +43,6 @@ public:
...
@@ -43,10 +43,6 @@ public:
// nop
// nop
}
}
virtual
~
stream_transport
()
{
// nop
}
// -- interface for stream_oriented_layer_ptr --------------------------------
// -- interface for stream_oriented_layer_ptr --------------------------------
template
<
class
ParentPtr
>
template
<
class
ParentPtr
>
...
...
libcaf_net/caf/net/web_socket/client.hpp
View file @
d2830d80
...
@@ -10,6 +10,7 @@
...
@@ -10,6 +10,7 @@
#include "caf/hash/sha1.hpp"
#include "caf/hash/sha1.hpp"
#include "caf/logger.hpp"
#include "caf/logger.hpp"
#include "caf/net/fwd.hpp"
#include "caf/net/fwd.hpp"
#include "caf/net/http/v1.hpp"
#include "caf/net/message_flow_bridge.hpp"
#include "caf/net/message_flow_bridge.hpp"
#include "caf/net/receive_policy.hpp"
#include "caf/net/receive_policy.hpp"
#include "caf/net/web_socket/framing.hpp"
#include "caf/net/web_socket/framing.hpp"
...
@@ -106,12 +107,10 @@ public:
...
@@ -106,12 +107,10 @@ public:
return
upper_layer_
.
consume
(
down
,
input
,
delta
);
return
upper_layer_
.
consume
(
down
,
input
,
delta
);
// Check whether received a HTTP header or else wait for more data or abort
// Check whether received a HTTP header or else wait for more data or abort
// when exceeding the maximum size.
// when exceeding the maximum size.
auto
[
hdr
,
remainder
]
=
h
andshake
::
split_http_1
_header
(
input
);
auto
[
hdr
,
remainder
]
=
h
ttp
::
v1
::
split
_header
(
input
);
if
(
hdr
.
empty
())
{
if
(
hdr
.
empty
())
{
if
(
input
.
size
()
>=
handshake
::
max_http_size
)
{
if
(
input
.
size
()
>=
handshake
::
max_http_size
)
{
down
->
begin_output
();
CAF_LOG_ERROR
(
"server response exceeded maximum header size"
);
handshake
::
write_http_1_header_too_large
(
down
->
output_buffer
());
down
->
end_output
();
auto
err
=
make_error
(
pec
::
too_many_characters
,
auto
err
=
make_error
(
pec
::
too_many_characters
,
"exceeded maximum header size"
);
"exceeded maximum header size"
);
down
->
abort_reason
(
std
::
move
(
err
));
down
->
abort_reason
(
std
::
move
(
err
));
...
@@ -188,8 +187,8 @@ void run_client(multiplexer& mpx, Socket fd, handshake hs,
...
@@ -188,8 +187,8 @@ void run_client(multiplexer& mpx, Socket fd, handshake hs,
using
app_t
=
message_flow_bridge
<
T
,
Trait
,
tag
::
mixed_message_oriented
>
;
using
app_t
=
message_flow_bridge
<
T
,
Trait
,
tag
::
mixed_message_oriented
>
;
using
stack_t
=
Transport
<
client
<
app_t
>>
;
using
stack_t
=
Transport
<
client
<
app_t
>>
;
auto
mgr
=
make_socket_manager
<
stack_t
>
(
fd
,
&
mpx
,
std
::
move
(
hs
),
auto
mgr
=
make_socket_manager
<
stack_t
>
(
fd
,
&
mpx
,
std
::
move
(
hs
),
std
::
move
(
in
),
std
::
move
(
out
),
std
::
move
(
trait
));
std
::
move
(
trait
));
mgr
->
top_layer
().
connect_flows
(
mgr
.
get
(),
std
::
move
(
in
),
std
::
move
(
out
));
mpx
.
init
(
mgr
);
mpx
.
init
(
mgr
);
}
}
...
...
libcaf_net/caf/net/web_socket/handshake.hpp
View file @
d2830d80
...
@@ -125,13 +125,6 @@ public:
...
@@ -125,13 +125,6 @@ public:
/// @pre `has_valid_key()`
/// @pre `has_valid_key()`
void
write_http_1_response
(
byte_buffer
&
buf
)
const
;
void
write_http_1_response
(
byte_buffer
&
buf
)
const
;
/// Writes an HTTP 1.1 'Bad Request' error to `buf` with `descr` providing
/// additional information to the client.
static
void
write_http_1_bad_request
(
byte_buffer
&
buf
,
string_view
descr
);
/// Writes a HTTP 1.1 431 (Request Header Fields Too Large) response.
static
void
write_http_1_header_too_large
(
byte_buffer
&
buf
);
/// Checks whether the `http_response` contains a HTTP 1.1 response to the
/// Checks whether the `http_response` contains a HTTP 1.1 response to the
/// generated HTTP GET request. A valid response contains:
/// generated HTTP GET request. A valid response contains:
/// - HTTP status code 101 (Switching Protocols).
/// - HTTP status code 101 (Switching Protocols).
...
@@ -140,11 +133,6 @@ public:
...
@@ -140,11 +133,6 @@ public:
/// - A `Sec-WebSocket-Accept` field with the value `response_key()`.
/// - A `Sec-WebSocket-Accept` field with the value `response_key()`.
bool
is_valid_http_1_response
(
string_view
http_response
)
const
;
bool
is_valid_http_1_response
(
string_view
http_response
)
const
;
/// Tries splitting the given byte span into an HTTP header (`first`) and a
/// remainder (`second`). Returns an empty @ref string_view as `first` for
/// incomplete HTTP headers.
static
std
::
pair
<
string_view
,
byte_span
>
split_http_1_header
(
byte_span
bytes
);
private:
private:
// -- utility ----------------------------------------------------------------
// -- utility ----------------------------------------------------------------
...
...
libcaf_net/caf/net/web_socket/server.hpp
View file @
d2830d80
...
@@ -9,6 +9,10 @@
...
@@ -9,6 +9,10 @@
#include "caf/logger.hpp"
#include "caf/logger.hpp"
#include "caf/net/connection_acceptor.hpp"
#include "caf/net/connection_acceptor.hpp"
#include "caf/net/fwd.hpp"
#include "caf/net/fwd.hpp"
#include "caf/net/http/header.hpp"
#include "caf/net/http/method.hpp"
#include "caf/net/http/status.hpp"
#include "caf/net/http/v1.hpp"
#include "caf/net/message_flow_bridge.hpp"
#include "caf/net/message_flow_bridge.hpp"
#include "caf/net/multiplexer.hpp"
#include "caf/net/multiplexer.hpp"
#include "caf/net/receive_policy.hpp"
#include "caf/net/receive_policy.hpp"
...
@@ -92,18 +96,22 @@ public:
...
@@ -92,18 +96,22 @@ public:
template
<
class
LowerLayerPtr
>
template
<
class
LowerLayerPtr
>
ptrdiff_t
consume
(
LowerLayerPtr
down
,
byte_span
input
,
byte_span
delta
)
{
ptrdiff_t
consume
(
LowerLayerPtr
down
,
byte_span
input
,
byte_span
delta
)
{
using
namespace
caf
::
literals
;
CAF_LOG_TRACE
(
CAF_ARG2
(
"socket"
,
down
->
handle
().
id
)
CAF_LOG_TRACE
(
CAF_ARG2
(
"socket"
,
down
->
handle
().
id
)
<<
CAF_ARG2
(
"bytes"
,
input
.
size
()));
<<
CAF_ARG2
(
"bytes"
,
input
.
size
()));
// Short circuit to the framing layer after the handshake completed.
// Short circuit to the framing layer after the handshake completed.
if
(
handshake_complete_
)
if
(
handshake_complete_
)
return
upper_layer_
.
consume
(
down
,
input
,
delta
);
return
upper_layer_
.
consume
(
down
,
input
,
delta
);
// Check whether
received a HTTP header or else wait for more data or abort
// Check whether
we received an HTTP header or else wait for more data.
// when exceeding the maximum size.
//
Abort
when exceeding the maximum size.
auto
[
hdr
,
remainder
]
=
h
andshake
::
split_http_1
_header
(
input
);
auto
[
hdr
,
remainder
]
=
h
ttp
::
v1
::
split
_header
(
input
);
if
(
hdr
.
empty
())
{
if
(
hdr
.
empty
())
{
if
(
input
.
size
()
>=
handshake
::
max_http_size
)
{
if
(
input
.
size
()
>=
handshake
::
max_http_size
)
{
down
->
begin_output
();
down
->
begin_output
();
handshake
::
write_http_1_header_too_large
(
down
->
output_buffer
());
http
::
v1
::
write_response
(
http
::
status
::
request_header_fields_too_large
,
"text/plain"
_sv
,
"Header exceeds maximum size."
_sv
,
down
->
output_buffer
());
down
->
end_output
();
down
->
end_output
();
auto
err
=
make_error
(
pec
::
too_many_characters
,
auto
err
=
make_error
(
pec
::
too_many_characters
,
"exceeded maximum header size"
);
"exceeded maximum header size"
);
...
@@ -136,92 +144,68 @@ public:
...
@@ -136,92 +144,68 @@ public:
private:
private:
// -- HTTP request processing ------------------------------------------------
// -- HTTP request processing ------------------------------------------------
template
<
class
LowerLayerPtr
>
void
write_response
(
LowerLayerPtr
down
,
http
::
status
code
,
string_view
msg
)
{
down
->
begin_output
();
http
::
v1
::
write_response
(
code
,
"text/plain"
,
msg
,
down
->
output_buffer
());
down
->
end_output
();
}
template
<
class
LowerLayerPtr
>
template
<
class
LowerLayerPtr
>
bool
handle_header
(
LowerLayerPtr
down
,
string_view
http
)
{
bool
handle_header
(
LowerLayerPtr
down
,
string_view
http
)
{
using
namespace
std
::
literals
;
using
namespace
std
::
literals
;
// Parse the first line, i.e., "METHOD REQUEST-URI VERSION".
// Parse the header and reject invalid inputs.
auto
[
first_line
,
remainder
]
=
split
(
http
,
"
\r\n
"
);
http
::
header
hdr
;
auto
[
method
,
request_uri_str
,
version
]
=
split2
(
first_line
,
" "
);
auto
[
code
,
msg
]
=
hdr
.
parse
(
http
);
auto
&
hdr
=
cfg_
[
"web-socket"
].
as_dictionary
();
if
(
code
!=
http
::
status
::
ok
)
{
if
(
method
!=
"GET"
)
{
write_response
(
down
,
code
,
msg
);
down
->
begin_output
();
down
->
abort_reason
(
make_error
(
pec
::
invalid_argument
,
"malformed header"
));
handshake
::
write_http_1_bad_request
(
down
->
output_buffer
(),
"Expected WebSocket handshake."
);
down
->
end_output
();
auto
err
=
make_error
(
pec
::
invalid_argument
,
"invalid operation: expected GET, got "
+
to_string
(
method
));
down
->
abort_reason
(
std
::
move
(
err
));
return
false
;
return
false
;
}
}
// The path must be absolute.
if
(
hdr
.
method
()
!=
http
::
method
::
get
)
{
if
(
request_uri_str
.
empty
()
||
request_uri_str
.
front
()
!=
'/'
)
{
write_response
(
down
,
http
::
status
::
bad_request
,
auto
descr
=
"Malformed Request-URI path: expected absolute path."
s
;
"Expected a WebSocket handshake."
);
down
->
begin_output
();
auto
err
=
make_error
(
pec
::
invalid_argument
,
handshake
::
write_http_1_bad_request
(
down
->
output_buffer
(),
descr
);
"invalid operation: expected method get, got "
down
->
end_output
();
+
to_string
(
hdr
.
method
()));
down
->
abort_reason
(
make_error
(
pec
::
invalid_argument
,
std
::
move
(
descr
)));
down
->
abort_reason
(
std
::
move
(
err
));
return
false
;
}
// The path must form a valid URI when prefixing a scheme. We don't actually
// care about the scheme, so just use "foo" here for the validation step.
uri
request_uri
;
if
(
auto
res
=
make_uri
(
"foo://localhost"
+
to_string
(
request_uri_str
)))
{
request_uri
=
std
::
move
(
*
res
);
}
else
{
auto
descr
=
"Malformed Request-URI path: "
+
to_string
(
res
.
error
());
descr
+=
'.'
;
down
->
begin_output
();
handshake
::
write_http_1_bad_request
(
down
->
output_buffer
(),
descr
);
down
->
end_output
();
down
->
abort_reason
(
make_error
(
pec
::
invalid_argument
,
std
::
move
(
descr
)));
return
false
;
return
false
;
}
}
// Store the request information in the settings for the upper layer.
put
(
hdr
,
"method"
,
method
);
put
(
hdr
,
"path"
,
request_uri
.
path
());
put
(
hdr
,
"query"
,
request_uri
.
query
());
put
(
hdr
,
"fragment"
,
request_uri
.
fragment
());
put
(
hdr
,
"http-version"
,
version
);
// Store the remaining header fields.
auto
&
fields
=
hdr
[
"fields"
].
as_dictionary
();
for_each_line
(
remainder
,
[
&
fields
](
string_view
line
)
{
if
(
auto
sep
=
std
::
find
(
line
.
begin
(),
line
.
end
(),
':'
);
sep
!=
line
.
end
())
{
auto
key
=
trim
({
line
.
begin
(),
sep
});
auto
val
=
trim
({
sep
+
1
,
line
.
end
()});
if
(
!
key
.
empty
())
put
(
fields
,
key
,
val
);
}
});
// Check whether the mandatory fields exist.
// Check whether the mandatory fields exist.
handshake
hs
;
auto
sec_key
=
hdr
.
field
(
"Sec-WebSocket-Key"
);
if
(
auto
skey_field
=
get_if
<
std
::
string
>
(
&
fields
,
"Sec-WebSocket-Key"
);
if
(
sec_key
.
empty
())
{
skey_field
&&
hs
.
assign_key
(
*
skey_field
))
{
CAF_LOG_DEBUG
(
"received Sec-WebSocket-Key"
<<
*
skey_field
);
}
else
{
auto
descr
=
"Mandatory field Sec-WebSocket-Key missing or invalid."
s
;
auto
descr
=
"Mandatory field Sec-WebSocket-Key missing or invalid."
s
;
down
->
begin_output
();
write_response
(
down
,
http
::
status
::
bad_request
,
descr
);
handshake
::
write_http_1_bad_request
(
down
->
output_buffer
(),
descr
);
down
->
end_output
();
CAF_LOG_DEBUG
(
"received invalid WebSocket handshake"
);
CAF_LOG_DEBUG
(
"received invalid WebSocket handshake"
);
down
->
abort_reason
(
make_error
(
pec
::
missing_field
,
std
::
move
(
descr
)));
down
->
abort_reason
(
make_error
(
pec
::
missing_field
,
std
::
move
(
descr
)));
return
false
;
return
false
;
}
}
// Try initializing the upper layer.
// Store the request information in the settings for the upper layer.
auto
&
ws
=
cfg_
[
"web-socket"
].
as_dictionary
();
put
(
ws
,
"method"
,
to_rfc_string
(
hdr
.
method
()));
put
(
ws
,
"path"
,
"/"
+
to_string
(
hdr
.
path
()));
put
(
ws
,
"query"
,
hdr
.
query
());
put
(
ws
,
"fragment"
,
hdr
.
fragment
());
put
(
ws
,
"http-version"
,
hdr
.
version
());
if
(
!
hdr
.
fields
().
empty
())
{
auto
&
fields
=
ws
[
"fields"
].
as_dictionary
();
for
(
auto
&
[
key
,
val
]
:
hdr
.
fields
())
put
(
fields
,
to_string
(
key
),
to_string
(
val
));
}
// Try to initialize the upper layer.
if
(
auto
err
=
upper_layer_
.
init
(
owner_
,
down
,
cfg_
))
{
if
(
auto
err
=
upper_layer_
.
init
(
owner_
,
down
,
cfg_
))
{
auto
descr
=
to_string
(
err
);
auto
descr
=
to_string
(
err
);
down
->
begin_output
();
CAF_LOG_DEBUG
(
"upper layer rejected a WebSocket connection:"
<<
descr
);
handshake
::
write_http_1_bad_request
(
down
->
output_buffer
(),
descr
);
write_response
(
down
,
http
::
status
::
bad_request
,
descr
);
down
->
end_output
();
down
->
abort_reason
(
std
::
move
(
err
));
down
->
abort_reason
(
std
::
move
(
err
));
return
false
;
return
false
;
}
}
// Send server handshake.
// Finalize the WebSocket handshake.
handshake
hs
;
hs
.
assign_key
(
sec_key
);
down
->
begin_output
();
down
->
begin_output
();
hs
.
write_http_1_response
(
down
->
output_buffer
());
hs
.
write_http_1_response
(
down
->
output_buffer
());
down
->
end_output
();
down
->
end_output
();
// Done.
CAF_LOG_DEBUG
(
"completed WebSocket handshake"
);
CAF_LOG_DEBUG
(
"completed WebSocket handshake"
);
handshake_complete_
=
true
;
handshake_complete_
=
true
;
return
true
;
return
true
;
...
@@ -293,8 +277,8 @@ socket_manager_ptr make_server(multiplexer& mpx, Socket fd,
...
@@ -293,8 +277,8 @@ socket_manager_ptr make_server(multiplexer& mpx, Socket fd,
async
::
producer_resource
<
T
>
out
,
Trait
trait
)
{
async
::
producer_resource
<
T
>
out
,
Trait
trait
)
{
using
app_t
=
message_flow_bridge
<
T
,
Trait
,
tag
::
mixed_message_oriented
>
;
using
app_t
=
message_flow_bridge
<
T
,
Trait
,
tag
::
mixed_message_oriented
>
;
using
stack_t
=
Transport
<
server
<
app_t
>>
;
using
stack_t
=
Transport
<
server
<
app_t
>>
;
auto
mgr
=
make_socket_manager
<
stack_t
>
(
fd
,
&
mpx
,
std
::
move
(
trait
));
auto
mgr
=
make_socket_manager
<
stack_t
>
(
fd
,
&
mpx
,
std
::
move
(
in
),
mgr
->
top_layer
().
connect_flows
(
mgr
.
get
(),
std
::
move
(
in
),
std
::
move
(
ou
t
));
std
::
move
(
out
),
std
::
move
(
trai
t
));
return
mgr
;
return
mgr
;
}
}
...
...
libcaf_net/caf/net/web_socket/status.hpp
View file @
d2830d80
...
@@ -8,6 +8,7 @@
...
@@ -8,6 +8,7 @@
#include "caf/detail/net_export.hpp"
#include "caf/detail/net_export.hpp"
#include <cstdint>
#include <cstdint>
#include <type_traits>
namespace
caf
::
net
::
web_socket
{
namespace
caf
::
net
::
web_socket
{
...
...
libcaf_net/caf/tag/hypertext_oriented.hpp
0 → 100644
View file @
d2830d80
// This file is part of CAF, the C++ Actor Framework. See the file LICENSE in
// the main distribution directory for license terms and copyright or visit
// https://github.com/actor-framework/actor-framework/blob/master/LICENSE.
#pragma once
namespace
caf
::
tag
{
/// Tags a layer that expects an HTTP-like transport.
struct
hypertext_oriented
{};
}
// namespace caf::tag
libcaf_net/src/net/http/header.cpp
0 → 100644
View file @
d2830d80
#include "caf/net/http/header.hpp"
#include "caf/expected.hpp"
#include "caf/logger.hpp"
namespace
caf
::
net
::
http
{
namespace
{
constexpr
string_view
eol
=
"
\r\n
"
;
template
<
class
F
>
bool
for_each_line
(
string_view
input
,
F
&&
f
)
{
for
(
auto
pos
=
input
.
begin
();;)
{
auto
line_end
=
std
::
search
(
pos
,
input
.
end
(),
eol
.
begin
(),
eol
.
end
());
if
(
line_end
==
input
.
end
()
||
std
::
distance
(
pos
,
input
.
end
())
==
2
)
{
// Reaching the end or hitting the last empty line tells us we're done.
return
true
;
}
if
(
!
f
(
string_view
{
pos
,
line_end
}))
return
false
;
pos
=
line_end
+
eol
.
size
();
}
return
true
;
}
string_view
trim
(
string_view
str
)
{
str
.
remove_prefix
(
std
::
min
(
str
.
find_first_not_of
(
' '
),
str
.
size
()));
auto
trim_pos
=
str
.
find_last_not_of
(
' '
);
if
(
trim_pos
!=
str
.
npos
)
str
.
remove_suffix
(
str
.
size
()
-
(
trim_pos
+
1
));
return
str
;
}
/// Splits `str` at the first occurrence of `sep` into the head and the
/// remainder (excluding the separator).
static
std
::
pair
<
string_view
,
string_view
>
split
(
string_view
str
,
string_view
sep
)
{
auto
i
=
std
::
search
(
str
.
begin
(),
str
.
end
(),
sep
.
begin
(),
sep
.
end
());
if
(
i
!=
str
.
end
())
return
{{
str
.
begin
(),
i
},
{
i
+
sep
.
size
(),
str
.
end
()}};
return
{{
str
},
{}};
}
/// Convenience function for splitting twice.
static
std
::
tuple
<
string_view
,
string_view
,
string_view
>
split2
(
string_view
str
,
string_view
sep
)
{
auto
[
first
,
r1
]
=
split
(
str
,
sep
);
auto
[
second
,
third
]
=
split
(
r1
,
sep
);
return
{
first
,
second
,
third
};
}
/// @pre `y` is all lowercase
bool
case_insensitive_eq
(
string_view
x
,
string_view
y
)
{
return
std
::
equal
(
x
.
begin
(),
x
.
end
(),
y
.
begin
(),
y
.
end
(),
[](
char
a
,
char
b
)
{
return
tolower
(
a
)
==
b
;
});
}
}
// namespace
header
::
header
(
const
header
&
other
)
{
assign
(
other
);
}
header
&
header
::
operator
=
(
const
header
&
other
)
{
assign
(
other
);
return
*
this
;
}
void
header
::
assign
(
const
header
&
other
)
{
auto
remap
=
[](
const
char
*
base
,
string_view
src
,
const
char
*
new_base
)
{
auto
offset
=
std
::
distance
(
base
,
src
.
data
());
return
string_view
{
new_base
+
offset
,
src
.
size
()};
};
method_
=
other
.
method_
;
uri_
=
other
.
uri_
;
if
(
other
.
valid
())
{
raw_
.
assign
(
other
.
raw_
.
begin
(),
other
.
raw_
.
end
());
auto
base
=
other
.
raw_
.
data
();
auto
new_base
=
raw_
.
data
();
version_
=
remap
(
base
,
other
.
version_
,
new_base
);
auto
&
fields
=
fields_
.
container
();
auto
&
other_fields
=
other
.
fields_
.
container
();
fields
.
resize
(
other_fields
.
size
());
for
(
size_t
index
=
0
;
index
<
fields
.
size
();
++
index
)
{
fields
[
index
].
first
=
remap
(
base
,
other_fields
[
index
].
first
,
new_base
);
fields
[
index
].
second
=
remap
(
base
,
other_fields
[
index
].
second
,
new_base
);
}
}
else
{
raw_
.
clear
();
version_
=
string_view
{};
fields_
.
clear
();
}
}
std
::
pair
<
status
,
string_view
>
header
::
parse
(
string_view
raw
)
{
CAF_LOG_TRACE
(
CAF_ARG
(
raw
));
// Sanity checking and copying of the raw input.
using
namespace
literals
;
if
(
raw
.
empty
())
{
raw_
.
clear
();
return
{
status
::
bad_request
,
"Empty header."
};
};
raw_
.
assign
(
raw
.
begin
(),
raw
.
end
());
// Parse the first line, i.e., "METHOD REQUEST-URI VERSION".
auto
[
first_line
,
remainder
]
=
split
(
string_view
{
raw_
.
data
(),
raw_
.
size
()},
eol
);
auto
[
method_str
,
request_uri_str
,
version
]
=
split2
(
first_line
,
" "
);
// The path must be absolute.
if
(
request_uri_str
.
empty
()
||
request_uri_str
.
front
()
!=
'/'
)
{
CAF_LOG_DEBUG
(
"Malformed Request-URI: expected an absolute path."
);
raw_
.
clear
();
return
{
status
::
bad_request
,
"Malformed Request-URI: expected an absolute path."
};
}
// The path must form a valid URI when prefixing a scheme. We don't actually
// care about the scheme, so just use "foo" here for the validation step.
if
(
auto
res
=
make_uri
(
"nil://host"
+
to_string
(
request_uri_str
)))
{
uri_
=
std
::
move
(
*
res
);
}
else
{
CAF_LOG_DEBUG
(
"Failed to parse URI"
<<
request_uri_str
<<
"->"
<<
res
.
error
());
raw_
.
clear
();
return
{
status
::
bad_request
,
"Malformed Request-URI."
};
}
// Verify and store the method.
if
(
case_insensitive_eq
(
method_str
,
"get"
))
{
method_
=
method
::
get
;
}
else
if
(
case_insensitive_eq
(
method_str
,
"head"
))
{
method_
=
method
::
head
;
}
else
if
(
case_insensitive_eq
(
method_str
,
"post"
))
{
method_
=
method
::
post
;
}
else
if
(
case_insensitive_eq
(
method_str
,
"put"
))
{
method_
=
method
::
put
;
}
else
if
(
case_insensitive_eq
(
method_str
,
"delete"
))
{
method_
=
method
::
del
;
}
else
if
(
case_insensitive_eq
(
method_str
,
"connect"
))
{
method_
=
method
::
connect
;
}
else
if
(
case_insensitive_eq
(
method_str
,
"options"
))
{
method_
=
method
::
options
;
}
else
if
(
case_insensitive_eq
(
method_str
,
"trace"
))
{
method_
=
method
::
trace
;
}
else
{
CAF_LOG_DEBUG
(
"Invalid HTTP method."
);
raw_
.
clear
();
return
{
status
::
bad_request
,
"Invalid HTTP method."
};
}
// Store the remaining header fields.
version_
=
version
;
fields_
.
clear
();
bool
ok
=
for_each_line
(
remainder
,
[
this
](
string_view
line
)
{
if
(
auto
sep
=
std
::
find
(
line
.
begin
(),
line
.
end
(),
':'
);
sep
!=
line
.
end
())
{
auto
key
=
trim
({
line
.
begin
(),
sep
});
auto
val
=
trim
({
sep
+
1
,
line
.
end
()});
if
(
!
key
.
empty
())
{
fields_
.
emplace
(
key
,
val
);
return
true
;
}
}
return
false
;
});
if
(
ok
)
{
return
{
status
::
ok
,
"OK"
};
}
else
{
raw_
.
clear
();
version_
=
string_view
{};
fields_
.
clear
();
return
{
status
::
bad_request
,
"Malformed header fields."
};
}
}
bool
header
::
chunked_transfer_encoding
()
const
{
return
field
(
"Transfer-Encoding"
).
find
(
"chunked"
)
!=
string_view
::
npos
;
}
optional
<
size_t
>
header
::
content_length
()
const
{
return
field_as
<
size_t
>
(
"Content-Length"
);
}
}
// namespace caf::net::http
libcaf_net/src/net/http/method.cpp
0 → 100644
View file @
d2830d80
#include "caf/net/http/method.hpp"
namespace
caf
::
net
::
http
{
std
::
string
to_rfc_string
(
method
x
)
{
using
namespace
caf
::
literals
;
switch
(
x
)
{
case
method
:
:
get
:
return
"GET"
;
case
method
:
:
head
:
return
"HEAD"
;
case
method
:
:
post
:
return
"POST"
;
case
method
:
:
put
:
return
"PUT"
;
case
method
:
:
del
:
return
"DELETE"
;
case
method
:
:
connect
:
return
"CONNECT"
;
case
method
:
:
options
:
return
"OPTIONS"
;
case
method
:
:
trace
:
return
"TRACE"
;
default:
return
"INVALID"
;
}
}
}
// namespace caf::net::http
libcaf_net/src/net/http/status.cpp
0 → 100644
View file @
d2830d80
#include "caf/net/http/status.hpp"
namespace
caf
::
net
::
http
{
string_view
phrase
(
status
code
)
{
using
namespace
caf
::
literals
;
switch
(
code
)
{
case
status
:
:
continue_request
:
return
"Continue"
_sv
;
case
status
:
:
switching_protocols
:
return
"Switching Protocols"
_sv
;
case
status
:
:
ok
:
return
"OK"
_sv
;
case
status
:
:
created
:
return
"Created"
_sv
;
case
status
:
:
accepted
:
return
"Accepted"
_sv
;
case
status
:
:
non_authoritative_information
:
return
"Non-Authoritative Information"
_sv
;
case
status
:
:
no_content
:
return
"No Content"
_sv
;
case
status
:
:
reset_content
:
return
"Reset Content"
_sv
;
case
status
:
:
partial_content
:
return
"Partial Content"
_sv
;
case
status
:
:
multiple_choices
:
return
"Multiple Choices"
_sv
;
case
status
:
:
moved_permanently
:
return
"Moved Permanently"
_sv
;
case
status
:
:
found
:
return
"Found"
_sv
;
case
status
:
:
see_other
:
return
"See Other"
_sv
;
case
status
:
:
not_modified
:
return
"Not Modified"
_sv
;
case
status
:
:
use_proxy
:
return
"Use Proxy"
_sv
;
case
status
:
:
temporary_redirect
:
return
"Temporary Redirect"
_sv
;
case
status
:
:
bad_request
:
return
"Bad Request"
_sv
;
case
status
:
:
unauthorized
:
return
"Unauthorized"
_sv
;
case
status
:
:
payment_required
:
return
"Payment Required"
_sv
;
case
status
:
:
forbidden
:
return
"Forbidden"
_sv
;
case
status
:
:
not_found
:
return
"Not Found"
_sv
;
case
status
:
:
method_not_allowed
:
return
"Method Not Allowed"
_sv
;
case
status
:
:
not_acceptable
:
return
"Not Acceptable"
_sv
;
case
status
:
:
proxy_authentication_required
:
return
"Proxy Authentication Required"
_sv
;
case
status
:
:
request_timeout
:
return
"Request Timeout"
_sv
;
case
status
:
:
conflict
:
return
"Conflict"
_sv
;
case
status
:
:
gone
:
return
"Gone"
_sv
;
case
status
:
:
length_required
:
return
"Length Required"
_sv
;
case
status
:
:
precondition_failed
:
return
"Precondition Failed"
_sv
;
case
status
:
:
payload_too_large
:
return
"Payload Too Large"
_sv
;
case
status
:
:
uri_too_long
:
return
"URI Too Long"
_sv
;
case
status
:
:
unsupported_media_type
:
return
"Unsupported Media Type"
_sv
;
case
status
:
:
range_not_satisfiable
:
return
"Range Not Satisfiable"
_sv
;
case
status
:
:
expectation_failed
:
return
"Expectation Failed"
_sv
;
case
status
:
:
upgrade_required
:
return
"Upgrade Required"
_sv
;
case
status
:
:
precondition_required
:
return
"Precondition Required"
_sv
;
case
status
:
:
too_many_requests
:
return
"Too Many Requests"
_sv
;
case
status
:
:
request_header_fields_too_large
:
return
"Request Header Fields Too Large"
_sv
;
case
status
:
:
internal_server_error
:
return
"Internal Server Error"
_sv
;
case
status
:
:
not_implemented
:
return
"Not Implemented"
_sv
;
case
status
:
:
bad_gateway
:
return
"Bad Gateway"
_sv
;
case
status
:
:
service_unavailable
:
return
"Service Unavailable"
_sv
;
case
status
:
:
gateway_timeout
:
return
"Gateway Timeout"
_sv
;
case
status
:
:
http_version_not_supported
:
return
"HTTP Version Not Supported"
_sv
;
case
status
:
:
network_authentication_required
:
return
"Network Authentication Required"
_sv
;
default:
return
"Unrecognized"
;
}
}
}
// namespace caf::net::http
libcaf_net/src/net/http/v1.cpp
0 → 100644
View file @
d2830d80
#include "caf/net/http/v1.hpp"
#include <array>
#include <string_view>
using
namespace
std
::
literals
;
namespace
caf
::
net
::
http
::
v1
{
namespace
{
struct
writer
{
byte_buffer
*
buf
;
};
writer
&
operator
<<
(
writer
&
out
,
char
x
)
{
out
.
buf
->
push_back
(
static_cast
<
byte
>
(
x
));
return
out
;
}
writer
&
operator
<<
(
writer
&
out
,
string_view
str
)
{
auto
bytes
=
as_bytes
(
make_span
(
str
));
out
.
buf
->
insert
(
out
.
buf
->
end
(),
bytes
.
begin
(),
bytes
.
end
());
return
out
;
}
writer
&
operator
<<
(
writer
&
out
,
std
::
string_view
str
)
{
auto
bytes
=
as_bytes
(
make_span
(
str
));
out
.
buf
->
insert
(
out
.
buf
->
end
(),
bytes
.
begin
(),
bytes
.
end
());
return
out
;
}
writer
&
operator
<<
(
writer
&
out
,
const
std
::
string
&
str
)
{
return
out
<<
string_view
{
str
};
}
}
// namespace
std
::
pair
<
string_view
,
byte_span
>
split_header
(
byte_span
bytes
)
{
std
::
array
<
byte
,
4
>
end_of_header
{{
byte
{
'\r'
},
byte
{
'\n'
},
byte
{
'\r'
},
byte
{
'\n'
},
}};
if
(
auto
i
=
std
::
search
(
bytes
.
begin
(),
bytes
.
end
(),
end_of_header
.
begin
(),
end_of_header
.
end
());
i
==
bytes
.
end
())
{
return
{
string_view
{},
bytes
};
}
else
{
auto
offset
=
static_cast
<
size_t
>
(
std
::
distance
(
bytes
.
begin
(),
i
));
offset
+=
end_of_header
.
size
();
return
{
string_view
{
reinterpret_cast
<
const
char
*>
(
bytes
.
begin
()),
offset
},
bytes
.
subspan
(
offset
)};
}
}
void
write_header
(
status
code
,
const
header_fields_map
&
fields
,
byte_buffer
&
buf
)
{
writer
out
{
&
buf
};
out
<<
"HTTP/1.1 "
sv
<<
std
::
to_string
(
static_cast
<
int
>
(
code
))
<<
' '
<<
phrase
(
code
)
<<
"
\r\n
"
sv
;
for
(
auto
&
[
key
,
val
]
:
fields
)
out
<<
key
<<
": "
sv
<<
val
<<
"
\r\n
"
sv
;
out
<<
"
\r\n
"
sv
;
}
void
write_response
(
status
code
,
string_view
content_type
,
string_view
content
,
byte_buffer
&
buf
)
{
header_fields_map
fields
;
write_response
(
code
,
content_type
,
content
,
fields
,
buf
);
writer
out
{
&
buf
};
out
<<
content
;
}
void
write_response
(
status
code
,
string_view
content_type
,
string_view
content
,
const
header_fields_map
&
fields
,
byte_buffer
&
buf
)
{
writer
out
{
&
buf
};
out
<<
"HTTP/1.1 "
sv
<<
std
::
to_string
(
static_cast
<
int
>
(
code
))
<<
' '
<<
phrase
(
code
)
<<
"
\r\n
"
sv
;
out
<<
"Content-Type: "
sv
<<
content_type
<<
"
\r\n
"
sv
;
out
<<
"Content-Length: "
sv
<<
std
::
to_string
(
content
.
size
())
<<
"
\r\n
"
sv
;
for
(
auto
&
[
key
,
val
]
:
fields
)
out
<<
key
<<
": "
sv
<<
val
<<
"
\r\n
"
sv
;
out
<<
"
\r\n
"
sv
;
out
<<
content
;
}
}
// namespace caf::net::http::v1
libcaf_net/src/net/web_socket/handshake.cpp
View file @
d2830d80
...
@@ -10,11 +10,6 @@
...
@@ -10,11 +10,6 @@
#include <random>
#include <random>
#include <tuple>
#include <tuple>
#include <iostream>
#include "caf/config.hpp"
#include "caf/config.hpp"
#include "caf/detail/base64.hpp"
#include "caf/detail/base64.hpp"
#include "caf/hash/sha1.hpp"
#include "caf/hash/sha1.hpp"
...
@@ -122,23 +117,6 @@ void handshake::write_http_1_response(byte_buffer& buf) const {
...
@@ -122,23 +117,6 @@ void handshake::write_http_1_response(byte_buffer& buf) const {
<<
response_key
()
<<
"
\r\n\r\n
"
;
<<
response_key
()
<<
"
\r\n\r\n
"
;
}
}
void
handshake
::
write_http_1_bad_request
(
byte_buffer
&
buf
,
string_view
descr
)
{
std
::
cout
<<
"BAD REQUEST: "
<<
descr
<<
'\n'
;
writer
out
{
&
buf
};
out
<<
"HTTP/1.1 400 Bad Request
\r\n
"
"Content-Type: text/plain
\r\n
"
"
\r\n
"
<<
descr
<<
"
\r\n
"
;
}
void
handshake
::
write_http_1_header_too_large
(
byte_buffer
&
buf
)
{
writer
out
{
&
buf
};
out
<<
"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
"
;
}
namespace
{
namespace
{
template
<
class
F
>
template
<
class
F
>
...
@@ -244,26 +222,6 @@ bool handshake::is_valid_http_1_response(string_view http_response) const {
...
@@ -244,26 +222,6 @@ bool handshake::is_valid_http_1_response(string_view http_response) const {
return
checker
.
ok
();
return
checker
.
ok
();
}
}
std
::
pair
<
string_view
,
byte_span
>
handshake
::
split_http_1_header
(
byte_span
bytes
)
{
std
::
array
<
byte
,
4
>
end_of_header
{{
byte
{
'\r'
},
byte
{
'\n'
},
byte
{
'\r'
},
byte
{
'\n'
},
}};
if
(
auto
i
=
std
::
search
(
bytes
.
begin
(),
bytes
.
end
(),
end_of_header
.
begin
(),
end_of_header
.
end
());
i
==
bytes
.
end
())
{
return
{
string_view
{},
bytes
};
}
else
{
auto
offset
=
static_cast
<
size_t
>
(
std
::
distance
(
bytes
.
begin
(),
i
));
offset
+=
end_of_header
.
size
();
return
{
string_view
{
reinterpret_cast
<
const
char
*>
(
bytes
.
begin
()),
offset
},
bytes
.
subspan
(
offset
)};
}
}
// -- utility ------------------------------------------------------------------
// -- utility ------------------------------------------------------------------
string_view
handshake
::
lookup
(
string_view
field_name
)
const
noexcept
{
string_view
handshake
::
lookup
(
string_view
field_name
)
const
noexcept
{
...
...
libcaf_net/test/net/http/server.cpp
0 → 100644
View file @
d2830d80
// This file is part of CAF, the C++ Actor Framework. See the file LICENSE in
// the main distribution directory for license terms and copyright or visit
// https://github.com/actor-framework/actor-framework/blob/master/LICENSE.
#define CAF_SUITE net.http.server
#include "caf/net/http/server.hpp"
#include "net-test.hpp"
using
namespace
caf
;
using
namespace
caf
::
literals
;
using
namespace
std
::
literals
::
string_literals
;
namespace
{
struct
app_t
{
net
::
http
::
header
hdr
;
caf
::
byte_buffer
payload
;
template
<
class
LowerLayerPtr
>
error
init
(
net
::
socket_manager
*
,
LowerLayerPtr
,
const
settings
&
)
{
return
none
;
}
template
<
class
LowerLayerPtr
>
bool
prepare_send
(
LowerLayerPtr
)
{
return
true
;
}
template
<
class
LowerLayerPtr
>
bool
done_sending
(
LowerLayerPtr
)
{
return
true
;
}
template
<
class
LowerLayerPtr
>
void
abort
(
LowerLayerPtr
,
const
error
&
reason
)
{
CAF_FAIL
(
"app::abort called: "
<<
reason
);
}
template
<
class
LowerLayerPtr
>
bool
consume
(
LowerLayerPtr
down
,
net
::
http
::
context
ctx
,
const
net
::
http
::
header
&
request_hdr
,
caf
::
const_byte_span
body
)
{
hdr
=
request_hdr
;
down
->
send_response
(
ctx
,
net
::
http
::
status
::
ok
,
"text/plain"
,
"Hello world!"
);
payload
.
assign
(
body
.
begin
(),
body
.
end
());
return
true
;
}
string_view
field
(
string_view
key
)
{
if
(
auto
i
=
hdr
.
fields
().
find
(
key
);
i
!=
hdr
.
fields
().
end
())
return
i
->
second
;
else
return
{};
}
string_view
param
(
string_view
key
)
{
auto
&
qm
=
hdr
.
query
();
if
(
auto
i
=
qm
.
find
(
key
);
i
!=
qm
.
end
())
return
i
->
second
;
else
return
{};
}
};
using
mock_server_type
=
mock_stream_transport
<
net
::
http
::
server
<
app_t
>>
;
}
// namespace
BEGIN_FIXTURE_SCOPE
(
host_fixture
)
SCENARIO
(
"the server parses HTTP GET requests into header fields"
)
{
GIVEN
(
"valid HTTP GET request"
)
{
string_view
req
=
"GET /foo/bar?user=foo&pw=bar HTTP/1.1
\r\n
"
"Host: localhost:8090
\r\n
"
"User-Agent: AwesomeLib/1.0
\r\n
"
"Accept-Encoding: gzip
\r\n\r\n
"
;
string_view
res
=
"HTTP/1.1 200 OK
\r\n
"
"Content-Type: text/plain
\r\n
"
"Content-Length: 12
\r\n
"
"
\r\n
"
"Hello world!"
;
WHEN
(
"sending it to an HTTP server"
)
{
mock_server_type
serv
;
CHECK_EQ
(
serv
.
init
(),
error
{});
serv
.
push
(
req
);
THEN
(
"the HTTP layer parses the data and calls the application layer"
)
{
CHECK_EQ
(
serv
.
handle_input
(),
static_cast
<
ptrdiff_t
>
(
req
.
size
()));
auto
&
app
=
serv
.
upper_layer
.
upper_layer
();
auto
&
hdr
=
app
.
hdr
;
CHECK_EQ
(
hdr
.
method
(),
net
::
http
::
method
::
get
);
CHECK_EQ
(
hdr
.
version
(),
"HTTP/1.1"
);
CHECK_EQ
(
hdr
.
path
(),
"foo/bar"
);
CHECK_EQ
(
app
.
field
(
"Host"
),
"localhost:8090"
);
CHECK_EQ
(
app
.
field
(
"User-Agent"
),
"AwesomeLib/1.0"
);
CHECK_EQ
(
app
.
field
(
"Accept-Encoding"
),
"gzip"
);
}
AND
(
"the server properly formats a response from the application layer"
)
{
CHECK_EQ
(
serv
.
output_as_str
(),
res
);
}
}
}
}
END_FIXTURE_SCOPE
()
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