Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
P
pistache
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
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
Libraries
pistache
Commits
9fcc1835
Commit
9fcc1835
authored
Aug 14, 2015
by
Mathieu Stefani
Committed by
Mathieu STEFANI
Aug 23, 2015
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Added a basic HTTP handler and request / response classes
parent
17282a44
Changes
13
Hide whitespace changes
Inline
Side-by-side
Showing
13 changed files
with
496 additions
and
93 deletions
+496
-93
main.cc
main.cc
+18
-0
src/.CMakeLists.txt.swp
src/.CMakeLists.txt.swp
+0
-0
src/CMakeLists.txt
src/CMakeLists.txt
+3
-0
src/common.h
src/common.h
+5
-2
src/http.cc
src/http.cc
+139
-25
src/http.h
src/http.h
+80
-10
src/listener.cc
src/listener.cc
+140
-28
src/listener.h
src/listener.h
+50
-14
src/mailbox.h
src/mailbox.h
+4
-0
src/net.cc
src/net.cc
+16
-11
src/net.h
src/net.h
+11
-2
src/peer.cc
src/peer.cc
+21
-0
src/peer.h
src/peer.h
+9
-1
No files found.
main.cc
View file @
9fcc1835
#include "net.h"
#include "peer.h"
#include "http.h"
#include <iostream>
#include <cstring>
using
namespace
std
;
class
MyHandler
:
public
Net
::
Http
::
Handler
{
void
onRequest
(
const
Net
::
Http
::
Request
&
req
,
Net
::
Tcp
::
Peer
&
peer
)
{
cout
<<
"Received "
<<
methodString
(
req
.
method
)
<<
" request on "
<<
req
.
resource
<<
endl
;
if
(
req
.
resource
==
"/ping"
)
{
if
(
req
.
method
==
Net
::
Http
::
Method
::
Get
)
{
cout
<<
"PONG"
<<
endl
;
Net
::
Http
::
Response
response
(
Net
::
Http
::
Code
::
Ok
,
"PONG"
);
response
.
writeTo
(
peer
);
}
}
}
};
int
main
(
int
argc
,
char
*
argv
[])
{
Net
::
Port
port
(
9080
);
...
...
@@ -18,5 +34,7 @@ int main(int argc, char *argv[]) {
cout
<<
"Cores = "
<<
hardware_concurrency
()
<<
endl
;
Net
::
Http
::
Server
server
(
addr
);
server
.
setHandler
(
std
::
make_shared
<
MyHandler
>
());
server
.
serve
();
}
src/.CMakeLists.txt.swp
deleted
100644 → 0
View file @
17282a44
File deleted
src/CMakeLists.txt
View file @
9fcc1835
...
...
@@ -2,6 +2,9 @@ set(SOURCE_FILES
listener.cc
net.cc
peer.cc
os.cc
peer.cc
http.cc
)
add_library
(
net
${
SOURCE_FILES
}
)
...
...
src/common.h
View file @
9fcc1835
...
...
@@ -6,6 +6,7 @@
#pragma once
#include <sstream>
#include <cstdio>
#include <cassert>
#include <sys/types.h>
...
...
@@ -26,8 +27,10 @@
[&]() { \
auto ret = __VA_ARGS__; \
if (ret < 0) { \
perror(#__VA_ARGS__); \
throw std::runtime_error(#__VA_ARGS__); \
const char *str = #__VA_ARGS__; \
std::ostringstream oss; \
oss << str << ": " << strerror(errno); \
throw std::runtime_error(oss.str()); \
} \
return ret; \
}(); \
...
...
src/http.cc
View file @
9fcc1835
...
...
@@ -7,8 +7,10 @@
#include <cstring>
#include <iostream>
#include <stdexcept>
#include "common.h"
#include "http.h"
#include "net.h"
#include "peer.h"
using
namespace
std
;
...
...
@@ -16,10 +18,11 @@ namespace Net {
namespace
Http
{
namespace
Private
{
static
constexpr
char
CR
=
0xD
;
static
constexpr
char
LF
=
0xA
;
static
constexpr
char
CRLF
[]
=
{
CR
,
LF
};
static
constexpr
char
CR
=
0xD
;
static
constexpr
char
LF
=
0xA
;
namespace
Private
{
void
Parser
::
advance
(
size_t
count
)
{
...
...
@@ -32,7 +35,25 @@ namespace Private {
bool
Parser
::
eol
()
const
{
return
buffer
[
cursor
]
==
CR
&&
buffer
[
cursor
+
1
]
==
LF
;
return
buffer
[
cursor
]
==
CR
&&
next
()
==
LF
;
}
char
Parser
::
next
()
const
{
if
(
cursor
+
1
>=
len
)
{
throw
std
::
runtime_error
(
"Early EOF"
);
}
return
buffer
[
cursor
+
1
];
}
Request
Parser
::
expectRequest
()
{
expectRequestLine
();
expectHeaders
();
expectBody
();
return
request
;
}
// 5.1 Request-Line
...
...
@@ -99,6 +120,7 @@ namespace Private {
void
Parser
::
expectHeaders
()
{
while
(
!
eol
())
{
// Read the header name
size_t
start
=
cursor
;
while
(
buffer
[
cursor
]
!=
':'
)
...
...
@@ -111,51 +133,135 @@ namespace Private {
// Skip the ':'
advance
(
1
);
// Read the header value
start
=
cursor
;
while
(
!
eol
())
advance
(
1
);
std
::
string
fieldValue
=
std
::
string
(
buffer
+
start
,
cursor
-
start
);
advance
(
2
);
if
(
fieldName
==
"Content-Length"
)
{
size_t
pos
;
contentLength
=
std
::
stol
(
fieldValue
,
&
pos
);
}
request
.
headers
.
push_back
(
make_pair
(
std
::
move
(
fieldName
),
std
::
move
(
fieldValue
)));
// CRLF
advance
(
2
);
}
}
void
Parser
::
expectBody
()
{
if
(
contentLength
>
0
)
{
advance
(
2
);
// CRLF
if
(
cursor
+
contentLength
>
len
)
{
throw
std
::
runtime_error
(
"Corrupted HTTP Body"
);
}
request
.
body
=
std
::
string
(
buffer
+
cursor
,
contentLength
);
}
}
}
// namespace Private
const
char
*
methodString
(
Method
method
)
{
switch
(
method
)
{
#define METHOD(name, str) \
case Method::name: \
return str;
HTTP_METHODS
#undef METHOD
}
return
nullptr
;
}
const
char
*
codeString
(
Code
code
)
{
switch
(
code
)
{
#define CODE(_, name, str) \
case Code::name: \
return str;
STATUS_CODES
#undef CODE
}
return
nullptr
;
}
Message
::
Message
()
{
}
Request
::
Request
()
:
Message
()
{
}
Response
::
Response
(
Code
code
,
std
::
string
body
)
:
Message
()
{
this
->
body
=
std
::
move
(
body
);
code_
=
code
;
}
const
char
*
methodString
(
Method
method
)
void
Response
::
writeTo
(
Tcp
::
Peer
&
peer
)
{
static
constexpr
size_t
MethodsCount
=
sizeof
priv__methodStrings
/
sizeof
*
priv__methodStrings
;
int
fd
=
peer
.
fd
();
char
buffer
[
Const
::
MaxBuffer
];
std
::
memset
(
buffer
,
0
,
Const
::
MaxBuffer
);
char
*
p_buf
=
buffer
;
int
index
=
static_cast
<
int
>
(
method
);
if
(
index
>=
MethodsCount
)
throw
std
::
logic_error
(
"Invalid method index"
);
auto
writeRaw
=
[
&
](
const
void
*
data
,
size_t
len
)
{
p_buf
=
static_cast
<
char
*>
(
memcpy
(
p_buf
,
data
,
len
));
p_buf
+=
len
;
};
return
priv__methodStrings
[
index
];
auto
writeString
=
[
&
](
const
char
*
str
)
{
const
size_t
len
=
std
::
strlen
(
str
);
writeRaw
(
str
,
std
::
strlen
(
str
));
};
auto
writeInt
=
[
&
](
uint64_t
value
)
{
auto
str
=
std
::
to_string
(
value
);
writeRaw
(
str
.
c_str
(),
str
.
size
());
};
auto
writeChar
=
[
&
](
char
c
)
{
*
p_buf
++
=
c
;
};
writeString
(
"HTTP/1.1 "
);
writeInt
(
static_cast
<
int
>
(
code_
));
writeChar
(
' '
);
writeString
(
codeString
(
code_
));
writeRaw
(
CRLF
,
2
);
writeString
(
"Content-Length:"
);
writeInt
(
body
.
size
());
writeRaw
(
CRLF
,
2
);
writeRaw
(
CRLF
,
2
);
writeString
(
body
.
c_str
());
const
size_t
len
=
p_buf
-
buffer
;
ssize_t
bytes
=
send
(
fd
,
buffer
,
len
,
0
);
cout
<<
bytes
<<
" bytes sent"
<<
endl
;
}
void
Handler
::
onInput
(
const
char
*
buffer
,
size_t
len
)
{
cout
<<
"Received http request "
<<
string
(
buffer
,
len
)
<<
endl
;
Handler
::
onInput
(
const
char
*
buffer
,
size_t
len
,
Tcp
::
Peer
&
peer
)
{
Private
::
Parser
parser
(
buffer
,
len
);
parser
.
expectRequestLine
();
parser
.
expectHeaders
();
auto
request
=
parser
.
expectRequest
();
auto
req
=
parser
.
request
;
cout
<<
"method = "
<<
methodString
(
req
.
method
)
<<
endl
;
cout
<<
"resource = "
<<
req
.
resource
<<
endl
;
cout
<<
"headers = "
<<
endl
;
for
(
const
auto
&
header
:
req
.
headers
)
{
cout
<<
string
(
' '
,
4
)
<<
header
.
first
<<
" -> "
<<
header
.
second
<<
endl
;
}
onRequest
(
request
,
peer
);
}
void
...
...
@@ -169,11 +275,19 @@ Server::Server(const Net::Address& addr)
:
listener
(
addr
)
{
}
void
Server
::
setHandler
(
const
std
::
shared_ptr
<
Handler
>&
handler
)
{
handler_
=
handler
;
}
void
Server
::
serve
()
{
if
(
!
handler_
)
throw
std
::
runtime_error
(
"Must call setHandler() prior to serve()"
);
listener
.
init
(
4
);
listener
.
setHandler
(
make_shared
<
Handler
>
()
);
listener
.
setHandler
(
handler_
);
if
(
listener
.
bind
())
{
const
auto
&
addr
=
listener
.
address
();
...
...
src/http.h
View file @
9fcc1835
...
...
@@ -23,6 +23,48 @@ namespace Http {
METHOD(Trace, "TRACE") \
METHOD(Connect, "CONNECT")
// 10. Status Code Definitions
#define STATUS_CODES \
CODE(100, Continue, "Continue") \
CODE(101, Switching_Protocols, "Switching Protocols") \
CODE(200, Ok, "OK") \
CODE(201, Created, "Created") \
CODE(202, Accepted, "Accepted") \
CODE(203, NonAuthoritative_Information, "Non-Authoritative Information") \
CODE(204, No_Content, "No Content") \
CODE(205, Reset_Content, "Reset Content") \
CODE(206, Partial_Content, "Partial Content") \
CODE(300, Multiple_Choices, "Multiple Choices") \
CODE(301, Moved_Permanently, "Moved Permanently") \
CODE(302, Found, "Found") \
CODE(303, See_Other, "See Other") \
CODE(304, Not_Modified, "Not Modified") \
CODE(305, Use_Proxy, "Use Proxy") \
CODE(307, Temporary_Redirect, "Temporary Redirect") \
CODE(400, Bad_Request, "Bad Request") \
CODE(401, Unauthorized, "Unauthorized") \
CODE(402, Payment_Required, "Payment Required") \
CODE(403, Forbidden, "Forbidden") \
CODE(404, Not_Found, "Not Found") \
CODE(405, Method_Not_Allowed, "Method Not Allowed") \
CODE(406, Not_Acceptable, "Not Acceptable") \
CODE(407, Proxy_Authentication_Required, "Proxy Authentication Required") \
CODE(408, Request_Timeout, "Request Timeout") \
CODE(409, Conflict, "Conflict") \
CODE(410, Gone, "Gone") \
CODE(411, Length_Required, "Length Required") \
CODE(412, Precondition_Failed, "Precondition Failed") \
CODE(413, Request_Entity_Too_Large, "Request Entity Too Large") \
CODE(414, RequestURI_Too_Long, "Request-URI Too Long") \
CODE(415, Unsupported_Media_Type, "Unsupported Media Type") \
CODE(416, Requested_Range_Not_Satisfiable, "Requested Range Not Satisfiable") \
CODE(417, Expectation_Failed, "Expectation Failed") \
CODE(500, Internal_Server_Error, "Internal Server Error") \
CODE(501, Not_Implemented, "Not Implemented") \
CODE(502, Bad_Gateway, "Bad Gateway") \
CODE(503, Service_Unavailable, "Service Unavailable") \
CODE(504, Gateway_Timeout, "Gateway Timeout")
enum
class
Method
{
#define METHOD(m, _) m,
...
...
@@ -30,23 +72,42 @@ enum class Method {
#undef METHOD
};
static
constexpr
const
char
*
priv__methodStrings
[]
=
{
#define METHOD(_, str) #str,
HTTP_METHODS
#undef METHOD
enum
class
Code
{
#define CODE(value, name, _) name = value,
STATUS_CODES
#undef CODE
};
const
char
*
methodString
(
Method
method
);
const
char
*
codeString
(
Code
code
);
// 4. HTTP Message
class
Message
{
public:
Message
();
std
::
vector
<
std
::
pair
<
std
::
string
,
std
::
string
>>
headers
;
std
::
string
body
;
};
class
Request
{
// 5. Request
class
Request
:
public
Message
{
public:
Request
();
Method
method
;
std
::
string
resource
;
std
::
string
body
;
std
::
vector
<
std
::
pair
<
std
::
string
,
std
::
string
>>
headers
;
};
// 6. Response
class
Response
:
public
Message
{
public:
Response
(
Code
code
,
std
::
string
body
);
void
writeTo
(
Tcp
::
Peer
&
peer
);
private:
Code
code_
;
};
namespace
Private
{
...
...
@@ -55,10 +116,13 @@ namespace Private {
:
buffer
(
buffer
)
,
len
(
len
)
,
cursor
(
0
)
,
contentLength
(
-
1
)
{
}
Request
expectRequest
();
void
expectRequestLine
();
void
expectHeaders
();
void
expectBody
();
void
advance
(
size_t
count
);
bool
eol
()
const
;
...
...
@@ -67,7 +131,9 @@ namespace Private {
size_t
len
;
size_t
cursor
;
char
next
()
const
;
private:
ssize_t
contentLength
;
Request
request
;
};
...
...
@@ -75,8 +141,10 @@ namespace Private {
class
Handler
:
public
Net
::
Tcp
::
Handler
{
public:
void
onInput
(
const
char
*
buffer
,
size_t
len
);
void
onInput
(
const
char
*
buffer
,
size_t
len
,
Tcp
::
Peer
&
peer
);
void
onOutput
();
virtual
void
onRequest
(
const
Request
&
request
,
Tcp
::
Peer
&
peer
)
=
0
;
};
class
Server
{
...
...
@@ -84,9 +152,11 @@ public:
Server
();
Server
(
const
Net
::
Address
&
addr
);
void
setHandler
(
const
std
::
shared_ptr
<
Handler
>&
handler
);
void
serve
();
private:
std
::
shared_ptr
<
Handler
>
handler_
;
Net
::
Tcp
::
Listener
listener
;
};
...
...
src/listener.cc
View file @
9fcc1835
...
...
@@ -16,6 +16,7 @@
#include "listener.h"
#include "peer.h"
#include "common.h"
#include "os.h"
using
namespace
std
;
...
...
@@ -23,6 +24,33 @@ namespace Net {
namespace
Tcp
{
struct
Message
{
virtual
~
Message
()
{
}
enum
class
Type
{
NewPeer
,
Shutdown
};
virtual
Type
type
()
const
=
0
;
};
struct
PeerMessage
:
public
Message
{
PeerMessage
(
const
std
::
shared_ptr
<
Peer
>&
peer
)
:
peer_
(
peer
)
{
}
Type
type
()
const
{
return
Type
::
NewPeer
;
}
std
::
shared_ptr
<
Peer
>
peer
()
const
{
return
peer_
;
}
private:
std
::
shared_ptr
<
Peer
>
peer_
;
};
template
<
typename
To
>
To
*
message_cast
(
const
std
::
unique_ptr
<
Message
>&
from
)
{
return
static_cast
<
To
*>
(
from
.
get
());
}
IoWorker
::
IoWorker
()
{
epoll_fd
=
TRY_RET
(
epoll_create
(
128
));
}
...
...
@@ -34,20 +62,33 @@ IoWorker::~IoWorker() {
}
void
IoWorker
::
start
()
{
IoWorker
::
start
(
const
std
::
shared_ptr
<
Handler
>&
handler
)
{
handler_
=
handler
;
thread
.
reset
(
new
std
::
thread
([
this
]()
{
this
->
run
();
}));
}
std
::
shared_ptr
<
Peer
>
IoWorker
::
getPeer
(
Fd
fd
)
const
{
auto
it
=
peers
.
find
(
fd
);
if
(
it
==
std
::
end
(
peers
))
{
throw
std
::
runtime_error
(
"No peer found for fd: "
+
to_string
(
fd
));
}
return
it
->
second
;
}
void
IoWorker
::
readIncomingData
(
int
fd
)
{
IoWorker
::
handleIncoming
(
const
std
::
shared_ptr
<
Peer
>&
peer
)
{
char
buffer
[
Const
::
MaxBuffer
];
memset
(
buffer
,
0
,
sizeof
buffer
);
ssize_t
totalBytes
=
0
;
int
fd
=
peer
->
fd
();
for
(;;)
{
...
...
@@ -56,14 +97,15 @@ IoWorker::readIncomingData(int fd) {
bytes
=
recv
(
fd
,
buffer
+
totalBytes
,
Const
::
MaxBuffer
-
totalBytes
,
0
);
if
(
bytes
==
-
1
)
{
if
(
errno
==
EAGAIN
||
errno
==
EWOULDBLOCK
)
{
cout
<<
"Received "
<<
buffer
<<
endl
;
handler_
->
onInput
(
buffer
,
totalBytes
,
*
peer
)
;
}
else
{
throw
std
::
runtime_error
(
strerror
(
errno
));
}
break
;
}
else
if
(
bytes
==
0
)
{
cout
<<
"Peer has been shutdowned"
<<
endl
;
cout
<<
"Peer "
<<
*
peer
<<
" has disconnected"
<<
endl
;
close
(
fd
);
break
;
}
...
...
@@ -78,6 +120,21 @@ IoWorker::readIncomingData(int fd) {
}
void
IoWorker
::
handleNewPeer
(
const
std
::
shared_ptr
<
Peer
>&
peer
)
{
std
::
cout
<<
"New peer: "
<<
*
peer
<<
std
::
endl
;
int
fd
=
peer
->
fd
();
struct
epoll_event
event
;
event
.
events
=
EPOLLIN
;
event
.
data
.
fd
=
fd
;
epoll_ctl
(
epoll_fd
,
EPOLL_CTL_ADD
,
fd
,
&
event
);
peers
.
insert
(
std
::
make_pair
(
fd
,
peer
));
}
void
IoWorker
::
run
()
{
struct
epoll_event
events
[
Const
::
MaxEvents
];
...
...
@@ -89,21 +146,19 @@ IoWorker::run() {
break
;
case
0
:
if
(
!
mailbox
.
isEmpty
())
{
int
*
fd
=
mailbox
.
clear
();
struct
epoll_event
event
;
event
.
events
=
EPOLLIN
;
event
.
data
.
fd
=
*
fd
;
std
::
unique_ptr
<
Message
>
msg
(
mailbox
.
clear
());
if
(
msg
->
type
()
==
Message
::
Type
::
NewPeer
)
{
auto
peer_msg
=
message_cast
<
PeerMessage
>
(
msg
);
epoll_ctl
(
epoll_fd
,
EPOLL_CTL_ADD
,
*
fd
,
&
event
);
handleNewPeer
(
peer_msg
->
peer
());
}
delete
fd
;
}
break
;
default:
for
(
int
i
=
0
;
i
<
ready_fds
;
++
i
)
{
const
struct
epoll_event
*
event
=
events
+
i
;
readIncomingData
(
event
->
data
.
fd
);
handleIncoming
(
getPeer
(
event
->
data
.
fd
)
);
}
break
;
...
...
@@ -111,13 +166,62 @@ IoWorker::run() {
}
}
Handler
::
Handler
()
{
}
Handler
::~
Handler
()
{
}
void
Handler
::
onConnection
()
{
}
void
Handler
::
onDisconnection
()
{
}
Listener
::
Listener
()
:
listen_fd
(
-
1
)
{
}
Listener
::
Listener
(
const
Address
&
address
)
:
addr_
(
address
)
,
listen_fd
(
-
1
)
{
}
bool
Listener
::
bind
()
{
void
Listener
::
init
(
size_t
workers
)
{
if
(
workers
>
hardware_concurrency
())
{
// Log::warning() << "More workers than available cores"
}
for
(
size_t
i
=
0
;
i
<
workers
;
++
i
)
{
auto
wrk
=
std
::
unique_ptr
<
IoWorker
>
(
new
IoWorker
);
ioGroup
.
push_back
(
std
::
move
(
wrk
));
}
}
void
Listener
::
setHandler
(
const
std
::
shared_ptr
<
Handler
>&
handler
)
{
handler_
=
handler
;
}
bool
Listener
::
bind
()
{
return
bind
(
addr_
);
}
bool
Listener
::
bind
(
const
Address
&
address
)
{
if
(
ioGroup
.
empty
())
{
throw
std
::
runtime_error
(
"Call init() before calling bind()"
);
}
addr_
=
address
;
struct
addrinfo
hints
;
hints
.
ai_family
=
AF_INET
;
...
...
@@ -135,7 +239,7 @@ bool Listener::bind() {
char
port
[
MaxPortLen
];
std
::
fill
(
port
,
port
+
MaxPortLen
,
0
);
std
::
snprintf
(
port
,
MaxPortLen
,
"%d"
,
addr_
.
port
(
));
std
::
snprintf
(
port
,
MaxPortLen
,
"%d"
,
static_cast
<
uint16_t
>
(
addr_
.
port
()
));
struct
addrinfo
*
addrs
;
TRY
(
::
getaddrinfo
(
host
.
c_str
(),
port
,
&
hints
,
&
addrs
));
...
...
@@ -156,16 +260,15 @@ bool Listener::bind() {
listen_fd
=
fd
;
for
(
int
i
=
0
;
i
<
4
;
++
i
)
{
auto
wrk
=
std
::
unique_ptr
<
IoWorker
>
(
new
IoWorker
);
wrk
->
start
();
ioGroup
.
push_back
(
std
::
move
(
wrk
));
for
(
auto
&
io
:
ioGroup
)
{
io
->
start
(
handler_
);
}
return
true
;
}
void
Listener
::
run
()
{
void
Listener
::
run
()
{
for
(;;)
{
struct
sockaddr_in
peer_addr
;
socklen_t
peer_addr_len
=
sizeof
(
peer_addr
);
...
...
@@ -177,31 +280,40 @@ void Listener::run() {
if
(
getnameinfo
((
struct
sockaddr
*
)
&
peer_addr
,
peer_addr_len
,
peer_host
,
NI_MAXHOST
,
nullptr
,
0
,
0
)
==
0
)
{
Address
addr
=
Address
::
fromUnix
((
struct
sockaddr
*
)
&
peer_addr
);
Peer
peer
(
addr
,
peer_host
);
auto
peer
=
make_shared
<
Peer
>
(
addr
,
peer_host
);
peer
->
associateFd
(
client_fd
);
std
::
cout
<<
"New peer: "
<<
peer
<<
std
::
endl
;
dispatchConnection
(
client_fd
);
dispatchPeer
(
peer
);
}
}
}
Address
Listener
::
address
()
const
{
return
addr_
;
}
void
Listener
::
dispatchPeer
(
const
std
::
shared_ptr
<
Peer
>&
peer
)
{
const
size_t
workers
=
ioGroup
.
size
();
size_t
start
=
peer
->
fd
()
%
workers
;
void
Listener
::
dispatchConnection
(
int
fd
)
{
size_t
start
=
fd
%
4
;
/* Find the first available worker */
size_t
current
=
start
;
for
(;;)
{
auto
&
mailbox
=
ioGroup
[
current
]
->
mailbox
;
if
(
mailbox
.
isEmpty
())
{
int
*
message
=
new
int
(
fd
);
int
*
old
=
mailbox
.
post
(
message
);
auto
message
=
new
PeerMessage
(
peer
);
auto
*
old
=
mailbox
.
post
(
message
);
assert
(
old
==
nullptr
);
return
;
}
current
=
(
current
+
1
)
%
4
;
current
=
(
current
+
1
)
%
workers
;
if
(
current
==
start
)
{
break
;
}
...
...
src/listener.h
View file @
9fcc1835
...
...
@@ -8,45 +8,81 @@
#include "net.h"
#include "mailbox.h"
#include "os.h"
#include <vector>
#include <memory>
#include <thread>
#include <unordered_map>
namespace
Net
{
namespace
Tcp
{
class
Peer
;
class
Message
;
class
Handler
;
class
IoWorker
{
public:
Mailbox
<
int
>
mailbox
;
Mailbox
<
Message
>
mailbox
;
IoWorker
();
~
IoWorker
();
void
start
();
void
start
(
const
std
::
shared_ptr
<
Handler
>
&
handler
);
private:
int
epoll_fd
;
std
::
unique_ptr
<
std
::
thread
>
thread
;
std
::
unordered_map
<
Fd
,
std
::
shared_ptr
<
Peer
>>
peers
;
std
::
shared_ptr
<
Handler
>
handler_
;
std
::
shared_ptr
<
Peer
>
getPeer
(
Fd
fd
)
const
;
void
handleIncoming
(
const
std
::
shared_ptr
<
Peer
>&
peer
);
void
handleNewPeer
(
const
std
::
shared_ptr
<
Peer
>&
peer
);
void
run
();
};
class
Listener
{
public:
friend
class
IoWorker
;
Listener
();
Listener
(
const
Address
&
address
);
void
init
(
size_t
workers
);
void
setHandler
(
const
std
::
shared_ptr
<
Handler
>&
handler
);
bool
bind
();
bool
bind
(
const
Address
&
adress
);
void
readIncomingData
(
int
fd
);
void
run
();
Address
address
()
const
;
private:
Address
addr_
;
int
listen_fd
;
std
::
vector
<
std
::
unique_ptr
<
IoWorker
>>
ioGroup
;
std
::
shared_ptr
<
Handler
>
handler_
;
void
dispatchPeer
(
const
std
::
shared_ptr
<
Peer
>&
peer
);
};
class
Listener
{
public:
Listener
(
const
Address
&
address
);
class
Handler
{
public:
Handler
();
~
Handler
();
bool
bind
()
;
void
run
()
;
virtual
void
onInput
(
const
char
*
buffer
,
size_t
len
,
Tcp
::
Peer
&
peer
)
=
0
;
virtual
void
onOutput
()
=
0
;
private:
virtual
void
onConnection
();
virtual
void
onDisconnection
();
Address
addr_
;
int
listen_fd
;
std
::
vector
<
std
::
unique_ptr
<
IoWorker
>>
ioGroup
;
};
void
dispatchConnection
(
int
fd
);
};
}
// namespace Tcp
...
...
src/mailbox.h
View file @
9fcc1835
...
...
@@ -13,6 +13,10 @@
template
<
typename
T
>
class
Mailbox
{
public:
Mailbox
()
{
data
.
store
(
nullptr
);
}
const
T
*
get
()
const
{
if
(
isEmpty
())
{
throw
std
::
runtime_error
(
"Can not retrieve mail from empty mailbox"
);
...
...
src/net.cc
View file @
9fcc1835
...
...
@@ -9,7 +9,6 @@
#include <stdexcept>
#include <limits>
#include <cstring>
#include <fcntl.h>
#include <netinet/in.h>
#include <arpa/inet.h>
...
...
@@ -17,16 +16,19 @@ using namespace std;
namespace
Net
{
bool
make_non_blocking
(
int
sfd
)
{
int
flags
=
fcntl
(
sfd
,
F_GETFL
,
0
);
if
(
flags
==
-
1
)
return
false
;
Port
::
Port
(
uint16_t
port
)
:
port
(
port
)
{
}
flags
|=
O_NONBLOCK
;
int
ret
=
fcntl
(
sfd
,
F_SETFL
,
flags
);
if
(
ret
==
-
1
)
return
false
;
bool
Port
::
isReserved
()
const
{
return
port
<
1024
;
}
return
true
;
bool
Port
::
isUsed
()
const
{
throw
std
::
runtime_error
(
"Unimplemented"
);
return
false
;
}
Ipv4
::
Ipv4
(
uint8_t
a
,
uint8_t
b
,
uint8_t
c
,
uint8_t
d
)
...
...
@@ -36,11 +38,13 @@ Ipv4::Ipv4(uint8_t a, uint8_t b, uint8_t c, uint8_t d)
,
d
(
d
)
{
}
Ipv4
Ipv4
::
any
()
{
Ipv4
Ipv4
::
any
()
{
return
Ipv4
(
0
,
0
,
0
,
0
);
}
std
::
string
Ipv4
::
toString
()
const
{
std
::
string
Ipv4
::
toString
()
const
{
static
constexpr
size_t
MaxSize
=
sizeof
(
"255"
)
*
4
+
3
+
1
;
/* 4 * 255 + 3 * dot + \0 */
char
buff
[
MaxSize
];
...
...
@@ -88,6 +92,7 @@ Address
Address
::
fromUnix
(
struct
sockaddr
*
addr
)
{
struct
sockaddr_in
*
in_addr
=
reinterpret_cast
<
struct
sockaddr_in
*>
(
addr
);
std
::
string
host
=
TRY_RET
(
inet_ntoa
(
in_addr
->
sin_addr
));
int
port
=
in_addr
->
sin_port
;
return
Address
(
std
::
move
(
host
),
port
);
...
...
src/net.h
View file @
9fcc1835
...
...
@@ -10,9 +10,18 @@
namespace
Net
{
typedef
uint16_t
Port
;
class
Port
{
public:
Port
(
uint16_t
port
=
0
);
operator
uint16_t
()
const
{
return
port
;
}
bool
isReserved
()
const
;
bool
isUsed
()
const
;
bool
make_non_blocking
(
int
fd
);
private:
uint16_t
port
;
};
class
Ipv4
{
public:
...
...
src/peer.cc
View file @
9fcc1835
...
...
@@ -5,17 +5,22 @@
#include "peer.h"
#include <iostream>
#include <stdexcept>
namespace
Net
{
namespace
Tcp
{
using
namespace
std
;
Peer
::
Peer
()
:
fd_
(
-
1
)
{
}
Peer
::
Peer
(
const
Address
&
addr
,
const
string
&
host
)
:
addr
(
addr
)
,
host
(
host
)
,
fd_
(
-
1
)
{
}
Address
...
...
@@ -28,10 +33,26 @@ Peer::hostname() const {
return
host
;
}
void
Peer
::
associateFd
(
int
fd
)
{
fd_
=
fd
;
}
int
Peer
::
fd
()
const
{
if
(
fd_
==
-
1
)
{
throw
std
::
runtime_error
(
"The peer has no associated fd"
);
}
return
fd_
;
}
std
::
ostream
&
operator
<<
(
std
::
ostream
&
os
,
const
Peer
&
peer
)
{
const
auto
&
addr
=
peer
.
address
();
os
<<
"("
<<
addr
.
host
()
<<
", "
<<
addr
.
port
()
<<
") ["
<<
peer
.
hostname
()
<<
"]"
;
return
os
;
}
}
// namespace Tcp
}
// namespace Net
src/peer.h
View file @
9fcc1835
...
...
@@ -7,25 +7,33 @@
#pragma once
#include "net.h"
#include "os.h"
#include <string>
#include <iostream>
namespace
Net
{
namespace
Tcp
{
class
Peer
{
public:
Peer
();
Peer
(
const
Address
&
addr
,
const
std
::
string
&
hostname
);
Address
address
()
const
;
std
::
string
hostname
()
const
;
void
associateFd
(
Fd
fd
);
Fd
fd
()
const
;
private:
Address
addr
;
std
::
string
host
;
Fd
fd_
;
};
std
::
ostream
&
operator
<<
(
std
::
ostream
&
os
,
const
Peer
&
peer
);
}
// namespace Tcp
}
// namespace Net
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