Skip to content
Projects
Groups
Snippets
Help
Loading...
Sign in
Toggle navigation
D
dlib
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
钟尚武
dlib
Commits
c2825e86
Commit
c2825e86
authored
Nov 08, 2012
by
Davis King
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Moved function definitions into cpp files and also updated the server_http's
spec appropriately.
parent
5e7303a3
Hide whitespace changes
Inline
Side-by-side
Showing
10 changed files
with
1286 additions
and
1024 deletions
+1286
-1024
source.cpp
dlib/all/source.cpp
+3
-0
server_http.cpp
dlib/server/server_http.cpp
+415
-0
server_http.h
dlib/server/server_http.h
+19
-377
server_http_abstract.h
dlib/server/server_http_abstract.h
+255
-77
server_iostream.cpp
dlib/server/server_iostream.cpp
+14
-0
server_iostream.h
dlib/server/server_iostream.h
+4
-1
server_iostream_abstract.h
dlib/server/server_iostream_abstract.h
+2
-8
server_kernel.cpp
dlib/server/server_kernel.cpp
+570
-0
server_kernel.h
dlib/server/server_kernel.h
+4
-556
server_kernel_abstract.h
dlib/server/server_kernel_abstract.h
+0
-5
No files found.
dlib/all/source.cpp
View file @
c2825e86
...
...
@@ -38,6 +38,9 @@
#include "../sockets/sockets_kernel_2.cpp"
#include "../sockstreambuf/sockstreambuf_kernel_1.cpp"
#include "../sockstreambuf/sockstreambuf_kernel_2.cpp"
#include "../server/server_kernel.cpp"
#include "../server/server_iostream.cpp"
#include "../server/server_http.cpp"
#include "../threads/multithreaded_object_extension.cpp"
#include "../threads/threaded_object_extension.cpp"
#include "../threads/threads_kernel_1.cpp"
...
...
dlib/server/server_http.cpp
0 → 100644
View file @
c2825e86
// Copyright (C) 2003 Davis E. King (davis@dlib.net)
// License: Boost Software License See LICENSE.txt for the full license.
#ifndef DLIB_SERVER_HTTP_CPp_
#define DLIB_SERVER_HTTP_CPp_
#include "server_http.h"
namespace
dlib
{
// ----------------------------------------------------------------------------------------
namespace
http_impl
{
inline
unsigned
char
to_hex
(
unsigned
char
x
)
{
return
x
+
(
x
>
9
?
(
'A'
-
10
)
:
'0'
);
}
const
std
::
string
urlencode
(
const
std
::
string
&
s
)
{
std
::
ostringstream
os
;
for
(
std
::
string
::
const_iterator
ci
=
s
.
begin
();
ci
!=
s
.
end
();
++
ci
)
{
if
(
(
*
ci
>=
'a'
&&
*
ci
<=
'z'
)
||
(
*
ci
>=
'A'
&&
*
ci
<=
'Z'
)
||
(
*
ci
>=
'0'
&&
*
ci
<=
'9'
)
)
{
// allowed
os
<<
*
ci
;
}
else
if
(
*
ci
==
' '
)
{
os
<<
'+'
;
}
else
{
os
<<
'%'
<<
to_hex
(
*
ci
>>
4
)
<<
to_hex
(
*
ci
%
16
);
}
}
return
os
.
str
();
}
inline
unsigned
char
from_hex
(
unsigned
char
ch
)
{
if
(
ch
<=
'9'
&&
ch
>=
'0'
)
ch
-=
'0'
;
else
if
(
ch
<=
'f'
&&
ch
>=
'a'
)
ch
-=
'a'
-
10
;
else
if
(
ch
<=
'F'
&&
ch
>=
'A'
)
ch
-=
'A'
-
10
;
else
ch
=
0
;
return
ch
;
}
const
std
::
string
urldecode
(
const
std
::
string
&
str
)
{
using
namespace
std
;
string
result
;
string
::
size_type
i
;
for
(
i
=
0
;
i
<
str
.
size
();
++
i
)
{
if
(
str
[
i
]
==
'+'
)
{
result
+=
' '
;
}
else
if
(
str
[
i
]
==
'%'
&&
str
.
size
()
>
i
+
2
)
{
const
unsigned
char
ch1
=
from_hex
(
str
[
i
+
1
]);
const
unsigned
char
ch2
=
from_hex
(
str
[
i
+
2
]);
const
unsigned
char
ch
=
(
ch1
<<
4
)
|
ch2
;
result
+=
ch
;
i
+=
2
;
}
else
{
result
+=
str
[
i
];
}
}
return
result
;
}
void
parse_url
(
std
::
string
word
,
key_value_map
&
queries
)
/*!
Parses the query string of a URL. word should be the stuff that comes
after the ? in the query URL.
!*/
{
std
::
string
::
size_type
pos
;
for
(
pos
=
0
;
pos
<
word
.
size
();
++
pos
)
{
if
(
word
[
pos
]
==
'&'
)
word
[
pos
]
=
' '
;
}
std
::
istringstream
sin
(
word
);
sin
>>
word
;
while
(
sin
)
{
pos
=
word
.
find_first_of
(
"="
);
if
(
pos
!=
std
::
string
::
npos
)
{
std
::
string
key
=
urldecode
(
word
.
substr
(
0
,
pos
));
std
::
string
value
=
urldecode
(
word
.
substr
(
pos
+
1
));
queries
[
key
]
=
value
;
}
sin
>>
word
;
}
}
void
read_with_limit
(
std
::
istream
&
in
,
std
::
string
&
buffer
,
int
delim
=
'\n'
)
{
using
namespace
std
;
const
size_t
max
=
16
*
1024
;
buffer
.
clear
();
buffer
.
reserve
(
300
);
while
(
in
.
peek
()
!=
delim
&&
in
.
peek
()
!=
EOF
&&
buffer
.
size
()
<
max
)
{
buffer
+=
(
char
)
in
.
get
();
}
// if we quit the loop because the data is longer than expected or we hit EOF
if
(
in
.
peek
()
==
EOF
||
buffer
.
size
()
==
max
)
throw
http_parse_error
(
"HTTP field from client is too long"
,
414
);
// Make sure the last char is the delim.
if
(
in
.
get
()
!=
delim
)
{
in
.
setstate
(
ios
::
badbit
);
buffer
.
clear
();
}
else
{
// Read the remaining delimiters
if
(
delim
==
' '
)
{
while
(
in
.
peek
()
==
' '
)
in
.
get
();
}
}
}
}
// ----------------------------------------------------------------------------------------
unsigned
long
parse_http_request
(
std
::
istream
&
in
,
incoming_things
&
incoming
,
unsigned
long
max_content_length
)
{
using
namespace
std
;
using
namespace
http_impl
;
read_with_limit
(
in
,
incoming
.
request_type
,
' '
);
// get the path
read_with_limit
(
in
,
incoming
.
path
,
' '
);
// Get the HTTP/1.1 - Ignore for now...
read_with_limit
(
in
,
incoming
.
protocol
);
key_value_map
&
incoming_headers
=
incoming
.
headers
;
key_value_map
&
cookies
=
incoming
.
cookies
;
std
::
string
&
path
=
incoming
.
path
;
std
::
string
&
content_type
=
incoming
.
content_type
;
unsigned
long
content_length
=
0
;
string
line
;
read_with_limit
(
in
,
line
);
string
first_part_of_header
;
string
::
size_type
position_of_double_point
;
// now loop over all the incoming_headers
while
(
line
.
size
()
>
2
)
{
position_of_double_point
=
line
.
find_first_of
(
':'
);
if
(
position_of_double_point
!=
string
::
npos
)
{
first_part_of_header
=
dlib
::
trim
(
line
.
substr
(
0
,
position_of_double_point
));
if
(
!
incoming_headers
[
first_part_of_header
].
empty
()
)
incoming_headers
[
first_part_of_header
]
+=
" "
;
incoming_headers
[
first_part_of_header
]
+=
dlib
::
trim
(
line
.
substr
(
position_of_double_point
+
1
));
// look for Content-Type:
if
(
line
.
size
()
>
14
&&
strings_equal_ignore_case
(
line
,
"Content-Type:"
,
13
))
{
content_type
=
line
.
substr
(
14
);
if
(
content_type
[
content_type
.
size
()
-
1
]
==
'\r'
)
content_type
.
erase
(
content_type
.
size
()
-
1
);
}
// look for Content-Length:
else
if
(
line
.
size
()
>
16
&&
strings_equal_ignore_case
(
line
,
"Content-Length:"
,
15
))
{
istringstream
sin
(
line
.
substr
(
16
));
sin
>>
content_length
;
if
(
!
sin
)
{
throw
http_parse_error
(
"Invalid Content-Length of '"
+
line
.
substr
(
16
)
+
"'"
,
411
);
}
if
(
content_length
>
max_content_length
)
{
std
::
ostringstream
sout
;
sout
<<
"Content-Length of post back is too large. It must be less than "
<<
max_content_length
;
throw
http_parse_error
(
sout
.
str
(),
413
);
}
}
// look for any cookies
else
if
(
line
.
size
()
>
6
&&
strings_equal_ignore_case
(
line
,
"Cookie:"
,
7
))
{
string
::
size_type
pos
=
6
;
string
key
,
value
;
bool
seen_key_start
=
false
;
bool
seen_equal_sign
=
false
;
while
(
pos
+
1
<
line
.
size
())
{
++
pos
;
// ignore whitespace between cookies
if
(
!
seen_key_start
&&
line
[
pos
]
==
' '
)
continue
;
seen_key_start
=
true
;
if
(
!
seen_equal_sign
)
{
if
(
line
[
pos
]
==
'='
)
{
seen_equal_sign
=
true
;
}
else
{
key
+=
line
[
pos
];
}
}
else
{
if
(
line
[
pos
]
==
';'
)
{
cookies
[
urldecode
(
key
)]
=
urldecode
(
value
);
seen_equal_sign
=
false
;
seen_key_start
=
false
;
key
.
clear
();
value
.
clear
();
}
else
{
value
+=
line
[
pos
];
}
}
}
if
(
key
.
size
()
>
0
)
{
cookies
[
urldecode
(
key
)]
=
urldecode
(
value
);
key
.
clear
();
value
.
clear
();
}
}
}
// no ':' in it!
read_with_limit
(
in
,
line
);
}
// while (line.size() > 2 )
// If there is data being posted back to us as a query string then
// pick out the queries using parse_url.
if
((
strings_equal_ignore_case
(
incoming
.
request_type
,
"POST"
)
||
strings_equal_ignore_case
(
incoming
.
request_type
,
"PUT"
))
&&
strings_equal_ignore_case
(
left_substr
(
content_type
,
";"
),
"application/x-www-form-urlencoded"
))
{
if
(
content_length
>
0
)
{
incoming
.
body
.
resize
(
content_length
);
in
.
read
(
&
incoming
.
body
[
0
],
content_length
);
}
parse_url
(
incoming
.
body
,
incoming
.
queries
);
}
string
::
size_type
pos
=
path
.
find_first_of
(
"?"
);
if
(
pos
!=
string
::
npos
)
{
parse_url
(
path
.
substr
(
pos
+
1
),
incoming
.
queries
);
}
if
(
!
in
)
throw
http_parse_error
(
"Error parsing HTTP request"
,
500
);
return
content_length
;
}
// ----------------------------------------------------------------------------------------
void
read_body
(
std
::
istream
&
in
,
incoming_things
&
incoming
)
{
// if the body hasn't already been loaded and there is data to load
if
(
incoming
.
body
.
size
()
==
0
&&
incoming
.
headers
.
count
(
"Content-Length"
)
!=
0
)
{
const
unsigned
long
content_length
=
string_cast
<
unsigned
long
>
(
incoming
.
headers
[
"Content-Length"
]);
incoming
.
body
.
resize
(
content_length
);
if
(
content_length
>
0
)
{
in
.
read
(
&
incoming
.
body
[
0
],
content_length
);
}
}
}
// ----------------------------------------------------------------------------------------
void
write_http_response
(
std
::
ostream
&
out
,
outgoing_things
outgoing
,
const
std
::
string
&
result
)
{
using
namespace
http_impl
;
key_value_map
&
new_cookies
=
outgoing
.
cookies
;
key_value_map
&
response_headers
=
outgoing
.
headers
;
// only send this header if the user hasn't told us to send another kind
bool
has_content_type
=
false
,
has_location
=
false
;
for
(
typename
key_value_map
::
const_iterator
ci
=
response_headers
.
begin
();
ci
!=
response_headers
.
end
();
++
ci
)
{
if
(
!
has_content_type
&&
strings_equal_ignore_case
(
ci
->
first
,
"content-type"
)
)
{
has_content_type
=
true
;
}
else
if
(
!
has_location
&&
strings_equal_ignore_case
(
ci
->
first
,
"location"
)
)
{
has_location
=
true
;
}
}
if
(
has_location
)
{
outgoing
.
http_return
=
302
;
}
if
(
!
has_content_type
)
{
response_headers
[
"Content-Type"
]
=
"text/html"
;
}
response_headers
[
"Content-Length"
]
=
cast_to_string
(
result
.
size
());
out
<<
"HTTP/1.0 "
<<
outgoing
.
http_return
<<
" "
<<
outgoing
.
http_return_status
<<
"
\r\n
"
;
// Set any new headers
for
(
typename
key_value_map
::
const_iterator
ci
=
response_headers
.
begin
();
ci
!=
response_headers
.
end
();
++
ci
)
{
out
<<
ci
->
first
<<
": "
<<
ci
->
second
<<
"
\r\n
"
;
}
// set any cookies
for
(
typename
key_value_map
::
const_iterator
ci
=
new_cookies
.
begin
();
ci
!=
new_cookies
.
end
();
++
ci
)
{
out
<<
"Set-Cookie: "
<<
urlencode
(
ci
->
first
)
<<
'='
<<
urlencode
(
ci
->
second
)
<<
"
\r\n
"
;
}
out
<<
"
\r\n
"
<<
result
;
}
// ----------------------------------------------------------------------------------------
void
write_http_response
(
std
::
ostream
&
out
,
const
http_parse_error
&
e
)
{
outgoing_things
outgoing
;
outgoing
.
http_return
=
e
.
http_error_code
;
outgoing
.
http_return_status
=
e
.
what
();
write_http_response
(
out
,
outgoing
,
std
::
string
(
"Error processing request: "
)
+
e
.
what
());
}
// ----------------------------------------------------------------------------------------
void
write_http_response
(
std
::
ostream
&
out
,
const
std
::
exception
&
e
)
{
outgoing_things
outgoing
;
outgoing
.
http_return
=
500
;
outgoing
.
http_return_status
=
e
.
what
();
write_http_response
(
out
,
outgoing
,
std
::
string
(
"Error processing request: "
)
+
e
.
what
());
}
// ----------------------------------------------------------------------------------------
const
logger
server_http
::
dlog
(
"dlib.server_http"
);
// ----------------------------------------------------------------------------------------
}
#endif // DLIB_SERVER_HTTP_CPp_
dlib/server/server_http.h
View file @
c2825e86
...
...
@@ -4,11 +4,11 @@
#define DLIB_SERVER_HTTp_1_
#include "server_iostream_abstract.h"
#include "server_http_abstract.h"
#include <iostream>
#include <sstream>
#include <string>
#include <map>
#include "../logger.h"
#include "../string.h"
#include "server_iostream.h"
...
...
@@ -106,401 +106,38 @@ namespace dlib
// ----------------------------------------------------------------------------------------
namespace
http_impl
{
inline
unsigned
char
to_hex
(
unsigned
char
x
)
{
return
x
+
(
x
>
9
?
(
'A'
-
10
)
:
'0'
);
}
inline
const
std
::
string
urlencode
(
const
std
::
string
&
s
)
{
std
::
ostringstream
os
;
for
(
std
::
string
::
const_iterator
ci
=
s
.
begin
();
ci
!=
s
.
end
();
++
ci
)
{
if
(
(
*
ci
>=
'a'
&&
*
ci
<=
'z'
)
||
(
*
ci
>=
'A'
&&
*
ci
<=
'Z'
)
||
(
*
ci
>=
'0'
&&
*
ci
<=
'9'
)
)
{
// allowed
os
<<
*
ci
;
}
else
if
(
*
ci
==
' '
)
{
os
<<
'+'
;
}
else
{
os
<<
'%'
<<
to_hex
(
*
ci
>>
4
)
<<
to_hex
(
*
ci
%
16
);
}
}
return
os
.
str
();
}
inline
unsigned
char
from_hex
(
unsigned
char
ch
)
{
if
(
ch
<=
'9'
&&
ch
>=
'0'
)
ch
-=
'0'
;
else
if
(
ch
<=
'f'
&&
ch
>=
'a'
)
ch
-=
'a'
-
10
;
else
if
(
ch
<=
'F'
&&
ch
>=
'A'
)
ch
-=
'A'
-
10
;
else
ch
=
0
;
return
ch
;
}
inline
const
std
::
string
urldecode
(
const
std
::
string
&
str
)
{
using
namespace
std
;
string
result
;
string
::
size_type
i
;
for
(
i
=
0
;
i
<
str
.
size
();
++
i
)
{
if
(
str
[
i
]
==
'+'
)
{
result
+=
' '
;
}
else
if
(
str
[
i
]
==
'%'
&&
str
.
size
()
>
i
+
2
)
{
const
unsigned
char
ch1
=
from_hex
(
str
[
i
+
1
]);
const
unsigned
char
ch2
=
from_hex
(
str
[
i
+
2
]);
const
unsigned
char
ch
=
(
ch1
<<
4
)
|
ch2
;
result
+=
ch
;
i
+=
2
;
}
else
{
result
+=
str
[
i
];
}
}
return
result
;
}
inline
void
parse_url
(
std
::
string
word
,
key_value_map
&
queries
)
/*!
Parses the query string of a URL. word should be the stuff that comes
after the ? in the query URL.
!*/
{
std
::
string
::
size_type
pos
;
for
(
pos
=
0
;
pos
<
word
.
size
();
++
pos
)
{
if
(
word
[
pos
]
==
'&'
)
word
[
pos
]
=
' '
;
}
std
::
istringstream
sin
(
word
);
sin
>>
word
;
while
(
sin
)
{
pos
=
word
.
find_first_of
(
"="
);
if
(
pos
!=
std
::
string
::
npos
)
{
std
::
string
key
=
urldecode
(
word
.
substr
(
0
,
pos
));
std
::
string
value
=
urldecode
(
word
.
substr
(
pos
+
1
));
queries
[
key
]
=
value
;
}
sin
>>
word
;
}
}
inline
void
read_with_limit
(
std
::
istream
&
in
,
std
::
string
&
buffer
,
int
delim
=
'\n'
)
{
using
namespace
std
;
const
size_t
max
=
16
*
1024
;
buffer
.
clear
();
buffer
.
reserve
(
300
);
while
(
in
.
peek
()
!=
delim
&&
in
.
peek
()
!=
EOF
&&
buffer
.
size
()
<
max
)
{
buffer
+=
(
char
)
in
.
get
();
}
// if we quit the loop because the data is longer than expected or we hit EOF
if
(
in
.
peek
()
==
EOF
||
buffer
.
size
()
==
max
)
throw
http_parse_error
(
"HTTP field from client is too long"
,
414
);
// Make sure the last char is the delim.
if
(
in
.
get
()
!=
delim
)
{
in
.
setstate
(
ios
::
badbit
);
buffer
.
clear
();
}
else
{
// Read the remaining delimiters
if
(
delim
==
' '
)
{
while
(
in
.
peek
()
==
' '
)
in
.
get
();
}
}
}
}
inline
unsigned
long
parse_http_request
(
unsigned
long
parse_http_request
(
std
::
istream
&
in
,
incoming_things
&
incoming
,
unsigned
long
max_content_length
)
{
using
namespace
std
;
using
namespace
http_impl
;
read_with_limit
(
in
,
incoming
.
request_type
,
' '
);
// get the path
read_with_limit
(
in
,
incoming
.
path
,
' '
);
// Get the HTTP/1.1 - Ignore for now...
read_with_limit
(
in
,
incoming
.
protocol
);
key_value_map
&
incoming_headers
=
incoming
.
headers
;
key_value_map
&
cookies
=
incoming
.
cookies
;
std
::
string
&
path
=
incoming
.
path
;
std
::
string
&
content_type
=
incoming
.
content_type
;
unsigned
long
content_length
=
0
;
string
line
;
read_with_limit
(
in
,
line
);
string
first_part_of_header
;
string
::
size_type
position_of_double_point
;
// now loop over all the incoming_headers
while
(
line
.
size
()
>
2
)
{
position_of_double_point
=
line
.
find_first_of
(
':'
);
if
(
position_of_double_point
!=
string
::
npos
)
{
first_part_of_header
=
dlib
::
trim
(
line
.
substr
(
0
,
position_of_double_point
));
if
(
!
incoming_headers
[
first_part_of_header
].
empty
()
)
incoming_headers
[
first_part_of_header
]
+=
" "
;
incoming_headers
[
first_part_of_header
]
+=
dlib
::
trim
(
line
.
substr
(
position_of_double_point
+
1
));
// look for Content-Type:
if
(
line
.
size
()
>
14
&&
strings_equal_ignore_case
(
line
,
"Content-Type:"
,
13
))
{
content_type
=
line
.
substr
(
14
);
if
(
content_type
[
content_type
.
size
()
-
1
]
==
'\r'
)
content_type
.
erase
(
content_type
.
size
()
-
1
);
}
// look for Content-Length:
else
if
(
line
.
size
()
>
16
&&
strings_equal_ignore_case
(
line
,
"Content-Length:"
,
15
))
{
istringstream
sin
(
line
.
substr
(
16
));
sin
>>
content_length
;
if
(
!
sin
)
{
throw
http_parse_error
(
"Invalid Content-Length of '"
+
line
.
substr
(
16
)
+
"'"
,
411
);
}
if
(
content_length
>
max_content_length
)
{
std
::
ostringstream
sout
;
sout
<<
"Content-Length of post back is too large. It must be less than "
<<
max_content_length
;
throw
http_parse_error
(
sout
.
str
(),
413
);
}
}
// look for any cookies
else
if
(
line
.
size
()
>
6
&&
strings_equal_ignore_case
(
line
,
"Cookie:"
,
7
))
{
string
::
size_type
pos
=
6
;
string
key
,
value
;
bool
seen_key_start
=
false
;
bool
seen_equal_sign
=
false
;
while
(
pos
+
1
<
line
.
size
())
{
++
pos
;
// ignore whitespace between cookies
if
(
!
seen_key_start
&&
line
[
pos
]
==
' '
)
continue
;
seen_key_start
=
true
;
if
(
!
seen_equal_sign
)
{
if
(
line
[
pos
]
==
'='
)
{
seen_equal_sign
=
true
;
}
else
{
key
+=
line
[
pos
];
}
}
else
{
if
(
line
[
pos
]
==
';'
)
{
cookies
[
urldecode
(
key
)]
=
urldecode
(
value
);
seen_equal_sign
=
false
;
seen_key_start
=
false
;
key
.
clear
();
value
.
clear
();
}
else
{
value
+=
line
[
pos
];
}
}
}
if
(
key
.
size
()
>
0
)
{
cookies
[
urldecode
(
key
)]
=
urldecode
(
value
);
key
.
clear
();
value
.
clear
();
}
}
}
// no ':' in it!
read_with_limit
(
in
,
line
);
}
// while (line.size() > 2 )
// If there is data being posted back to us as a query string then
// pick out the queries using parse_url.
if
((
strings_equal_ignore_case
(
incoming
.
request_type
,
"POST"
)
||
strings_equal_ignore_case
(
incoming
.
request_type
,
"PUT"
))
&&
strings_equal_ignore_case
(
left_substr
(
content_type
,
";"
),
"application/x-www-form-urlencoded"
))
{
if
(
content_length
>
0
)
{
incoming
.
body
.
resize
(
content_length
);
in
.
read
(
&
incoming
.
body
[
0
],
content_length
);
}
parse_url
(
incoming
.
body
,
incoming
.
queries
);
}
string
::
size_type
pos
=
path
.
find_first_of
(
"?"
);
if
(
pos
!=
string
::
npos
)
{
parse_url
(
path
.
substr
(
pos
+
1
),
incoming
.
queries
);
}
);
if
(
!
in
)
throw
http_parse_error
(
"Error parsing HTTP request"
,
500
);
return
content_length
;
}
inline
void
read_body
(
void
read_body
(
std
::
istream
&
in
,
incoming_things
&
incoming
)
{
// if the body hasn't already been loaded and there is data to load
if
(
incoming
.
body
.
size
()
==
0
&&
incoming
.
headers
.
count
(
"Content-Length"
)
!=
0
)
{
const
unsigned
long
content_length
=
string_cast
<
unsigned
long
>
(
incoming
.
headers
[
"Content-Length"
]);
);
incoming
.
body
.
resize
(
content_length
);
if
(
content_length
>
0
)
{
in
.
read
(
&
incoming
.
body
[
0
],
content_length
);
}
}
}
inline
void
write_http_response
(
void
write_http_response
(
std
::
ostream
&
out
,
outgoing_things
outgoing
,
const
std
::
string
&
result
)
{
using
namespace
http_impl
;
key_value_map
&
new_cookies
=
outgoing
.
cookies
;
key_value_map
&
response_headers
=
outgoing
.
headers
;
// only send this header if the user hasn't told us to send another kind
bool
has_content_type
=
false
,
has_location
=
false
;
for
(
typename
key_value_map
::
const_iterator
ci
=
response_headers
.
begin
();
ci
!=
response_headers
.
end
();
++
ci
)
{
if
(
!
has_content_type
&&
strings_equal_ignore_case
(
ci
->
first
,
"content-type"
)
)
{
has_content_type
=
true
;
}
else
if
(
!
has_location
&&
strings_equal_ignore_case
(
ci
->
first
,
"location"
)
)
{
has_location
=
true
;
}
}
);
if
(
has_location
)
{
outgoing
.
http_return
=
302
;
}
if
(
!
has_content_type
)
{
response_headers
[
"Content-Type"
]
=
"text/html"
;
}
response_headers
[
"Content-Length"
]
=
cast_to_string
(
result
.
size
());
out
<<
"HTTP/1.0 "
<<
outgoing
.
http_return
<<
" "
<<
outgoing
.
http_return_status
<<
"
\r\n
"
;
// Set any new headers
for
(
typename
key_value_map
::
const_iterator
ci
=
response_headers
.
begin
();
ci
!=
response_headers
.
end
();
++
ci
)
{
out
<<
ci
->
first
<<
": "
<<
ci
->
second
<<
"
\r\n
"
;
}
// set any cookies
for
(
typename
key_value_map
::
const_iterator
ci
=
new_cookies
.
begin
();
ci
!=
new_cookies
.
end
();
++
ci
)
{
out
<<
"Set-Cookie: "
<<
urlencode
(
ci
->
first
)
<<
'='
<<
urlencode
(
ci
->
second
)
<<
"
\r\n
"
;
}
out
<<
"
\r\n
"
<<
result
;
}
inline
void
write_http_response
(
void
write_http_response
(
std
::
ostream
&
out
,
const
http_parse_error
&
e
)
{
outgoing_things
outgoing
;
outgoing
.
http_return
=
e
.
http_error_code
;
outgoing
.
http_return_status
=
e
.
what
();
write_http_response
(
out
,
outgoing
,
std
::
string
(
"Error processing request: "
)
+
e
.
what
());
}
);
inline
void
write_http_response
(
void
write_http_response
(
std
::
ostream
&
out
,
const
std
::
exception
&
e
)
{
outgoing_things
outgoing
;
outgoing
.
http_return
=
500
;
outgoing
.
http_return_status
=
e
.
what
();
write_http_response
(
out
,
outgoing
,
std
::
string
(
"Error processing request: "
)
+
e
.
what
());
}
);
// ----------------------------------------------------------------------------------------
class
server_http
:
public
server_iostream
{
/*!
CONVENTION
this extension doesn't add any new state to this object.
!*/
public
:
server_http
()
...
...
@@ -531,7 +168,7 @@ namespace dlib
)
=
0
;
void
on_connect
(
v
irtual
v
oid
on_connect
(
std
::
istream
&
in
,
std
::
ostream
&
out
,
const
std
::
string
&
foreign_ip
,
...
...
@@ -546,7 +183,7 @@ namespace dlib
incoming_things
incoming
(
foreign_ip
,
local_ip
,
foreign_port
,
local_port
);
outgoing_things
outgoing
;
parse_http_request
(
in
,
incoming
,
max_content_length
);
parse_http_request
(
in
,
incoming
,
get_max_content_length
()
);
read_body
(
in
,
incoming
);
const
std
::
string
&
result
=
on_request
(
incoming
,
outgoing
);
write_http_response
(
out
,
outgoing
,
result
);
...
...
@@ -568,8 +205,13 @@ namespace dlib
const
static
logger
dlog
;
};
const
logger
server_http
::
dlog
(
"dlib.server"
);
// ----------------------------------------------------------------------------------------
}
#ifdef NO_MAKEFILE
#include "server_http.cpp"
#endif
#endif // DLIB_SERVER_HTTp_1_
dlib/server/server_http_abstract.h
View file @
c2825e86
...
...
@@ -3,29 +3,228 @@
#undef DLIB_SERVER_HTTp_ABSTRACT_
#ifdef DLIB_SERVER_HTTp_ABSTRACT_
#include "server_iostream_abstract.h"
#include <iostream>
#include <sstream>
#include <string>
#include <map>
namespace
dlib
{
// -----------------------------------------------------------------------------------------
template
<
typename
server_base
typename
Key
,
typename
Value
>
class
server_http
:
public
server_base
class
constmap
:
public
std
::
map
<
Key
,
Value
>
{
/*!
WHAT THIS OBJECT REPRESENTS
This is simply an extension to the std::map that allows you
to use the operator[] accessor with a constant map.
!*/
public
:
const
Value
&
operator
[](
const
Key
&
k
)
const
;
/*!
ensures
- if (this->find(k) != this->end()) then
- This map contains the given key
- return the value associated with the given key
- else
- return a default initialized Value object
!*/
Value
&
operator
[](
const
Key
&
k
)
{
return
std
::
map
<
Key
,
Value
>::
operator
[](
k
);
}
/*!
ensures
- This function does the same thing as the normal std::map operator[]
function.
- if (this->find(k) != this->end()) then
- This map contains the given key
- return the value associated with the given key
- else
- Adds a new entry into the map that is associated with the
given key. The new entry will be default initialized and
this function returns a reference to it.
!*/
};
typedef
constmap
<
std
::
string
,
std
::
string
>
key_value_map
;
// -----------------------------------------------------------------------------------------
struct
incoming_things
{
/*!
WHAT THIS OBJECT REPRESENTS
This object contains all the various bits of information that describe a
HTTP request that comes into a web server.
For a detailed discussion of the fields of this object, see the
server_http::on_request() method defined later in this file.
!*/
incoming_things
(
const
std
::
string
&
foreign_ip_
,
const
std
::
string
&
local_ip_
,
unsigned
short
foreign_port_
,
unsigned
short
local_port_
);
/*!
ensures
- #foreign_ip = foreign_ip_
- #foreign_port = foreign_port_
- #local_ip = local_ip_
- #local_port = local_port_
!*/
std
::
string
path
;
std
::
string
request_type
;
std
::
string
content_type
;
std
::
string
protocol
;
std
::
string
body
;
key_value_map
queries
;
key_value_map
cookies
;
key_value_map
headers
;
std
::
string
foreign_ip
;
unsigned
short
foreign_port
;
std
::
string
local_ip
;
unsigned
short
local_port
;
};
struct
outgoing_things
{
/*!
WHAT THIS OBJECT REPRESENTS
This object contains all the various bits of information that describe a
HTTP response from a web server.
For a detailed discussion of the fields of this object, see the
server_http::on_request() method defined later in this file.
!*/
outgoing_things
(
);
/*!
REQUIREMENTS ON server_base
is an implementation of server/server_iostream_abstract.h
ensures
- #http_return == 200
- #http_return_status == "OK"
!*/
key_value_map
cookies
;
key_value_map
headers
;
unsigned
short
http_return
;
std
::
string
http_return_status
;
};
// -----------------------------------------------------------------------------------------
class
http_parse_error
:
public
error
{
/*!
WHAT THIS OBJECT REPRESENTS
This is an exception thrown by the parse_http_request() routine if
there is a problem.
!*/
};
// -----------------------------------------------------------------------------------------
unsigned
long
parse_http_request
(
std
::
istream
&
in
,
incoming_things
&
incoming
,
unsigned
long
max_content_length
);
/*!
ensures
- Attempts to read a HTTP GET, POST, or PUT request from the given input
stream.
- Reads all headers of the request and puts them into #incoming. In particular,
this function populates the following fields:
- #incoming.path
- #incoming.request_type
- #incoming.content_type
- #incoming.protocol
- #incoming.queries
- #incoming.cookies
- #incoming.headers
- This function also populates the #incoming.body field if and only if the
Content-Type field is equal to "application/x-www-form-urlencoded".
Otherwise, the content is not read from the stream.
throws
- http_parse_error
This exception is thrown if the Content-Length coming from the web
browser is greater than max_content_length or if any other problem
is detected with the request.
!*/
void
read_body
(
std
::
istream
&
in
,
incoming_things
&
incoming
);
/*!
requires
- parse_http_request(in,incoming,max_content_length) has already been called
and therefore populated the fields of incoming.
ensures
- if (incoming.body has already been populated with the content of an HTTP
request) then
- this function does nothing
- else
- reads the body of the HTTP request into #incoming.body.
!*/
void
write_http_response
(
std
::
ostream
&
out
,
outgoing_things
outgoing
,
const
std
::
string
&
result
);
/*!
ensures
- Writes an HTTP response, defined by the data in outgoing, to the given output
stream.
- The result variable is written out as the content of the response.
!*/
void
write_http_response
(
std
::
ostream
&
out
,
const
http_parse_error
&
e
);
/*!
ensures
- Writes an HTTP error response based on the information in the exception
object e.
!*/
WHAT THIS EXTENSION DOES FOR SERVER IOSTREAM
This extension turns the server object into a simple HTTP server.
It only handles HTTP GET and POST requests and each incoming request triggers the
on_request() callback.
void
write_http_response
(
std
::
ostream
&
out
,
const
std
::
exception
&
e
);
/*!
ensures
- Writes an HTTP error response based on the information in the exception
object e.
!*/
// -----------------------------------------------------------------------------------------
// -----------------------------------------------------------------------------------------
class
server_http
:
public
server_iostream
{
/*!
WHAT THIS EXTENSION DOES FOR server_iostream
This extension turns the server object into a simple HTTP server. It only
handles HTTP GET, PUT and POST requests and each incoming request triggers
the on_request() callback.
COOKIE STRINGS
The strings returned in the cookies key_value_map should be of the following form:
...
...
@@ -74,73 +273,6 @@ namespace dlib
- #get_max_content_length() == max_length
!*/
template
<
typename
Key
,
typename
Value
>
class
constmap
:
public
std
::
map
<
Key
,
Value
>
{
/*!
WHAT THIS OBJECT REPRESENTS
This is simply an extension to the std::map that allows you
to use the operator[] accessor with a constant map.
!*/
public
:
const
Value
&
operator
[](
const
Key
&
k
)
const
;
/*!
ensures
- if (this->find(k) != this->end()) then
- This map contains the given key
- return the value associated with the given key
- else
- return a default initialized Value object
!*/
Value
&
operator
[](
const
Key
&
k
)
{
return
std
::
map
<
Key
,
Value
>::
operator
[](
k
);
}
/*!
ensures
- This function does the same thing as the normal std::map operator[]
function.
- if (this->find(k) != this->end()) then
- This map contains the given key
- return the value associated with the given key
- else
- Adds a new entry into the map that is associated with the
given key. The new entry will be default initialized and
this function returns a reference to it.
!*/
};
typedef
constmap
<
std
::
string
,
std
::
string
>
key_value_map
;
struct
incoming_things
{
std
::
string
path
;
std
::
string
request_type
;
std
::
string
content_type
;
std
::
string
protocol
;
std
::
string
body
;
key_value_map
queries
;
key_value_map
cookies
;
key_value_map
headers
;
std
::
string
foreign_ip
;
unsigned
short
foreign_port
;
std
::
string
local_ip
;
unsigned
short
local_port
;
};
struct
outgoing_things
{
key_value_map
cookies
;
key_value_map
headers
;
unsigned
short
http_return
;
std
::
string
http_return_status
;
};
private
:
virtual
const
std
::
string
on_request
(
...
...
@@ -197,6 +329,52 @@ namespace dlib
then the error string from the exception is returned to the web browser.
!*/
// -----------------------------------------------------------------------
// Implementation Notes
// -----------------------------------------------------------------------
virtual
void
on_connect
(
std
::
istream
&
in
,
std
::
ostream
&
out
,
const
std
::
string
&
foreign_ip
,
const
std
::
string
&
local_ip
,
unsigned
short
foreign_port
,
unsigned
short
local_port
,
uint64
)
/*!
on_connect() is the function defined by server_iostream which is overloaded by
server_http. In particular, the server_http's implementation is shown below.
In it you can see how the server_http parses the incoming http request, gets a
response by calling on_request(), and sends it back using the helper routines
defined at the top of this file.
Therefore, if you want to modify the behavior of the HTTP server, for example,
to do some more complex data streaming requiring direct access to the
iostreams, then you can do so by defining your own on_connect() routine. In
particular, the default implementation shown below is a good starting point.
!*/
{
try
{
incoming_things
incoming
(
foreign_ip
,
local_ip
,
foreign_port
,
local_port
);
outgoing_things
outgoing
;
parse_http_request
(
in
,
incoming
,
get_max_content_length
());
read_body
(
in
,
incoming
);
const
std
::
string
&
result
=
on_request
(
incoming
,
outgoing
);
write_http_response
(
out
,
outgoing
,
result
);
}
catch
(
http_parse_error
&
e
)
{
write_http_response
(
out
,
e
);
}
catch
(
std
::
exception
&
e
)
{
write_http_response
(
out
,
e
);
}
}
};
}
...
...
dlib/server/server_iostream.cpp
0 → 100644
View file @
c2825e86
// Copyright (C) 2003 Davis E. King (davis@dlib.net)
// License: Boost Software License See LICENSE.txt for the full license.
#ifndef DLIB_SERVER_IOSTREAM_CPp_
#define DLIB_SERVER_IOSTREAM_CPp_
#include "server_iostream.h"
namespace
dlib
{
const
logger
server_iostream
::
dlog
(
"dlib.server_iostream"
);
}
#endif // DLIB_SERVER_IOSTREAM_CPp_
dlib/server/server_iostream.h
View file @
c2825e86
...
...
@@ -143,10 +143,13 @@ namespace dlib
};
const
logger
server_iostream
::
dlog
(
"dlib.server"
);
}
#ifdef NO_MAKEFILE
#include "server_iostream.cpp"
#endif
#endif // DLIB_SERVER_IOSTREAm_1_
...
...
dlib/server/server_iostream_abstract.h
View file @
c2825e86
...
...
@@ -12,17 +12,11 @@
namespace
dlib
{
template
<
typename
server_base
>
class
server_iostream
:
public
server_base
class
server_iostream
:
public
server
{
/*!
REQUIREMENTS ON server_base
is an implementation of server/server_kernel_abstract.h
WHAT THIS EXTENSION DOES FOR SERVER
WHAT THIS EXTENSION DOES FOR server
This extension redefines the on_connect() function so that
instead of giving you a connection object you get an istream
and ostream object.
...
...
dlib/server/server_kernel.cpp
0 → 100644
View file @
c2825e86
// Copyright (C) 2003 Davis E. King (davis@dlib.net)
// License: Boost Software License See LICENSE.txt for the full license.
#ifndef DLIB_SERVER_KERNEL_CPp_
#define DLIB_SERVER_KERNEL_CPp_
#include "server_kernel.h"
#include "../string.h"
namespace
dlib
{
// ----------------------------------------------------------------------------------------
server
::
server
(
)
:
listening_port
(
0
),
running
(
false
),
shutting_down
(
false
),
running_signaler
(
running_mutex
),
thread_count
(
0
),
thread_count_signaler
(
thread_count_mutex
),
max_connections
(
1000
),
thread_count_zero
(
thread_count_mutex
)
{
}
// ----------------------------------------------------------------------------------------
server
::
~
server
(
)
{
clear
();
}
// ----------------------------------------------------------------------------------------
int
server
::
get_max_connections
(
)
const
{
max_connections_mutex
.
lock
();
int
temp
=
max_connections
;
max_connections_mutex
.
unlock
();
return
temp
;
}
// ----------------------------------------------------------------------------------------
void
server
::
set_max_connections
(
int
max
)
{
// make sure requires clause is not broken
DLIB_CASSERT
(
max
>=
0
,
"
\t
void server::set_max_connections"
<<
"
\n\t
max == "
<<
max
<<
"
\n\t
this: "
<<
this
);
max_connections_mutex
.
lock
();
max_connections
=
max
;
max_connections_mutex
.
unlock
();
}
// ----------------------------------------------------------------------------------------
void
server
::
clear
(
)
{
// signal that we are shutting down
shutting_down_mutex
.
lock
();
shutting_down
=
true
;
shutting_down_mutex
.
unlock
();
max_connections_mutex
.
lock
();
listening_port_mutex
.
lock
();
listening_ip_mutex
.
lock
();
listening_ip
=
""
;
listening_port
=
0
;
max_connections
=
1000
;
listening_port_mutex
.
unlock
();
listening_ip_mutex
.
unlock
();
max_connections_mutex
.
unlock
();
// tell all the connections to shut down
cons_mutex
.
lock
();
connection
*
temp
;
while
(
cons
.
size
()
>
0
)
{
cons
.
remove_any
(
temp
);
temp
->
shutdown
();
}
cons_mutex
.
unlock
();
// wait for all the connections to shut down
thread_count_mutex
.
lock
();
while
(
thread_count
>
0
)
{
thread_count_zero
.
wait
();
}
thread_count_mutex
.
unlock
();
// wait for the listener to close
running_mutex
.
lock
();
while
(
running
==
true
)
{
running_signaler
.
wait
();
}
running_mutex
.
unlock
();
// signal that the shutdown is complete
shutting_down_mutex
.
lock
();
shutting_down
=
false
;
shutting_down_mutex
.
unlock
();
}
// ----------------------------------------------------------------------------------------
void
server
::
start_async_helper
(
)
{
try
{
start_accepting_connections
();
}
catch
(
std
::
exception
&
e
)
{
sdlog
<<
LERROR
<<
e
.
what
();
}
}
// ----------------------------------------------------------------------------------------
void
server
::
start_async
(
)
{
auto_mutex
lock
(
running_mutex
);
if
(
running
)
return
;
// Any exceptions likely to be thrown by the server are going to be
// thrown when trying to bind the port. So calling this here rather
// than in the thread we are about to make will cause start_async()
// to report errors back to the user in a very straight forward way.
open_listening_socket
();
member_function_pointer
<>::
kernel_1a
mfp
;
mfp
.
set
(
*
this
,
&
server
::
start_async_helper
);
async_start_thread
.
reset
(
new
thread_function
(
mfp
));
}
// ----------------------------------------------------------------------------------------
void
server
::
open_listening_socket
(
)
{
if
(
!
sock
)
{
int
status
=
create_listener
(
sock
,
listening_port
,
listening_ip
);
const
int
port_used
=
listening_port
;
// if there was an error then clear this object
if
(
status
<
0
)
{
max_connections_mutex
.
lock
();
listening_port_mutex
.
lock
();
listening_ip_mutex
.
lock
();
listening_ip
=
""
;
listening_port
=
0
;
max_connections
=
1000
;
listening_port_mutex
.
unlock
();
listening_ip_mutex
.
unlock
();
max_connections_mutex
.
unlock
();
}
// throw an exception for the error
if
(
status
==
PORTINUSE
)
{
throw
dlib
::
socket_error
(
EPORT_IN_USE
,
"error occurred in server::start()
\n
port "
+
cast_to_string
(
port_used
)
+
" already in use"
);
}
else
if
(
status
==
OTHER_ERROR
)
{
throw
dlib
::
socket_error
(
"error occurred in server::start()
\n
unable to create listener"
);
}
}
running_mutex
.
lock
();
running
=
true
;
running_mutex
.
unlock
();
}
// ----------------------------------------------------------------------------------------
void
server
::
start
(
)
{
// make sure requires clause is not broken
DLIB_CASSERT
(
this
->
is_running
()
==
false
,
"
\t
void server::start"
<<
"
\n\t
is_running() == "
<<
this
->
is_running
()
<<
"
\n\t
this: "
<<
this
);
start_accepting_connections
();
}
// ----------------------------------------------------------------------------------------
void
server
::
start_accepting_connections
(
)
{
open_listening_socket
();
// determine the listening port
bool
port_assigned
=
false
;
listening_port_mutex
.
lock
();
if
(
listening_port
==
0
)
{
port_assigned
=
true
;
listening_port
=
sock
->
get_listening_port
();
}
listening_port_mutex
.
unlock
();
if
(
port_assigned
)
on_listening_port_assigned
();
int
status
=
0
;
connection
*
client
;
bool
exit
=
false
;
while
(
true
)
{
// accept the next connection
status
=
sock
->
accept
(
client
,
1000
);
// if there was an error then quit the loop
if
(
status
==
OTHER_ERROR
)
{
break
;
}
shutting_down_mutex
.
lock
();
// if we are shutting down then signal that we should quit the loop
exit
=
shutting_down
;
shutting_down_mutex
.
unlock
();
// if we should be shutting down
if
(
exit
)
{
// if a connection was opened then close it
if
(
status
==
0
)
delete
client
;
break
;
}
// if the accept timed out
if
(
status
==
TIMEOUT
)
{
continue
;
}
// add this new connection to cons
cons_mutex
.
lock
();
connection
*
client_temp
=
client
;
try
{
cons
.
add
(
client_temp
);}
catch
(...)
{
sock
.
reset
();;
delete
client
;
cons_mutex
.
unlock
();
// signal that we are not running start() anymore
running_mutex
.
lock
();
running
=
false
;
running_signaler
.
broadcast
();
running_mutex
.
unlock
();
clear
();
throw
;
}
cons_mutex
.
unlock
();
// make a param structure
param
*
temp
=
0
;
try
{
temp
=
new
param
(
*
this
,
*
client
);
}
catch
(...)
{
sock
.
reset
();
delete
client
;
running_mutex
.
lock
();
running
=
false
;
running_signaler
.
broadcast
();
running_mutex
.
unlock
();
clear
();
throw
;
}
// if create_new_thread failed
if
(
!
create_new_thread
(
service_connection
,
temp
))
{
delete
temp
;
// close the listening socket
sock
.
reset
();
// close the new connection and remove it from cons
cons_mutex
.
lock
();
connection
*
ctemp
;
if
(
cons
.
is_member
(
client
))
{
cons
.
remove
(
client
,
ctemp
);
}
delete
client
;
cons_mutex
.
unlock
();
// signal that the listener has closed
running_mutex
.
lock
();
running
=
false
;
running_signaler
.
broadcast
();
running_mutex
.
unlock
();
// make sure the object is cleared
clear
();
// throw the exception
throw
dlib
::
thread_error
(
ECREATE_THREAD
,
"error occurred in server::start()
\n
unable to start thread"
);
}
// if we made the new thread then update thread_count
else
{
// increment the thread count
thread_count_mutex
.
lock
();
++
thread_count
;
if
(
thread_count
==
0
)
thread_count_zero
.
broadcast
();
thread_count_mutex
.
unlock
();
}
// check if we have hit the maximum allowed number of connections
max_connections_mutex
.
lock
();
// if max_connections is zero or the loop is ending then skip this
if
(
max_connections
!=
0
)
{
// wait for thread_count to be less than max_connections
thread_count_mutex
.
lock
();
while
(
thread_count
>=
max_connections
)
{
max_connections_mutex
.
unlock
();
thread_count_signaler
.
wait
();
max_connections_mutex
.
lock
();
// if we are shutting down the quit the loop
shutting_down_mutex
.
lock
();
exit
=
shutting_down
;
shutting_down_mutex
.
unlock
();
if
(
exit
)
break
;
}
thread_count_mutex
.
unlock
();
}
max_connections_mutex
.
unlock
();
if
(
exit
)
{
break
;
}
}
//while ( true )
// close the socket
sock
.
reset
();
// signal that the listener has closed
running_mutex
.
lock
();
running
=
false
;
running_signaler
.
broadcast
();
running_mutex
.
unlock
();
// if there was an error with accept then throw an exception
if
(
status
==
OTHER_ERROR
)
{
// make sure the object is cleared
clear
();
// throw the exception
throw
dlib
::
socket_error
(
"error occurred in server::start()
\n
listening socket returned error"
);
}
}
// ----------------------------------------------------------------------------------------
bool
server
::
is_running
(
)
const
{
running_mutex
.
lock
();
bool
temp
=
running
;
running_mutex
.
unlock
();
return
temp
;
}
// ----------------------------------------------------------------------------------------
const
std
::
string
server
::
get_listening_ip
(
)
const
{
listening_ip_mutex
.
lock
();
std
::
string
ip
(
listening_ip
);
listening_ip_mutex
.
unlock
();
return
ip
;
}
// ----------------------------------------------------------------------------------------
int
server
::
get_listening_port
(
)
const
{
listening_port_mutex
.
lock
();
int
port
=
listening_port
;
listening_port_mutex
.
unlock
();
return
port
;
}
// ----------------------------------------------------------------------------------------
void
server
::
set_listening_port
(
int
port
)
{
// make sure requires clause is not broken
DLIB_CASSERT
(
(
port
>=
0
&&
this
->
is_running
()
==
false
),
"
\t
void server::set_listening_port"
<<
"
\n\t
port == "
<<
port
<<
"
\n\t
is_running() == "
<<
this
->
is_running
()
<<
"
\n\t
this: "
<<
this
);
listening_port_mutex
.
lock
();
listening_port
=
port
;
listening_port_mutex
.
unlock
();
}
// ----------------------------------------------------------------------------------------
void
server
::
set_listening_ip
(
const
std
::
string
&
ip
)
{
// make sure requires clause is not broken
DLIB_CASSERT
(
(
(
is_ip_address
(
ip
)
||
ip
==
""
)
&&
this
->
is_running
()
==
false
),
"
\t
void server::set_listening_ip"
<<
"
\n\t
ip == "
<<
ip
<<
"
\n\t
is_running() == "
<<
this
->
is_running
()
<<
"
\n\t
this: "
<<
this
);
listening_ip_mutex
.
lock
();
listening_ip
=
ip
;
listening_ip_mutex
.
unlock
();
}
// ----------------------------------------------------------------------------------------
// ----------------------------------------------------------------------------------------
// static member function definitions
// ----------------------------------------------------------------------------------------
// ----------------------------------------------------------------------------------------
const
logger
server
::
sdlog
(
"dlib.server"
);
void
server
::
service_connection
(
void
*
item
)
{
param
&
p
=
*
static_cast
<
param
*>
(
item
);
p
.
the_server
.
on_connect
(
p
.
new_connection
);
// remove this connection from cons and close it
p
.
the_server
.
cons_mutex
.
lock
();
connection
*
temp
;
if
(
p
.
the_server
.
cons
.
is_member
(
&
p
.
new_connection
))
p
.
the_server
.
cons
.
remove
(
&
p
.
new_connection
,
temp
);
try
{
close_gracefully
(
&
p
.
new_connection
);
}
catch
(...)
{
sdlog
<<
LERROR
<<
"close_gracefully() threw"
;
}
p
.
the_server
.
cons_mutex
.
unlock
();
// decrement the thread count and signal if it is now zero
p
.
the_server
.
thread_count_mutex
.
lock
();
--
p
.
the_server
.
thread_count
;
p
.
the_server
.
thread_count_signaler
.
broadcast
();
if
(
p
.
the_server
.
thread_count
==
0
)
p
.
the_server
.
thread_count_zero
.
broadcast
();
p
.
the_server
.
thread_count_mutex
.
unlock
();
delete
&
p
;
}
// ----------------------------------------------------------------------------------------
}
#endif // DLIB_SERVER_KERNEL_CPp_
dlib/server/server_kernel.h
View file @
c2825e86
...
...
@@ -209,565 +209,13 @@ namespace dlib
);
};
// ----------------------------------------------------------------------------------------
// ----------------------------------------------------------------------------------------
// member function definitions
// ----------------------------------------------------------------------------------------
// ----------------------------------------------------------------------------------------
server
::
server
(
)
:
listening_port
(
0
),
running
(
false
),
shutting_down
(
false
),
running_signaler
(
running_mutex
),
thread_count
(
0
),
thread_count_signaler
(
thread_count_mutex
),
max_connections
(
1000
),
thread_count_zero
(
thread_count_mutex
)
{
}
// ----------------------------------------------------------------------------------------
server
::
~
server
(
)
{
clear
();
}
// ----------------------------------------------------------------------------------------
int
server
::
get_max_connections
(
)
const
{
max_connections_mutex
.
lock
();
int
temp
=
max_connections
;
max_connections_mutex
.
unlock
();
return
temp
;
}
// ----------------------------------------------------------------------------------------
void
server
::
set_max_connections
(
int
max
)
{
// make sure requires clause is not broken
DLIB_CASSERT
(
max
>=
0
,
"
\t
void server::set_max_connections"
<<
"
\n\t
max == "
<<
max
<<
"
\n\t
this: "
<<
this
);
max_connections_mutex
.
lock
();
max_connections
=
max
;
max_connections_mutex
.
unlock
();
}
// ----------------------------------------------------------------------------------------
void
server
::
clear
(
)
{
// signal that we are shutting down
shutting_down_mutex
.
lock
();
shutting_down
=
true
;
shutting_down_mutex
.
unlock
();
max_connections_mutex
.
lock
();
listening_port_mutex
.
lock
();
listening_ip_mutex
.
lock
();
listening_ip
=
""
;
listening_port
=
0
;
max_connections
=
1000
;
listening_port_mutex
.
unlock
();
listening_ip_mutex
.
unlock
();
max_connections_mutex
.
unlock
();
// tell all the connections to shut down
cons_mutex
.
lock
();
connection
*
temp
;
while
(
cons
.
size
()
>
0
)
{
cons
.
remove_any
(
temp
);
temp
->
shutdown
();
}
cons_mutex
.
unlock
();
// wait for all the connections to shut down
thread_count_mutex
.
lock
();
while
(
thread_count
>
0
)
{
thread_count_zero
.
wait
();
}
thread_count_mutex
.
unlock
();
// wait for the listener to close
running_mutex
.
lock
();
while
(
running
==
true
)
{
running_signaler
.
wait
();
}
running_mutex
.
unlock
();
// signal that the shutdown is complete
shutting_down_mutex
.
lock
();
shutting_down
=
false
;
shutting_down_mutex
.
unlock
();
}
// ----------------------------------------------------------------------------------------
void
server
::
start_async_helper
(
)
{
try
{
start_accepting_connections
();
}
catch
(
std
::
exception
&
e
)
{
sdlog
<<
LERROR
<<
e
.
what
();
}
}
// ----------------------------------------------------------------------------------------
void
server
::
start_async
(
)
{
auto_mutex
lock
(
running_mutex
);
if
(
running
)
return
;
// Any exceptions likely to be thrown by the server are going to be
// thrown when trying to bind the port. So calling this here rather
// than in the thread we are about to make will cause start_async()
// to report errors back to the user in a very straight forward way.
open_listening_socket
();
member_function_pointer
<>::
kernel_1a
mfp
;
mfp
.
set
(
*
this
,
&
server
::
start_async_helper
);
async_start_thread
.
reset
(
new
thread_function
(
mfp
));
}
// ----------------------------------------------------------------------------------------
void
server
::
open_listening_socket
(
)
{
if
(
!
sock
)
{
int
status
=
create_listener
(
sock
,
listening_port
,
listening_ip
);
// if there was an error then clear this object
if
(
status
<
0
)
{
max_connections_mutex
.
lock
();
listening_port_mutex
.
lock
();
listening_ip_mutex
.
lock
();
listening_ip
=
""
;
listening_port
=
0
;
max_connections
=
1000
;
listening_port_mutex
.
unlock
();
listening_ip_mutex
.
unlock
();
max_connections_mutex
.
unlock
();
}
// throw an exception for the error
if
(
status
==
PORTINUSE
)
{
throw
dlib
::
socket_error
(
EPORT_IN_USE
,
"error occurred in server::start()
\n
port already in use"
);
}
else
if
(
status
==
OTHER_ERROR
)
{
throw
dlib
::
socket_error
(
"error occurred in server::start()
\n
unable to create listener"
);
}
}
running_mutex
.
lock
();
running
=
true
;
running_mutex
.
unlock
();
}
// ----------------------------------------------------------------------------------------
void
server
::
start
(
)
{
// make sure requires clause is not broken
DLIB_CASSERT
(
this
->
is_running
()
==
false
,
"
\t
void server::start"
<<
"
\n\t
is_running() == "
<<
this
->
is_running
()
<<
"
\n\t
this: "
<<
this
);
start_accepting_connections
();
}
// ----------------------------------------------------------------------------------------
void
server
::
start_accepting_connections
(
)
{
open_listening_socket
();
// determine the listening port
bool
port_assigned
=
false
;
listening_port_mutex
.
lock
();
if
(
listening_port
==
0
)
{
port_assigned
=
true
;
listening_port
=
sock
->
get_listening_port
();
}
listening_port_mutex
.
unlock
();
if
(
port_assigned
)
on_listening_port_assigned
();
int
status
=
0
;
connection
*
client
;
bool
exit
=
false
;
while
(
true
)
{
// accept the next connection
status
=
sock
->
accept
(
client
,
1000
);
// if there was an error then quit the loop
if
(
status
==
OTHER_ERROR
)
{
break
;
}
shutting_down_mutex
.
lock
();
// if we are shutting down then signal that we should quit the loop
exit
=
shutting_down
;
shutting_down_mutex
.
unlock
();
// if we should be shutting down
if
(
exit
)
{
// if a connection was opened then close it
if
(
status
==
0
)
delete
client
;
break
;
}
// if the accept timed out
if
(
status
==
TIMEOUT
)
{
continue
;
}
// add this new connection to cons
cons_mutex
.
lock
();
connection
*
client_temp
=
client
;
try
{
cons
.
add
(
client_temp
);}
catch
(...)
{
sock
.
reset
();;
delete
client
;
cons_mutex
.
unlock
();
// signal that we are not running start() anymore
running_mutex
.
lock
();
running
=
false
;
running_signaler
.
broadcast
();
running_mutex
.
unlock
();
clear
();
throw
;
}
cons_mutex
.
unlock
();
// make a param structure
param
*
temp
=
0
;
try
{
temp
=
new
param
(
*
this
,
*
client
);
}
catch
(...)
{
sock
.
reset
();
delete
client
;
running_mutex
.
lock
();
running
=
false
;
running_signaler
.
broadcast
();
running_mutex
.
unlock
();
clear
();
throw
;
}
// if create_new_thread failed
if
(
!
create_new_thread
(
service_connection
,
temp
))
{
delete
temp
;
// close the listening socket
sock
.
reset
();
// close the new connection and remove it from cons
cons_mutex
.
lock
();
connection
*
ctemp
;
if
(
cons
.
is_member
(
client
))
{
cons
.
remove
(
client
,
ctemp
);
}
delete
client
;
cons_mutex
.
unlock
();
// signal that the listener has closed
running_mutex
.
lock
();
running
=
false
;
running_signaler
.
broadcast
();
running_mutex
.
unlock
();
// make sure the object is cleared
clear
();
// throw the exception
throw
dlib
::
thread_error
(
ECREATE_THREAD
,
"error occurred in server::start()
\n
unable to start thread"
);
}
// if we made the new thread then update thread_count
else
{
// increment the thread count
thread_count_mutex
.
lock
();
++
thread_count
;
if
(
thread_count
==
0
)
thread_count_zero
.
broadcast
();
thread_count_mutex
.
unlock
();
}
// check if we have hit the maximum allowed number of connections
max_connections_mutex
.
lock
();
// if max_connections is zero or the loop is ending then skip this
if
(
max_connections
!=
0
)
{
// wait for thread_count to be less than max_connections
thread_count_mutex
.
lock
();
while
(
thread_count
>=
max_connections
)
{
max_connections_mutex
.
unlock
();
thread_count_signaler
.
wait
();
max_connections_mutex
.
lock
();
// if we are shutting down the quit the loop
shutting_down_mutex
.
lock
();
exit
=
shutting_down
;
shutting_down_mutex
.
unlock
();
if
(
exit
)
break
;
}
thread_count_mutex
.
unlock
();
}
max_connections_mutex
.
unlock
();
if
(
exit
)
{
break
;
}
}
//while ( true )
// close the socket
sock
.
reset
();
// signal that the listener has closed
running_mutex
.
lock
();
running
=
false
;
running_signaler
.
broadcast
();
running_mutex
.
unlock
();
// if there was an error with accept then throw an exception
if
(
status
==
OTHER_ERROR
)
{
// make sure the object is cleared
clear
();
// throw the exception
throw
dlib
::
socket_error
(
"error occurred in server::start()
\n
listening socket returned error"
);
}
}
// ----------------------------------------------------------------------------------------
bool
server
::
is_running
(
)
const
{
running_mutex
.
lock
();
bool
temp
=
running
;
running_mutex
.
unlock
();
return
temp
;
}
// ----------------------------------------------------------------------------------------
const
std
::
string
server
::
get_listening_ip
(
)
const
{
listening_ip_mutex
.
lock
();
std
::
string
ip
(
listening_ip
);
listening_ip_mutex
.
unlock
();
return
ip
;
}
// ----------------------------------------------------------------------------------------
int
server
::
get_listening_port
(
)
const
{
listening_port_mutex
.
lock
();
int
port
=
listening_port
;
listening_port_mutex
.
unlock
();
return
port
;
}
// ----------------------------------------------------------------------------------------
void
server
::
set_listening_port
(
int
port
)
{
// make sure requires clause is not broken
DLIB_CASSERT
(
(
port
>=
0
&&
this
->
is_running
()
==
false
),
"
\t
void server::set_listening_port"
<<
"
\n\t
port == "
<<
port
<<
"
\n\t
is_running() == "
<<
this
->
is_running
()
<<
"
\n\t
this: "
<<
this
);
listening_port_mutex
.
lock
();
listening_port
=
port
;
listening_port_mutex
.
unlock
();
}
// ----------------------------------------------------------------------------------------
void
server
::
set_listening_ip
(
const
std
::
string
&
ip
)
{
// make sure requires clause is not broken
DLIB_CASSERT
(
(
(
is_ip_address
(
ip
)
||
ip
==
""
)
&&
this
->
is_running
()
==
false
),
"
\t
void server::set_listening_ip"
<<
"
\n\t
ip == "
<<
ip
<<
"
\n\t
is_running() == "
<<
this
->
is_running
()
<<
"
\n\t
this: "
<<
this
);
listening_ip_mutex
.
lock
();
listening_ip
=
ip
;
listening_ip_mutex
.
unlock
();
}
// ----------------------------------------------------------------------------------------
// ----------------------------------------------------------------------------------------
// static member function definitions
// ----------------------------------------------------------------------------------------
// ----------------------------------------------------------------------------------------
const
logger
server
::
sdlog
(
"dlib.server"
);
void
server
::
service_connection
(
void
*
item
)
{
param
&
p
=
*
static_cast
<
param
*>
(
item
);
p
.
the_server
.
on_connect
(
p
.
new_connection
);
// remove this connection from cons and close it
p
.
the_server
.
cons_mutex
.
lock
();
connection
*
temp
;
if
(
p
.
the_server
.
cons
.
is_member
(
&
p
.
new_connection
))
p
.
the_server
.
cons
.
remove
(
&
p
.
new_connection
,
temp
);
try
{
close_gracefully
(
&
p
.
new_connection
);
}
catch
(...)
{
sdlog
<<
LERROR
<<
"close_gracefully() threw"
;
}
p
.
the_server
.
cons_mutex
.
unlock
();
// decrement the thread count and signal if it is now zero
p
.
the_server
.
thread_count_mutex
.
lock
();
--
p
.
the_server
.
thread_count
;
p
.
the_server
.
thread_count_signaler
.
broadcast
();
if
(
p
.
the_server
.
thread_count
==
0
)
p
.
the_server
.
thread_count_zero
.
broadcast
();
p
.
the_server
.
thread_count_mutex
.
unlock
();
delete
&
p
;
}
// ----------------------------------------------------------------------------------------
}
#ifdef NO_MAKEFILE
#include "server_kernel.cpp"
#endif
#endif // DLIB_SERVER_KERNEL_1_
dlib/server/server_kernel_abstract.h
View file @
c2825e86
...
...
@@ -3,7 +3,6 @@
#undef DLIB_SERVER_KERNEL_ABSTRACT_
#ifdef DLIB_SERVER_KERNEL_ABSTRACT_
// non-templatable dependencies
#include "../threads/threads_kernel_abstract.h"
#include "../sockets/sockets_kernel_abstract.h"
#include <string>
...
...
@@ -11,8 +10,6 @@
namespace
dlib
{
class
server
{
...
...
@@ -244,8 +241,6 @@ namespace dlib
throws
- std::bad_alloc
!*/
private
:
...
...
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