Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Contribute to GitLab
Sign in
Toggle navigation
M
mongoose
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
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
esp
mongoose
Commits
2260c8dd
Commit
2260c8dd
authored
Sep 20, 2012
by
Igor Okulist
Browse files
Options
Browse Files
Download
Plain Diff
Merge branch 'master' of
https://github.com/okigan/mongoose
parents
4f01f101
edffc46b
Changes
10
Show whitespace changes
Inline
Side-by-side
Showing
10 changed files
with
484 additions
and
115 deletions
+484
-115
README.md
README.md
+2
-1
Makefile
examples/Makefile
+2
-0
websocket.c
examples/websocket.c
+59
-0
index.html
examples/websocket_html_root/index.html
+34
-0
main.c
main.c
+3
-1
mongoose.1
mongoose.1
+0
-2
mongoose.c
mongoose.c
+348
-97
mongoose.h
mongoose.h
+16
-9
embed.c
test/embed.c
+3
-2
unit_test.c
test/unit_test.c
+17
-3
No files found.
README.md
View file @
2260c8dd
...
@@ -14,7 +14,8 @@ Features
...
@@ -14,7 +14,8 @@ Features
-
IP-based ACL, Windows service, GET, POST, HEAD, PUT, DELETE methods
-
IP-based ACL, Windows service, GET, POST, HEAD, PUT, DELETE methods
-
Small footprint: executable size is 40 kB on Linux 2.6 i386 system
-
Small footprint: executable size is 40 kB on Linux 2.6 i386 system
-
Embeddable with
[
simple and clean API
](
https://github.com/valenok/mongoose/blob/master/mongoose.h
)
. Source is in single .c file to make things easy.
-
Embeddable with
[
simple and clean API
](
https://github.com/valenok/mongoose/blob/master/mongoose.h
)
. Source is in single .c file to make things easy.
-
Examples:
[
hello.c
](
https://github.com/valenok/mongoose/blob/master/examples/hello.c
)
,
[
post.c
](
https://github.com/valenok/mongoose/blob/master/examples/post.c
)
,
[
upload.c
](
https://github.com/valenok/mongoose/blob/master/examples/upload.c
)
-
Examples:
[
hello.c
](
https://github.com/valenok/mongoose/blob/master/examples/hello.c
)
,
[
post.c
](
https://github.com/valenok/mongoose/blob/master/examples/post.c
)
,
[
upload.c
](
https://github.com/valenok/mongoose/blob/master/examples/upload.c
)
,
[
websocket.c
](
https://github.com/valenok/mongoose/blob/master/examples/websocket.c
)
-
Python and C# bindings
-
Python and C# bindings
...
...
examples/Makefile
View file @
2260c8dd
...
@@ -4,5 +4,7 @@ all:
...
@@ -4,5 +4,7 @@ all:
OS
=
`
uname
`
;
\
OS
=
`
uname
`
;
\
test
"
$$
OS"
=
Linux
&&
LIBS
=
"-ldl"
;
\
test
"
$$
OS"
=
Linux
&&
LIBS
=
"-ldl"
;
\
$(CC)
$(CFLAGS)
hello.c ../mongoose.c
$$
LIBS
$(ADD)
-o
hello
;
$(CC)
$(CFLAGS)
hello.c ../mongoose.c
$$
LIBS
$(ADD)
-o
hello
;
$(CC)
$(CFLAGS)
upload.c ../mongoose.c
$$
LIBS
$(ADD)
-o
upload
;
$(CC)
$(CFLAGS)
post.c ../mongoose.c
$$
LIBS
$(ADD)
-o
post
;
$(CC)
$(CFLAGS)
post.c ../mongoose.c
$$
LIBS
$(ADD)
-o
post
;
$(CC)
$(CFLAGS)
-DUSE_WEBSOCKET
websocket.c ../mongoose.c
$$
LIBS
$(ADD)
-o
websocket
;
$(CC)
$(CFLAGS)
chat.c ../mongoose.c
$$
LIBS
$(ADD)
-o
chat
$(CC)
$(CFLAGS)
chat.c ../mongoose.c
$$
LIBS
$(ADD)
-o
chat
examples/websocket.c
0 → 100644
View file @
2260c8dd
// Copyright (c) 2004-2012 Sergey Lyubka
// This file is a part of mongoose project, http://github.com/valenok/mongoose
#include <stdio.h>
#include <string.h>
#include "mongoose.h"
static
void
*
callback
(
enum
mg_event
event
,
struct
mg_connection
*
conn
)
{
if
(
event
==
MG_WEBSOCKET_READY
)
{
static
const
char
*
hello
=
"hello from mongoose! waiting for message ..."
;
char
frame
[
2
];
// Prepare websocket frame.
frame
[
0
]
=
0x81
;
// text frame
frame
[
1
]
=
strlen
(
hello
);
// length is < 126
// Write frame and a text message
mg_write
(
conn
,
frame
,
sizeof
(
frame
));
mg_write
(
conn
,
hello
,
strlen
(
hello
));
return
""
;
}
else
if
(
event
==
MG_WEBSOCKET_MESSAGE
)
{
unsigned
char
buf
[
500
],
reply
[
500
];
int
len
,
msg_len
,
i
,
mask_len
,
xor
;
// Read message from the client and echo it back
if
((
len
=
mg_read
(
conn
,
buf
,
sizeof
(
buf
)))
>
8
)
{
msg_len
=
buf
[
1
]
&
127
;
mask_len
=
(
buf
[
1
]
&
128
)
?
4
:
0
;
if
(
msg_len
<
126
)
{
reply
[
0
]
=
0x81
;
// text, FIN set
reply
[
1
]
=
msg_len
;
for
(
i
=
0
;
i
<
msg_len
;
i
++
)
{
xor
=
mask_len
==
0
?
0
:
buf
[
2
+
(
i
%
4
)];
reply
[
i
+
2
]
=
buf
[
i
+
2
+
mask_len
]
^
xor
;
}
mg_write
(
conn
,
reply
,
2
+
msg_len
);
}
}
return
""
;
// Return non-NULL: stop websocket conversation
}
else
{
return
NULL
;
}
}
int
main
(
void
)
{
struct
mg_context
*
ctx
;
const
char
*
options
[]
=
{
"listening_ports"
,
"8080"
,
"document_root"
,
"websocket_html_root"
,
NULL
};
ctx
=
mg_start
(
&
callback
,
NULL
,
options
);
getchar
();
// Wait until user hits "enter"
mg_stop
(
ctx
);
return
0
;
}
examples/websocket_html_root/index.html
0 → 100644
View file @
2260c8dd
<!DOCTYPE html>
<meta
charset=
"utf-8"
/>
<title>
WebSocket Test
</title>
<script
language=
"javascript"
type=
"text/javascript"
>
var
writeToScreen
=
function
(
message
)
{
var
div
=
document
.
createElement
(
'div'
);
div
.
innerHTML
=
message
;
document
.
getElementById
(
'output'
).
appendChild
(
div
);
};
window
.
onload
=
function
()
{
var
url
=
'ws://'
+
window
.
location
.
host
+
'/foo'
;
websocket
=
new
WebSocket
(
url
);
websocket
.
onopen
=
function
(
ev
)
{
writeToScreen
(
'CONNECTED'
);
var
message
=
'Не всё подчиняется разуму. Но всё подчиняется упорству. '
;
writeToScreen
(
'SENT: '
+
message
);
websocket
.
send
(
message
);
};
websocket
.
onclose
=
function
(
ev
)
{
writeToScreen
(
'DISCONNECTED'
);
};
websocket
.
onmessage
=
function
(
ev
)
{
writeToScreen
(
'<span style="color: blue;">RESPONSE: '
+
ev
.
data
+
' </span>'
);
websocket
.
close
();
};
websocket
.
onerror
=
function
(
ev
)
{
writeToScreen
(
'<span style="color: red; ">ERROR: </span> '
+
ev
.
data
);
};
};
</script>
<h2>
WebSocket Test
</h2>
<div
id=
"output"
></div>
</html>
main.c
View file @
2260c8dd
...
@@ -219,9 +219,11 @@ static void init_server_name(void) {
...
@@ -219,9 +219,11 @@ static void init_server_name(void) {
static
void
*
mongoose_callback
(
enum
mg_event
ev
,
struct
mg_connection
*
conn
)
{
static
void
*
mongoose_callback
(
enum
mg_event
ev
,
struct
mg_connection
*
conn
)
{
if
(
ev
==
MG_EVENT_LOG
)
{
if
(
ev
==
MG_EVENT_LOG
)
{
printf
(
"%s
\n
"
,
mg_get_
request_info
(
conn
)
->
log_message
);
printf
(
"%s
\n
"
,
mg_get_
log_message
(
conn
)
);
}
}
// Returning NULL marks request as not handled, signalling mongoose to
// proceed with handling it.
return
NULL
;
return
NULL
;
}
}
...
...
mongoose.1
View file @
2260c8dd
...
@@ -82,8 +82,6 @@ Use
...
@@ -82,8 +82,6 @@ Use
as a CGI interpreter for all CGI scripts regardless script extension.
as a CGI interpreter for all CGI scripts regardless script extension.
Mongoose decides which interpreter to use by looking at
Mongoose decides which interpreter to use by looking at
the first line of a CGI script. Default: "".
the first line of a CGI script. Default: "".
.It Fl M Ar max_request_size
Maximum HTTP request size in bytes. Default: "16384"
.It Fl P Ar protect_uri
.It Fl P Ar protect_uri
Comma separated list of URI=PATH pairs, specifying that given URIs
Comma separated list of URI=PATH pairs, specifying that given URIs
must be protected with respected password files. Default: ""
must be protected with respected password files. Default: ""
...
...
mongoose.c
View file @
2260c8dd
...
@@ -21,7 +21,9 @@
...
@@ -21,7 +21,9 @@
#if defined(_WIN32)
#if defined(_WIN32)
#define _CRT_SECURE_NO_WARNINGS // Disable deprecation warning in VS2005
#define _CRT_SECURE_NO_WARNINGS // Disable deprecation warning in VS2005
#else
#else
#define _XOPEN_SOURCE // For flockfile() on Linux
#ifdef __linux__
#define _XOPEN_SOURCE 600 // For flockfile() on Linux
#endif
#define _LARGEFILE_SOURCE // Enable 64-bit file offsets
#define _LARGEFILE_SOURCE // Enable 64-bit file offsets
#define __STDC_FORMAT_MACROS // <inttypes.h> wants this for C++
#define __STDC_FORMAT_MACROS // <inttypes.h> wants this for C++
#define __STDC_LIMIT_MACROS // C++ wants that for INT64_MAX
#define __STDC_LIMIT_MACROS // C++ wants that for INT64_MAX
...
@@ -226,6 +228,7 @@ typedef int SOCKET;
...
@@ -226,6 +228,7 @@ typedef int SOCKET;
#define CGI_ENVIRONMENT_SIZE 4096
#define CGI_ENVIRONMENT_SIZE 4096
#define MAX_CGI_ENVIR_VARS 64
#define MAX_CGI_ENVIR_VARS 64
#define MG_BUF_LEN 8192
#define MG_BUF_LEN 8192
#define MAX_REQUEST_SIZE 16384
#define ARRAY_SIZE(array) (sizeof(array) / sizeof(array[0]))
#define ARRAY_SIZE(array) (sizeof(array) / sizeof(array[0]))
#ifdef _WIN32
#ifdef _WIN32
...
@@ -425,8 +428,8 @@ struct socket {
...
@@ -425,8 +428,8 @@ struct socket {
// NOTE(lsm): this enum shoulds be in sync with the config_options below.
// NOTE(lsm): this enum shoulds be in sync with the config_options below.
enum
{
enum
{
CGI_EXTENSIONS
,
CGI_ENVIRONMENT
,
PUT_DELETE_PASSWORDS_FILE
,
CGI_INTERPRETER
,
CGI_EXTENSIONS
,
CGI_ENVIRONMENT
,
PUT_DELETE_PASSWORDS_FILE
,
CGI_INTERPRETER
,
MAX_REQUEST_SIZE
,
PROTECT_URI
,
AUTHENTICATION_DOMAIN
,
SSI_EXTENSIONS
,
PROTECT_URI
,
AUTHENTICATION_DOMAIN
,
SSI_EXTENSIONS
,
ACCESS_LOG_FILE
,
SSL_CHAIN_FILE
,
ENABLE_DIRECTORY_LISTING
,
ERROR_LOG_FILE
,
ACCESS_LOG_FILE
,
ENABLE_DIRECTORY_LISTING
,
ERROR_LOG_FILE
,
GLOBAL_PASSWORDS_FILE
,
INDEX_FILES
,
ENABLE_KEEP_ALIVE
,
ACCESS_CONTROL_LIST
,
GLOBAL_PASSWORDS_FILE
,
INDEX_FILES
,
ENABLE_KEEP_ALIVE
,
ACCESS_CONTROL_LIST
,
EXTRA_MIME_TYPES
,
LISTENING_PORTS
,
DOCUMENT_ROOT
,
SSL_CERTIFICATE
,
EXTRA_MIME_TYPES
,
LISTENING_PORTS
,
DOCUMENT_ROOT
,
SSL_CERTIFICATE
,
NUM_THREADS
,
RUN_AS_USER
,
REWRITE
,
HIDE_FILES
,
NUM_THREADS
,
RUN_AS_USER
,
REWRITE
,
HIDE_FILES
,
...
@@ -438,12 +441,10 @@ static const char *config_options[] = {
...
@@ -438,12 +441,10 @@ static const char *config_options[] = {
"E"
,
"cgi_environment"
,
NULL
,
"E"
,
"cgi_environment"
,
NULL
,
"G"
,
"put_delete_passwords_file"
,
NULL
,
"G"
,
"put_delete_passwords_file"
,
NULL
,
"I"
,
"cgi_interpreter"
,
NULL
,
"I"
,
"cgi_interpreter"
,
NULL
,
"M"
,
"max_request_size"
,
"16384"
,
"P"
,
"protect_uri"
,
NULL
,
"P"
,
"protect_uri"
,
NULL
,
"R"
,
"authentication_domain"
,
"mydomain.com"
,
"R"
,
"authentication_domain"
,
"mydomain.com"
,
"S"
,
"ssi_pattern"
,
"**.shtml$|**.shtm$"
,
"S"
,
"ssi_pattern"
,
"**.shtml$|**.shtm$"
,
"a"
,
"access_log_file"
,
NULL
,
"a"
,
"access_log_file"
,
NULL
,
"c"
,
"ssl_chain_file"
,
NULL
,
"d"
,
"enable_directory_listing"
,
"yes"
,
"d"
,
"enable_directory_listing"
,
"yes"
,
"e"
,
"error_log_file"
,
NULL
,
"e"
,
"error_log_file"
,
NULL
,
"g"
,
"global_passwords_file"
,
NULL
,
"g"
,
"global_passwords_file"
,
NULL
,
...
@@ -496,10 +497,12 @@ struct mg_connection {
...
@@ -496,10 +497,12 @@ struct mg_connection {
char
*
path_info
;
// PATH_INFO part of the URL
char
*
path_info
;
// PATH_INFO part of the URL
char
*
body
;
// Pointer to not-read yet buffered body data
char
*
body
;
// Pointer to not-read yet buffered body data
char
*
next_request
;
// Pointer to the buffered next request
char
*
next_request
;
// Pointer to the buffered next request
char
*
log_message
;
// Placeholder for the mongoose error log message
int
must_close
;
// 1 if connection must be closed
int
must_close
;
// 1 if connection must be closed
int
buf_size
;
// Buffer size
int
buf_size
;
// Buffer size
int
request_len
;
// Size of the request + headers in a buffer
int
request_len
;
// Size of the request + headers in a buffer
int
data_len
;
// Total size of data in a buffer
int
data_len
;
// Total size of data in a buffer
int
status_code
;
// HTTP reply status code, e.g. 200
};
};
const
char
**
mg_get_valid_option_names
(
void
)
{
const
char
**
mg_get_valid_option_names
(
void
)
{
...
@@ -507,9 +510,24 @@ const char **mg_get_valid_option_names(void) {
...
@@ -507,9 +510,24 @@ const char **mg_get_valid_option_names(void) {
}
}
static
void
*
call_user
(
struct
mg_connection
*
conn
,
enum
mg_event
event
)
{
static
void
*
call_user
(
struct
mg_connection
*
conn
,
enum
mg_event
event
)
{
conn
->
request_info
.
user_data
=
conn
->
ctx
->
user_data
;
return
conn
==
NULL
||
conn
->
ctx
==
NULL
||
conn
->
ctx
->
user_callback
==
NULL
?
return
conn
->
ctx
->
user_callback
==
NULL
?
NULL
:
NULL
:
conn
->
ctx
->
user_callback
(
event
,
conn
);
conn
->
ctx
->
user_callback
(
event
,
conn
);
}
void
*
mg_get_user_data
(
struct
mg_connection
*
conn
)
{
return
conn
!=
NULL
&&
conn
->
ctx
!=
NULL
?
conn
->
ctx
->
user_data
:
NULL
;
}
const
char
*
mg_get_log_message
(
const
struct
mg_connection
*
conn
)
{
return
conn
==
NULL
?
NULL
:
conn
->
log_message
;
}
int
mg_get_reply_status_code
(
const
struct
mg_connection
*
conn
)
{
return
conn
==
NULL
?
-
1
:
conn
->
status_code
;
}
void
*
mg_get_ssl_context
(
const
struct
mg_connection
*
conn
)
{
return
conn
==
NULL
||
conn
->
ctx
==
NULL
?
NULL
:
conn
->
ctx
->
ssl_ctx
;
}
}
static
int
get_option_index
(
const
char
*
name
)
{
static
int
get_option_index
(
const
char
*
name
)
{
...
@@ -564,9 +582,9 @@ static void cry(struct mg_connection *conn, const char *fmt, ...) {
...
@@ -564,9 +582,9 @@ static void cry(struct mg_connection *conn, const char *fmt, ...) {
// Do not lock when getting the callback value, here and below.
// Do not lock when getting the callback value, here and below.
// I suppose this is fine, since function cannot disappear in the
// I suppose this is fine, since function cannot disappear in the
// same way string option can.
// same way string option can.
conn
->
request_info
.
log_message
=
buf
;
conn
->
log_message
=
buf
;
if
(
call_user
(
conn
,
MG_EVENT_LOG
)
==
NULL
)
{
if
(
call_user
(
conn
,
MG_EVENT_LOG
)
==
NULL
)
{
fp
=
conn
->
ctx
->
config
[
ERROR_LOG_FILE
]
==
NULL
?
NULL
:
fp
=
conn
->
ctx
==
NULL
||
conn
->
ctx
->
config
[
ERROR_LOG_FILE
]
==
NULL
?
NULL
:
mg_fopen
(
conn
->
ctx
->
config
[
ERROR_LOG_FILE
],
"a+"
);
mg_fopen
(
conn
->
ctx
->
config
[
ERROR_LOG_FILE
],
"a+"
);
if
(
fp
!=
NULL
)
{
if
(
fp
!=
NULL
)
{
...
@@ -590,15 +608,14 @@ static void cry(struct mg_connection *conn, const char *fmt, ...) {
...
@@ -590,15 +608,14 @@ static void cry(struct mg_connection *conn, const char *fmt, ...) {
}
}
}
}
}
}
conn
->
request_info
.
log_message
=
NULL
;
conn
->
log_message
=
NULL
;
}
}
// Return fake connection structure. Used for logging, if connection
// Return fake connection structure. Used for logging, if connection
// is not applicable at the moment of logging.
// is not applicable at the moment of logging.
static
struct
mg_connection
*
fc
(
void
)
{
static
struct
mg_connection
*
fc
(
struct
mg_context
*
ctx
)
{
static
struct
mg_context
fake_context
;
static
struct
mg_connection
fake_connection
;
static
struct
mg_connection
fake_connection
;
fake_connection
.
ctx
=
&
fake_context
;
fake_connection
.
ctx
=
ctx
;
return
&
fake_connection
;
return
&
fake_connection
;
}
}
...
@@ -849,7 +866,7 @@ static int should_keep_alive(const struct mg_connection *conn) {
...
@@ -849,7 +866,7 @@ static int should_keep_alive(const struct mg_connection *conn) {
const
char
*
http_version
=
conn
->
request_info
.
http_version
;
const
char
*
http_version
=
conn
->
request_info
.
http_version
;
const
char
*
header
=
mg_get_header
(
conn
,
"Connection"
);
const
char
*
header
=
mg_get_header
(
conn
,
"Connection"
);
if
(
conn
->
must_close
||
if
(
conn
->
must_close
||
conn
->
request_info
.
status_code
==
401
||
conn
->
status_code
==
401
||
mg_strcasecmp
(
conn
->
ctx
->
config
[
ENABLE_KEEP_ALIVE
],
"yes"
)
!=
0
||
mg_strcasecmp
(
conn
->
ctx
->
config
[
ENABLE_KEEP_ALIVE
],
"yes"
)
!=
0
||
(
header
!=
NULL
&&
mg_strcasecmp
(
header
,
"keep-alive"
)
!=
0
)
||
(
header
!=
NULL
&&
mg_strcasecmp
(
header
,
"keep-alive"
)
!=
0
)
||
(
header
==
NULL
&&
http_version
&&
strcmp
(
http_version
,
"1.1"
)))
{
(
header
==
NULL
&&
http_version
&&
strcmp
(
http_version
,
"1.1"
)))
{
...
@@ -873,7 +890,7 @@ static void send_http_error(struct mg_connection *conn, int status,
...
@@ -873,7 +890,7 @@ static void send_http_error(struct mg_connection *conn, int status,
va_list
ap
;
va_list
ap
;
int
len
;
int
len
;
conn
->
request_info
.
status_code
=
status
;
conn
->
status_code
=
status
;
if
(
call_user
(
conn
,
MG_HTTP_ERROR
)
==
NULL
)
{
if
(
call_user
(
conn
,
MG_HTTP_ERROR
)
==
NULL
)
{
buf
[
0
]
=
'\0'
;
buf
[
0
]
=
'\0'
;
...
@@ -2139,7 +2156,7 @@ static FILE *open_auth_file(struct mg_connection *conn, const char *path) {
...
@@ -2139,7 +2156,7 @@ static FILE *open_auth_file(struct mg_connection *conn, const char *path) {
// Use global passwords file
// Use global passwords file
fp
=
mg_fopen
(
ctx
->
config
[
GLOBAL_PASSWORDS_FILE
],
"r"
);
fp
=
mg_fopen
(
ctx
->
config
[
GLOBAL_PASSWORDS_FILE
],
"r"
);
if
(
fp
==
NULL
)
if
(
fp
==
NULL
)
cry
(
fc
(),
"fopen(%s): %s"
,
cry
(
fc
(
ctx
),
"fopen(%s): %s"
,
ctx
->
config
[
GLOBAL_PASSWORDS_FILE
],
strerror
(
ERRNO
));
ctx
->
config
[
GLOBAL_PASSWORDS_FILE
],
strerror
(
ERRNO
));
}
else
if
(
!
mg_stat
(
path
,
&
st
)
&&
st
.
is_directory
)
{
}
else
if
(
!
mg_stat
(
path
,
&
st
)
&&
st
.
is_directory
)
{
(
void
)
mg_snprintf
(
conn
,
name
,
sizeof
(
name
),
"%s%c%s"
,
(
void
)
mg_snprintf
(
conn
,
name
,
sizeof
(
name
),
"%s%c%s"
,
...
@@ -2289,7 +2306,7 @@ static int check_authorization(struct mg_connection *conn, const char *path) {
...
@@ -2289,7 +2306,7 @@ static int check_authorization(struct mg_connection *conn, const char *path) {
}
}
static
void
send_authorization_request
(
struct
mg_connection
*
conn
)
{
static
void
send_authorization_request
(
struct
mg_connection
*
conn
)
{
conn
->
request_info
.
status_code
=
401
;
conn
->
status_code
=
401
;
(
void
)
mg_printf
(
conn
,
(
void
)
mg_printf
(
conn
,
"HTTP/1.1 401 Unauthorized
\r\n
"
"HTTP/1.1 401 Unauthorized
\r\n
"
"Content-Length: 0
\r\n
"
"Content-Length: 0
\r\n
"
...
@@ -2582,7 +2599,7 @@ static void handle_directory_request(struct mg_connection *conn,
...
@@ -2582,7 +2599,7 @@ static void handle_directory_request(struct mg_connection *conn,
free
(
data
.
entries
);
free
(
data
.
entries
);
conn
->
num_bytes_sent
+=
mg_printf
(
conn
,
"%s"
,
"</table></body></html>"
);
conn
->
num_bytes_sent
+=
mg_printf
(
conn
,
"%s"
,
"</table></body></html>"
);
conn
->
request_info
.
status_code
=
200
;
conn
->
status_code
=
200
;
}
}
// Send len bytes from the opened file to the client.
// Send len bytes from the opened file to the client.
...
@@ -2639,7 +2656,7 @@ static void handle_file_request(struct mg_connection *conn, const char *path,
...
@@ -2639,7 +2656,7 @@ static void handle_file_request(struct mg_connection *conn, const char *path,
get_mime_type
(
conn
->
ctx
,
path
,
&
mime_vec
);
get_mime_type
(
conn
->
ctx
,
path
,
&
mime_vec
);
cl
=
stp
->
size
;
cl
=
stp
->
size
;
conn
->
request_info
.
status_code
=
200
;
conn
->
status_code
=
200
;
range
[
0
]
=
'\0'
;
range
[
0
]
=
'\0'
;
if
((
fp
=
mg_fopen
(
path
,
"rb"
))
==
NULL
)
{
if
((
fp
=
mg_fopen
(
path
,
"rb"
))
==
NULL
)
{
...
@@ -2653,7 +2670,7 @@ static void handle_file_request(struct mg_connection *conn, const char *path,
...
@@ -2653,7 +2670,7 @@ static void handle_file_request(struct mg_connection *conn, const char *path,
r1
=
r2
=
0
;
r1
=
r2
=
0
;
hdr
=
mg_get_header
(
conn
,
"Range"
);
hdr
=
mg_get_header
(
conn
,
"Range"
);
if
(
hdr
!=
NULL
&&
(
n
=
parse_range_header
(
hdr
,
&
r1
,
&
r2
))
>
0
)
{
if
(
hdr
!=
NULL
&&
(
n
=
parse_range_header
(
hdr
,
&
r1
,
&
r2
))
>
0
)
{
conn
->
request_info
.
status_code
=
206
;
conn
->
status_code
=
206
;
(
void
)
fseeko
(
fp
,
r1
,
SEEK_SET
);
(
void
)
fseeko
(
fp
,
r1
,
SEEK_SET
);
cl
=
n
==
2
?
r2
-
r1
+
1
:
cl
-
r1
;
cl
=
n
==
2
?
r2
-
r1
+
1
:
cl
-
r1
;
(
void
)
mg_snprintf
(
conn
,
range
,
sizeof
(
range
),
(
void
)
mg_snprintf
(
conn
,
range
,
sizeof
(
range
),
...
@@ -2680,7 +2697,7 @@ static void handle_file_request(struct mg_connection *conn, const char *path,
...
@@ -2680,7 +2697,7 @@ static void handle_file_request(struct mg_connection *conn, const char *path,
"Connection: %s
\r\n
"
"Connection: %s
\r\n
"
"Accept-Ranges: bytes
\r\n
"
"Accept-Ranges: bytes
\r\n
"
"%s
\r\n
"
,
"%s
\r\n
"
,
conn
->
request_info
.
status_code
,
msg
,
date
,
lm
,
etag
,
(
int
)
mime_vec
.
len
,
conn
->
status_code
,
msg
,
date
,
lm
,
etag
,
(
int
)
mime_vec
.
len
,
mime_vec
.
ptr
,
cl
,
suggest_connection_header
(
conn
),
range
);
mime_vec
.
ptr
,
cl
,
suggest_connection_header
(
conn
),
range
);
if
(
strcmp
(
conn
->
request_info
.
request_method
,
"HEAD"
)
!=
0
)
{
if
(
strcmp
(
conn
->
request_info
.
request_method
,
"HEAD"
)
!=
0
)
{
...
@@ -2729,7 +2746,6 @@ static int parse_http_message(char *buf, int len, struct mg_request_info *ri) {
...
@@ -2729,7 +2746,6 @@ static int parse_http_message(char *buf, int len, struct mg_request_info *ri) {
// Reset attributes. DO NOT TOUCH is_ssl, remote_ip, remote_port
// Reset attributes. DO NOT TOUCH is_ssl, remote_ip, remote_port
ri
->
remote_user
=
ri
->
request_method
=
ri
->
uri
=
ri
->
http_version
=
NULL
;
ri
->
remote_user
=
ri
->
request_method
=
ri
->
uri
=
ri
->
http_version
=
NULL
;
ri
->
num_headers
=
0
;
ri
->
num_headers
=
0
;
ri
->
status_code
=
-
1
;
buf
[
request_length
-
1
]
=
'\0'
;
buf
[
request_length
-
1
]
=
'\0'
;
...
@@ -3129,7 +3145,7 @@ static void handle_cgi_request(struct mg_connection *conn, const char *prog) {
...
@@ -3129,7 +3145,7 @@ static void handle_cgi_request(struct mg_connection *conn, const char *prog) {
// Do not send anything back to client, until we buffer in all
// Do not send anything back to client, until we buffer in all
// HTTP headers.
// HTTP headers.
data_len
=
0
;
data_len
=
0
;
headers_len
=
read_request
(
out
,
fc
()
,
buf
,
sizeof
(
buf
),
&
data_len
);
headers_len
=
read_request
(
out
,
conn
,
buf
,
sizeof
(
buf
),
&
data_len
);
if
(
headers_len
<=
0
)
{
if
(
headers_len
<=
0
)
{
send_http_error
(
conn
,
500
,
http_500_error
,
send_http_error
(
conn
,
500
,
http_500_error
,
"CGI program sent malformed or too big (>%u bytes) "
"CGI program sent malformed or too big (>%u bytes) "
...
@@ -3144,21 +3160,21 @@ static void handle_cgi_request(struct mg_connection *conn, const char *prog) {
...
@@ -3144,21 +3160,21 @@ static void handle_cgi_request(struct mg_connection *conn, const char *prog) {
// Make up and send the status line
// Make up and send the status line
status_text
=
"OK"
;
status_text
=
"OK"
;
if
((
status
=
get_header
(
&
ri
,
"Status"
))
!=
NULL
)
{
if
((
status
=
get_header
(
&
ri
,
"Status"
))
!=
NULL
)
{
conn
->
request_info
.
status_code
=
atoi
(
status
);
conn
->
status_code
=
atoi
(
status
);
status_text
=
status
;
status_text
=
status
;
while
(
isdigit
(
*
(
unsigned
char
*
)
status_text
)
||
*
status_text
==
' '
)
{
while
(
isdigit
(
*
(
unsigned
char
*
)
status_text
)
||
*
status_text
==
' '
)
{
status_text
++
;
status_text
++
;
}
}
}
else
if
(
get_header
(
&
ri
,
"Location"
)
!=
NULL
)
{
}
else
if
(
get_header
(
&
ri
,
"Location"
)
!=
NULL
)
{
conn
->
request_info
.
status_code
=
302
;
conn
->
status_code
=
302
;
}
else
{
}
else
{
conn
->
request_info
.
status_code
=
200
;
conn
->
status_code
=
200
;
}
}
if
(
get_header
(
&
ri
,
"Connection"
)
!=
NULL
&&
if
(
get_header
(
&
ri
,
"Connection"
)
!=
NULL
&&
!
mg_strcasecmp
(
get_header
(
&
ri
,
"Connection"
),
"keep-alive"
))
{
!
mg_strcasecmp
(
get_header
(
&
ri
,
"Connection"
),
"keep-alive"
))
{
conn
->
must_close
=
1
;
conn
->
must_close
=
1
;
}
}
(
void
)
mg_printf
(
conn
,
"HTTP/1.1 %d %s
\r\n
"
,
conn
->
request_info
.
status_code
,
(
void
)
mg_printf
(
conn
,
"HTTP/1.1 %d %s
\r\n
"
,
conn
->
status_code
,
status_text
);
status_text
);
// Send headers
// Send headers
...
@@ -3241,10 +3257,10 @@ static void put_file(struct mg_connection *conn, const char *path) {
...
@@ -3241,10 +3257,10 @@ static void put_file(struct mg_connection *conn, const char *path) {
FILE
*
fp
;
FILE
*
fp
;
int
rc
;
int
rc
;
conn
->
request_info
.
status_code
=
mg_stat
(
path
,
&
st
)
==
0
?
200
:
201
;
conn
->
status_code
=
mg_stat
(
path
,
&
st
)
==
0
?
200
:
201
;
if
((
rc
=
put_dir
(
path
))
==
0
)
{
if
((
rc
=
put_dir
(
path
))
==
0
)
{
mg_printf
(
conn
,
"HTTP/1.1 %d OK
\r\n\r\n
"
,
conn
->
request_info
.
status_code
);
mg_printf
(
conn
,
"HTTP/1.1 %d OK
\r\n\r\n
"
,
conn
->
status_code
);
}
else
if
(
rc
==
-
1
)
{
}
else
if
(
rc
==
-
1
)
{
send_http_error
(
conn
,
500
,
http_500_error
,
send_http_error
(
conn
,
500
,
http_500_error
,
"put_dir(%s): %s"
,
path
,
strerror
(
ERRNO
));
"put_dir(%s): %s"
,
path
,
strerror
(
ERRNO
));
...
@@ -3256,13 +3272,13 @@ static void put_file(struct mg_connection *conn, const char *path) {
...
@@ -3256,13 +3272,13 @@ static void put_file(struct mg_connection *conn, const char *path) {
range
=
mg_get_header
(
conn
,
"Content-Range"
);
range
=
mg_get_header
(
conn
,
"Content-Range"
);
r1
=
r2
=
0
;
r1
=
r2
=
0
;
if
(
range
!=
NULL
&&
parse_range_header
(
range
,
&
r1
,
&
r2
)
>
0
)
{
if
(
range
!=
NULL
&&
parse_range_header
(
range
,
&
r1
,
&
r2
)
>
0
)
{
conn
->
request_info
.
status_code
=
206
;
conn
->
status_code
=
206
;
// TODO(lsm): handle seek error
// TODO(lsm): handle seek error
(
void
)
fseeko
(
fp
,
r1
,
SEEK_SET
);
(
void
)
fseeko
(
fp
,
r1
,
SEEK_SET
);
}
}
if
(
forward_body_data
(
conn
,
fp
,
INVALID_SOCKET
,
NULL
))
if
(
forward_body_data
(
conn
,
fp
,
INVALID_SOCKET
,
NULL
))
{
(
void
)
mg_printf
(
conn
,
"HTTP/1.1 %d OK
\r\n\r\n
"
,
(
void
)
mg_printf
(
conn
,
"HTTP/1.1 %d OK
\r\n\r\n
"
,
conn
->
status_code
);
conn
->
request_info
.
status_code
);
}
(
void
)
fclose
(
fp
);
(
void
)
fclose
(
fp
);
}
}
}
}
...
@@ -3412,7 +3428,7 @@ static void handle_ssi_file_request(struct mg_connection *conn,
...
@@ -3412,7 +3428,7 @@ static void handle_ssi_file_request(struct mg_connection *conn,
}
}
static
void
send_options
(
struct
mg_connection
*
conn
)
{
static
void
send_options
(
struct
mg_connection
*
conn
)
{
conn
->
request_info
.
status_code
=
200
;
conn
->
status_code
=
200
;
(
void
)
mg_printf
(
conn
,
(
void
)
mg_printf
(
conn
,
"HTTP/1.1 200 OK
\r\n
"
"HTTP/1.1 200 OK
\r\n
"
...
@@ -3456,7 +3472,7 @@ static void handle_propfind(struct mg_connection *conn, const char* path,
...
@@ -3456,7 +3472,7 @@ static void handle_propfind(struct mg_connection *conn, const char* path,
const
char
*
depth
=
mg_get_header
(
conn
,
"Depth"
);
const
char
*
depth
=
mg_get_header
(
conn
,
"Depth"
);
conn
->
must_close
=
1
;
conn
->
must_close
=
1
;
conn
->
request_info
.
status_code
=
207
;
conn
->
status_code
=
207
;
mg_printf
(
conn
,
"HTTP/1.1 207 Multi-Status
\r\n
"
mg_printf
(
conn
,
"HTTP/1.1 207 Multi-Status
\r\n
"
"Connection: close
\r\n
"
"Connection: close
\r\n
"
"Content-Type: text/xml; charset=utf-8
\r\n\r\n
"
);
"Content-Type: text/xml; charset=utf-8
\r\n\r\n
"
);
...
@@ -3478,6 +3494,243 @@ static void handle_propfind(struct mg_connection *conn, const char* path,
...
@@ -3478,6 +3494,243 @@ static void handle_propfind(struct mg_connection *conn, const char* path,
conn
->
num_bytes_sent
+=
mg_printf
(
conn
,
"%s
\n
"
,
"</d:multistatus>"
);
conn
->
num_bytes_sent
+=
mg_printf
(
conn
,
"%s
\n
"
,
"</d:multistatus>"
);
}
}
#if defined(USE_WEBSOCKET)
// START OF SHA-1 code
// Copyright(c) By Steve Reid <steve@edmweb.com>
#define SHA1HANDSOFF
#if defined(__sun)
#include "solarisfixes.h"
#endif
#define rol(value, bits) (((value) << (bits)) | ((value) >> (32 - (bits))))
#if BYTE_ORDER == LITTLE_ENDIAN
#define blk0(i) (block->l[i] = (rol(block->l[i],24)&0xFF00FF00) \
|
(
rol
(
block
->
l
[
i
],
8
)
&
0x00FF00FF
))
#elif BYTE_ORDER == BIG_ENDIAN
#define blk0(i) block->l[i]
#else
#error "Endianness not defined!"
#endif
#define blk(i) (block->l[i&15] = rol(block->l[(i+13)&15]^block->l[(i+8)&15] \
^
block
->
l
[(
i
+
2
)
&
15
]
^
block
->
l
[
i
&
15
],
1
))
#define R0(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk0(i)+0x5A827999+rol(v,5);w=rol(w,30);
#define R1(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk(i)+0x5A827999+rol(v,5);w=rol(w,30);
#define R2(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0x6ED9EBA1+rol(v,5);w=rol(w,30);
#define R3(v,w,x,y,z,i) z+=(((w|x)&y)|(w&x))+blk(i)+0x8F1BBCDC+rol(v,5);w=rol(w,30);
#define R4(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0xCA62C1D6+rol(v,5);w=rol(w,30);
typedef
struct
{
uint32_t
state
[
5
];
uint32_t
count
[
2
];
unsigned
char
buffer
[
64
];
}
SHA1_CTX
;
static
void
SHA1Transform
(
uint32_t
state
[
5
],
const
unsigned
char
buffer
[
64
])
{
uint32_t
a
,
b
,
c
,
d
,
e
;
typedef
union
{
unsigned
char
c
[
64
];
uint32_t
l
[
16
];
}
CHAR64LONG16
;
CHAR64LONG16
block
[
1
];
memcpy
(
block
,
buffer
,
64
);
a
=
state
[
0
];
b
=
state
[
1
];
c
=
state
[
2
];
d
=
state
[
3
];
e
=
state
[
4
];
R0
(
a
,
b
,
c
,
d
,
e
,
0
);
R0
(
e
,
a
,
b
,
c
,
d
,
1
);
R0
(
d
,
e
,
a
,
b
,
c
,
2
);
R0
(
c
,
d
,
e
,
a
,
b
,
3
);
R0
(
b
,
c
,
d
,
e
,
a
,
4
);
R0
(
a
,
b
,
c
,
d
,
e
,
5
);
R0
(
e
,
a
,
b
,
c
,
d
,
6
);
R0
(
d
,
e
,
a
,
b
,
c
,
7
);
R0
(
c
,
d
,
e
,
a
,
b
,
8
);
R0
(
b
,
c
,
d
,
e
,
a
,
9
);
R0
(
a
,
b
,
c
,
d
,
e
,
10
);
R0
(
e
,
a
,
b
,
c
,
d
,
11
);
R0
(
d
,
e
,
a
,
b
,
c
,
12
);
R0
(
c
,
d
,
e
,
a
,
b
,
13
);
R0
(
b
,
c
,
d
,
e
,
a
,
14
);
R0
(
a
,
b
,
c
,
d
,
e
,
15
);
R1
(
e
,
a
,
b
,
c
,
d
,
16
);
R1
(
d
,
e
,
a
,
b
,
c
,
17
);
R1
(
c
,
d
,
e
,
a
,
b
,
18
);
R1
(
b
,
c
,
d
,
e
,
a
,
19
);
R2
(
a
,
b
,
c
,
d
,
e
,
20
);
R2
(
e
,
a
,
b
,
c
,
d
,
21
);
R2
(
d
,
e
,
a
,
b
,
c
,
22
);
R2
(
c
,
d
,
e
,
a
,
b
,
23
);
R2
(
b
,
c
,
d
,
e
,
a
,
24
);
R2
(
a
,
b
,
c
,
d
,
e
,
25
);
R2
(
e
,
a
,
b
,
c
,
d
,
26
);
R2
(
d
,
e
,
a
,
b
,
c
,
27
);
R2
(
c
,
d
,
e
,
a
,
b
,
28
);
R2
(
b
,
c
,
d
,
e
,
a
,
29
);
R2
(
a
,
b
,
c
,
d
,
e
,
30
);
R2
(
e
,
a
,
b
,
c
,
d
,
31
);
R2
(
d
,
e
,
a
,
b
,
c
,
32
);
R2
(
c
,
d
,
e
,
a
,
b
,
33
);
R2
(
b
,
c
,
d
,
e
,
a
,
34
);
R2
(
a
,
b
,
c
,
d
,
e
,
35
);
R2
(
e
,
a
,
b
,
c
,
d
,
36
);
R2
(
d
,
e
,
a
,
b
,
c
,
37
);
R2
(
c
,
d
,
e
,
a
,
b
,
38
);
R2
(
b
,
c
,
d
,
e
,
a
,
39
);
R3
(
a
,
b
,
c
,
d
,
e
,
40
);
R3
(
e
,
a
,
b
,
c
,
d
,
41
);
R3
(
d
,
e
,
a
,
b
,
c
,
42
);
R3
(
c
,
d
,
e
,
a
,
b
,
43
);
R3
(
b
,
c
,
d
,
e
,
a
,
44
);
R3
(
a
,
b
,
c
,
d
,
e
,
45
);
R3
(
e
,
a
,
b
,
c
,
d
,
46
);
R3
(
d
,
e
,
a
,
b
,
c
,
47
);
R3
(
c
,
d
,
e
,
a
,
b
,
48
);
R3
(
b
,
c
,
d
,
e
,
a
,
49
);
R3
(
a
,
b
,
c
,
d
,
e
,
50
);
R3
(
e
,
a
,
b
,
c
,
d
,
51
);
R3
(
d
,
e
,
a
,
b
,
c
,
52
);
R3
(
c
,
d
,
e
,
a
,
b
,
53
);
R3
(
b
,
c
,
d
,
e
,
a
,
54
);
R3
(
a
,
b
,
c
,
d
,
e
,
55
);
R3
(
e
,
a
,
b
,
c
,
d
,
56
);
R3
(
d
,
e
,
a
,
b
,
c
,
57
);
R3
(
c
,
d
,
e
,
a
,
b
,
58
);
R3
(
b
,
c
,
d
,
e
,
a
,
59
);
R4
(
a
,
b
,
c
,
d
,
e
,
60
);
R4
(
e
,
a
,
b
,
c
,
d
,
61
);
R4
(
d
,
e
,
a
,
b
,
c
,
62
);
R4
(
c
,
d
,
e
,
a
,
b
,
63
);
R4
(
b
,
c
,
d
,
e
,
a
,
64
);
R4
(
a
,
b
,
c
,
d
,
e
,
65
);
R4
(
e
,
a
,
b
,
c
,
d
,
66
);
R4
(
d
,
e
,
a
,
b
,
c
,
67
);
R4
(
c
,
d
,
e
,
a
,
b
,
68
);
R4
(
b
,
c
,
d
,
e
,
a
,
69
);
R4
(
a
,
b
,
c
,
d
,
e
,
70
);
R4
(
e
,
a
,
b
,
c
,
d
,
71
);
R4
(
d
,
e
,
a
,
b
,
c
,
72
);
R4
(
c
,
d
,
e
,
a
,
b
,
73
);
R4
(
b
,
c
,
d
,
e
,
a
,
74
);
R4
(
a
,
b
,
c
,
d
,
e
,
75
);
R4
(
e
,
a
,
b
,
c
,
d
,
76
);
R4
(
d
,
e
,
a
,
b
,
c
,
77
);
R4
(
c
,
d
,
e
,
a
,
b
,
78
);
R4
(
b
,
c
,
d
,
e
,
a
,
79
);
state
[
0
]
+=
a
;
state
[
1
]
+=
b
;
state
[
2
]
+=
c
;
state
[
3
]
+=
d
;
state
[
4
]
+=
e
;
a
=
b
=
c
=
d
=
e
=
0
;
memset
(
block
,
'\0'
,
sizeof
(
block
));
}
static
void
SHA1Init
(
SHA1_CTX
*
context
)
{
context
->
state
[
0
]
=
0x67452301
;
context
->
state
[
1
]
=
0xEFCDAB89
;
context
->
state
[
2
]
=
0x98BADCFE
;
context
->
state
[
3
]
=
0x10325476
;
context
->
state
[
4
]
=
0xC3D2E1F0
;
context
->
count
[
0
]
=
context
->
count
[
1
]
=
0
;
}
static
void
SHA1Update
(
SHA1_CTX
*
context
,
const
unsigned
char
*
data
,
uint32_t
len
)
{
uint32_t
i
,
j
;
j
=
context
->
count
[
0
];
if
((
context
->
count
[
0
]
+=
len
<<
3
)
<
j
)
context
->
count
[
1
]
++
;
context
->
count
[
1
]
+=
(
len
>>
29
);
j
=
(
j
>>
3
)
&
63
;
if
((
j
+
len
)
>
63
)
{
memcpy
(
&
context
->
buffer
[
j
],
data
,
(
i
=
64
-
j
));
SHA1Transform
(
context
->
state
,
context
->
buffer
);
for
(
;
i
+
63
<
len
;
i
+=
64
)
{
SHA1Transform
(
context
->
state
,
&
data
[
i
]);
}
j
=
0
;
}
else
i
=
0
;
memcpy
(
&
context
->
buffer
[
j
],
&
data
[
i
],
len
-
i
);
}
static
void
SHA1Final
(
unsigned
char
digest
[
20
],
SHA1_CTX
*
context
)
{
unsigned
i
;
unsigned
char
finalcount
[
8
],
c
;
for
(
i
=
0
;
i
<
8
;
i
++
)
{
finalcount
[
i
]
=
(
unsigned
char
)((
context
->
count
[(
i
>=
4
?
0
:
1
)]
>>
((
3
-
(
i
&
3
))
*
8
)
)
&
255
);
}
c
=
0200
;
SHA1Update
(
context
,
&
c
,
1
);
while
((
context
->
count
[
0
]
&
504
)
!=
448
)
{
c
=
0000
;
SHA1Update
(
context
,
&
c
,
1
);
}
SHA1Update
(
context
,
finalcount
,
8
);
for
(
i
=
0
;
i
<
20
;
i
++
)
{
digest
[
i
]
=
(
unsigned
char
)
((
context
->
state
[
i
>>
2
]
>>
((
3
-
(
i
&
3
))
*
8
)
)
&
255
);
}
memset
(
context
,
'\0'
,
sizeof
(
*
context
));
memset
(
&
finalcount
,
'\0'
,
sizeof
(
finalcount
));
}
// END OF SHA1 CODE
static
void
base64_encode
(
const
unsigned
char
*
src
,
int
src_len
,
char
*
dst
)
{
static
const
char
*
b64
=
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
;
int
i
,
j
,
a
,
b
,
c
;
for
(
i
=
j
=
0
;
i
<
src_len
;
i
+=
3
)
{
a
=
src
[
i
];
b
=
i
+
1
>=
src_len
?
0
:
src
[
i
+
1
];
c
=
i
+
2
>=
src_len
?
0
:
src
[
i
+
2
];
dst
[
j
++
]
=
b64
[
a
>>
2
];
dst
[
j
++
]
=
b64
[((
a
&
3
)
<<
4
)
|
(
b
>>
4
)];
if
(
i
+
1
<
src_len
)
{
dst
[
j
++
]
=
b64
[(
b
&
15
)
<<
2
|
(
c
>>
6
)];
}
if
(
i
+
2
<
src_len
)
{
dst
[
j
++
]
=
b64
[
c
&
63
];
}
}
while
(
j
%
4
!=
0
)
{
dst
[
j
++
]
=
'='
;
}
dst
[
j
++
]
=
'\0'
;
}
static
void
send_websocket_handshake
(
struct
mg_connection
*
conn
)
{
static
const
char
*
magic
=
"258EAFA5-E914-47DA-95CA-C5AB0DC85B11"
;
char
buf
[
100
],
sha
[
20
],
b64_sha
[
sizeof
(
sha
)
*
2
];
SHA1_CTX
sha_ctx
;
mg_snprintf
(
conn
,
buf
,
sizeof
(
buf
),
"%s%s"
,
mg_get_header
(
conn
,
"Sec-WebSocket-Key"
),
magic
);
SHA1Init
(
&
sha_ctx
);
SHA1Update
(
&
sha_ctx
,
(
unsigned
char
*
)
buf
,
strlen
(
buf
));
SHA1Final
((
unsigned
char
*
)
sha
,
&
sha_ctx
);
base64_encode
((
unsigned
char
*
)
sha
,
sizeof
(
sha
),
b64_sha
);
mg_printf
(
conn
,
"%s%s%s"
,
"HTTP/1.1 101 Switching Protocols
\r\n
"
"Upgrade: websocket
\r\n
"
"Connection: Upgrade
\r\n
"
"Sec-WebSocket-Accept: "
,
b64_sha
,
"
\r\n\r\n
"
);
}
static
void
read_websocket
(
struct
mg_connection
*
conn
)
{
unsigned
char
*
mask
,
*
buf
=
(
unsigned
char
*
)
conn
->
body
;
int
n
,
len
,
mask_len
,
body_len
;
for
(;;)
{
if
((
body_len
=
conn
->
data_len
-
conn
->
request_len
)
>=
2
)
{
len
=
buf
[
1
]
&
127
;
mask_len
=
buf
[
1
]
&
128
?
4
:
0
;
if
(
len
<
126
)
{
conn
->
content_len
=
2
+
mask_len
+
len
;
mask
=
buf
+
2
;
}
else
if
(
len
==
126
&&
body_len
>=
4
)
{
conn
->
content_len
=
2
+
mask_len
+
((((
int
)
buf
[
2
])
<<
8
)
+
buf
[
3
]);
mask
=
buf
+
4
;
}
else
if
(
body_len
>=
10
)
{
conn
->
content_len
=
2
+
mask_len
+
((
uint64_t
)
htonl
(
*
(
uint32_t
*
)
&
buf
[
2
]))
<<
32
|
htonl
(
*
(
uint32_t
*
)
&
buf
[
6
]);
mask
=
buf
+
10
;
}
}
if
(
conn
->
content_len
>
0
)
{
conn
->
next_request
=
conn
->
buf
+
conn
->
data_len
;
if
(
call_user
(
conn
,
MG_WEBSOCKET_MESSAGE
)
!=
NULL
)
{
break
;
// Callback signalled to exit
}
}
else
{
if
(
wait_until_socket_is_readable
(
conn
)
==
0
)
{
break
;
}
n
=
pull
(
NULL
,
conn
,
conn
->
buf
+
conn
->
data_len
,
conn
->
buf_size
-
conn
->
data_len
);
if
(
n
<=
0
)
{
break
;
}
conn
->
data_len
+=
n
;
}
}
}
static
void
handle_websocket_request
(
struct
mg_connection
*
conn
)
{
if
(
strcmp
(
mg_get_header
(
conn
,
"Sec-WebSocket-Version"
),
"13"
)
!=
0
)
{
send_http_error
(
conn
,
426
,
"Upgrade Required"
,
"%s"
,
"Upgrade Required"
);
}
else
if
(
call_user
(
conn
,
MG_WEBSOCKET_CONNECT
)
!=
NULL
)
{
// Callback has returned non-NULL, do not proceed with handshake
}
else
{
send_websocket_handshake
(
conn
);
call_user
(
conn
,
MG_WEBSOCKET_READY
);
read_websocket
(
conn
);
}
}
static
int
is_websocket_request
(
const
struct
mg_connection
*
conn
)
{
const
char
*
host
,
*
upgrade
,
*
connection
,
*
version
,
*
key
;
host
=
mg_get_header
(
conn
,
"Host"
);
upgrade
=
mg_get_header
(
conn
,
"Upgrade"
);
connection
=
mg_get_header
(
conn
,
"Connection"
);
key
=
mg_get_header
(
conn
,
"Sec-WebSocket-Key"
);
version
=
mg_get_header
(
conn
,
"Sec-WebSocket-Version"
);
return
host
!=
NULL
&&
upgrade
!=
NULL
&&
connection
!=
NULL
&&
key
!=
NULL
&&
version
!=
NULL
&&
!
mg_strcasecmp
(
upgrade
,
"websocket"
)
&&
!
mg_strcasecmp
(
connection
,
"Upgrade"
);
}
#endif // !USE_WEBSOCKET
// This is the heart of the Mongoose's logic.
// This is the heart of the Mongoose's logic.
// This function is called when the request is read, parsed and validated,
// This function is called when the request is read, parsed and validated,
// and Mongoose must decide what action to take: serve a file, or
// and Mongoose must decide what action to take: serve a file, or
...
@@ -3499,6 +3752,10 @@ static void handle_request(struct mg_connection *conn) {
...
@@ -3499,6 +3752,10 @@ static void handle_request(struct mg_connection *conn) {
DEBUG_TRACE
((
"%s"
,
ri
->
uri
));
DEBUG_TRACE
((
"%s"
,
ri
->
uri
));
if
(
!
check_authorization
(
conn
,
path
))
{
if
(
!
check_authorization
(
conn
,
path
))
{
send_authorization_request
(
conn
);
send_authorization_request
(
conn
);
#if defined(USE_WEBSOCKET)
}
else
if
(
is_websocket_request
(
conn
))
{
handle_websocket_request
(
conn
);
#endif
}
else
if
(
call_user
(
conn
,
MG_NEW_REQUEST
)
!=
NULL
)
{
}
else
if
(
call_user
(
conn
,
MG_NEW_REQUEST
)
!=
NULL
)
{
// Do nothing, callback has served the request
// Do nothing, callback has served the request
}
else
if
(
!
strcmp
(
ri
->
request_method
,
"OPTIONS"
))
{
}
else
if
(
!
strcmp
(
ri
->
request_method
,
"OPTIONS"
))
{
...
@@ -3608,12 +3865,12 @@ static int set_ports_option(struct mg_context *ctx) {
...
@@ -3608,12 +3865,12 @@ static int set_ports_option(struct mg_context *ctx) {
while
(
success
&&
(
list
=
next_option
(
list
,
&
vec
,
NULL
))
!=
NULL
)
{
while
(
success
&&
(
list
=
next_option
(
list
,
&
vec
,
NULL
))
!=
NULL
)
{
if
(
!
parse_port_string
(
&
vec
,
&
so
))
{
if
(
!
parse_port_string
(
&
vec
,
&
so
))
{
cry
(
fc
(),
"%s: %.*s: invalid port spec. Expecting list of: %s"
,
cry
(
fc
(
ctx
),
"%s: %.*s: invalid port spec. Expecting list of: %s"
,
__func__
,
(
int
)
vec
.
len
,
vec
.
ptr
,
"[IP_ADDRESS:]PORT[s|p]"
);
__func__
,
(
int
)
vec
.
len
,
vec
.
ptr
,
"[IP_ADDRESS:]PORT[s|p]"
);
success
=
0
;
success
=
0
;
}
else
if
(
so
.
is_ssl
&&
}
else
if
(
so
.
is_ssl
&&
(
ctx
->
ssl_ctx
==
NULL
||
ctx
->
config
[
SSL_CERTIFICATE
]
==
NULL
))
{
(
ctx
->
ssl_ctx
==
NULL
||
ctx
->
config
[
SSL_CERTIFICATE
]
==
NULL
))
{
cry
(
fc
(),
"Cannot add SSL socket, is -ssl_certificate option set?"
);
cry
(
fc
(
ctx
),
"Cannot add SSL socket, is -ssl_certificate option set?"
);
success
=
0
;
success
=
0
;
}
else
if
((
sock
=
socket
(
so
.
lsa
.
sa
.
sa_family
,
SOCK_STREAM
,
6
))
==
}
else
if
((
sock
=
socket
(
so
.
lsa
.
sa
.
sa_family
,
SOCK_STREAM
,
6
))
==
INVALID_SOCKET
||
INVALID_SOCKET
||
...
@@ -3635,14 +3892,14 @@ static int set_ports_option(struct mg_context *ctx) {
...
@@ -3635,14 +3892,14 @@ static int set_ports_option(struct mg_context *ctx) {
bind
(
sock
,
&
so
.
lsa
.
sa
,
sizeof
(
so
.
lsa
))
!=
0
||
bind
(
sock
,
&
so
.
lsa
.
sa
,
sizeof
(
so
.
lsa
))
!=
0
||
listen
(
sock
,
SOMAXCONN
)
!=
0
)
{
listen
(
sock
,
SOMAXCONN
)
!=
0
)
{
closesocket
(
sock
);
closesocket
(
sock
);
cry
(
fc
(),
"%s: cannot bind to %.*s: %s"
,
__func__
,
cry
(
fc
(
ctx
),
"%s: cannot bind to %.*s: %s"
,
__func__
,
vec
.
len
,
vec
.
ptr
,
strerror
(
ERRNO
));
vec
.
len
,
vec
.
ptr
,
strerror
(
ERRNO
));
success
=
0
;
success
=
0
;
}
else
if
((
listener
=
(
struct
socket
*
)
}
else
if
((
listener
=
(
struct
socket
*
)
calloc
(
1
,
sizeof
(
*
listener
)))
==
NULL
)
{
calloc
(
1
,
sizeof
(
*
listener
)))
==
NULL
)
{
// NOTE(lsm): order is important: call cry before closesocket(),
// NOTE(lsm): order is important: call cry before closesocket(),
// cause closesocket() alters the errno.
// cause closesocket() alters the errno.
cry
(
fc
(),
"%s: %s"
,
__func__
,
strerror
(
ERRNO
));
cry
(
fc
(
ctx
),
"%s: %s"
,
__func__
,
strerror
(
ERRNO
));
closesocket
(
sock
);
closesocket
(
sock
);
success
=
0
;
success
=
0
;
}
else
{
}
else
{
...
@@ -3694,7 +3951,7 @@ static void log_access(const struct mg_connection *conn) {
...
@@ -3694,7 +3951,7 @@ static void log_access(const struct mg_connection *conn) {
src_addr
,
ri
->
remote_user
==
NULL
?
"-"
:
ri
->
remote_user
,
date
,
src_addr
,
ri
->
remote_user
==
NULL
?
"-"
:
ri
->
remote_user
,
date
,
ri
->
request_method
?
ri
->
request_method
:
"-"
,
ri
->
request_method
?
ri
->
request_method
:
"-"
,
ri
->
uri
?
ri
->
uri
:
"-"
,
ri
->
http_version
,
ri
->
uri
?
ri
->
uri
:
"-"
,
ri
->
http_version
,
conn
->
request_info
.
status_code
,
conn
->
num_bytes_sent
);
conn
->
status_code
,
conn
->
num_bytes_sent
);
log_header
(
conn
,
"Referer"
,
fp
);
log_header
(
conn
,
"Referer"
,
fp
);
log_header
(
conn
,
"User-Agent"
,
fp
);
log_header
(
conn
,
"User-Agent"
,
fp
);
fputc
(
'\n'
,
fp
);
fputc
(
'\n'
,
fp
);
...
@@ -3730,18 +3987,18 @@ static int check_acl(struct mg_context *ctx, const union usa *usa) {
...
@@ -3730,18 +3987,18 @@ static int check_acl(struct mg_context *ctx, const union usa *usa) {
mask
=
32
;
mask
=
32
;
if
(
sscanf
(
vec
.
ptr
,
"%c%d.%d.%d.%d%n"
,
&
flag
,
&
a
,
&
b
,
&
c
,
&
d
,
&
n
)
!=
5
)
{
if
(
sscanf
(
vec
.
ptr
,
"%c%d.%d.%d.%d%n"
,
&
flag
,
&
a
,
&
b
,
&
c
,
&
d
,
&
n
)
!=
5
)
{
cry
(
fc
(),
"%s: subnet must be [+|-]x.x.x.x[/x]"
,
__func__
);
cry
(
fc
(
ctx
),
"%s: subnet must be [+|-]x.x.x.x[/x]"
,
__func__
);
return
-
1
;
return
-
1
;
}
else
if
(
flag
!=
'+'
&&
flag
!=
'-'
)
{
}
else
if
(
flag
!=
'+'
&&
flag
!=
'-'
)
{
cry
(
fc
(),
"%s: flag must be + or -: [%s]"
,
__func__
,
vec
.
ptr
);
cry
(
fc
(
ctx
),
"%s: flag must be + or -: [%s]"
,
__func__
,
vec
.
ptr
);
return
-
1
;
return
-
1
;
}
else
if
(
!
isbyte
(
a
)
||!
isbyte
(
b
)
||!
isbyte
(
c
)
||!
isbyte
(
d
))
{
}
else
if
(
!
isbyte
(
a
)
||!
isbyte
(
b
)
||!
isbyte
(
c
)
||!
isbyte
(
d
))
{
cry
(
fc
(),
"%s: bad ip address: [%s]"
,
__func__
,
vec
.
ptr
);
cry
(
fc
(
ctx
),
"%s: bad ip address: [%s]"
,
__func__
,
vec
.
ptr
);
return
-
1
;
return
-
1
;
}
else
if
(
sscanf
(
vec
.
ptr
+
n
,
"/%d"
,
&
mask
)
==
0
)
{
}
else
if
(
sscanf
(
vec
.
ptr
+
n
,
"/%d"
,
&
mask
)
==
0
)
{
// Do nothing, no mask specified
// Do nothing, no mask specified
}
else
if
(
mask
<
0
||
mask
>
32
)
{
}
else
if
(
mask
<
0
||
mask
>
32
)
{
cry
(
fc
(),
"%s: bad subnet mask: %d [%s]"
,
__func__
,
n
,
vec
.
ptr
);
cry
(
fc
(
ctx
),
"%s: bad subnet mask: %d [%s]"
,
__func__
,
n
,
vec
.
ptr
);
return
-
1
;
return
-
1
;
}
}
...
@@ -3773,11 +4030,11 @@ static int set_uid_option(struct mg_context *ctx) {
...
@@ -3773,11 +4030,11 @@ static int set_uid_option(struct mg_context *ctx) {
success
=
1
;
success
=
1
;
}
else
{
}
else
{
if
((
pw
=
getpwnam
(
uid
))
==
NULL
)
{
if
((
pw
=
getpwnam
(
uid
))
==
NULL
)
{
cry
(
fc
(),
"%s: unknown user [%s]"
,
__func__
,
uid
);
cry
(
fc
(
ctx
),
"%s: unknown user [%s]"
,
__func__
,
uid
);
}
else
if
(
setgid
(
pw
->
pw_gid
)
==
-
1
)
{
}
else
if
(
setgid
(
pw
->
pw_gid
)
==
-
1
)
{
cry
(
fc
(),
"%s: setgid(%s): %s"
,
__func__
,
uid
,
strerror
(
errno
));
cry
(
fc
(
ctx
),
"%s: setgid(%s): %s"
,
__func__
,
uid
,
strerror
(
errno
));
}
else
if
(
setuid
(
pw
->
pw_uid
)
==
-
1
)
{
}
else
if
(
setuid
(
pw
->
pw_uid
)
==
-
1
)
{
cry
(
fc
(),
"%s: setuid(%s): %s"
,
__func__
,
uid
,
strerror
(
errno
));
cry
(
fc
(
ctx
),
"%s: setuid(%s): %s"
,
__func__
,
uid
,
strerror
(
errno
));
}
else
{
}
else
{
success
=
1
;
success
=
1
;
}
}
...
@@ -3814,13 +4071,14 @@ static unsigned long ssl_id_callback(void) {
...
@@ -3814,13 +4071,14 @@ static unsigned long ssl_id_callback(void) {
}
}
#if !defined(NO_SSL_DL)
#if !defined(NO_SSL_DL)
static
int
load_dll
(
const
char
*
dll_name
,
struct
ssl_func
*
sw
)
{
static
int
load_dll
(
struct
mg_context
*
ctx
,
const
char
*
dll_name
,
struct
ssl_func
*
sw
)
{
union
{
void
*
p
;
void
(
*
fp
)(
void
);}
u
;
union
{
void
*
p
;
void
(
*
fp
)(
void
);}
u
;
void
*
dll_handle
;
void
*
dll_handle
;
struct
ssl_func
*
fp
;
struct
ssl_func
*
fp
;
if
((
dll_handle
=
dlopen
(
dll_name
,
RTLD_LAZY
))
==
NULL
)
{
if
((
dll_handle
=
dlopen
(
dll_name
,
RTLD_LAZY
))
==
NULL
)
{
cry
(
fc
(),
"%s: cannot load %s"
,
__func__
,
dll_name
);
cry
(
fc
(
ctx
),
"%s: cannot load %s"
,
__func__
,
dll_name
);
return
0
;
return
0
;
}
}
...
@@ -3834,7 +4092,7 @@ static int load_dll(const char *dll_name, struct ssl_func *sw) {
...
@@ -3834,7 +4092,7 @@ static int load_dll(const char *dll_name, struct ssl_func *sw) {
u
.
p
=
dlsym
(
dll_handle
,
fp
->
name
);
u
.
p
=
dlsym
(
dll_handle
,
fp
->
name
);
#endif // _WIN32
#endif // _WIN32
if
(
u
.
fp
==
NULL
)
{
if
(
u
.
fp
==
NULL
)
{
cry
(
fc
(),
"%s: %s: cannot find %s"
,
__func__
,
dll_name
,
fp
->
name
);
cry
(
fc
(
ctx
),
"%s: %s: cannot find %s"
,
__func__
,
dll_name
,
fp
->
name
);
return
0
;
return
0
;
}
else
{
}
else
{
fp
->
ptr
=
u
.
fp
;
fp
->
ptr
=
u
.
fp
;
...
@@ -3847,13 +4105,12 @@ static int load_dll(const char *dll_name, struct ssl_func *sw) {
...
@@ -3847,13 +4105,12 @@ static int load_dll(const char *dll_name, struct ssl_func *sw) {
// Dynamically load SSL library. Set up ctx->ssl_ctx pointer.
// Dynamically load SSL library. Set up ctx->ssl_ctx pointer.
static
int
set_ssl_option
(
struct
mg_context
*
ctx
)
{
static
int
set_ssl_option
(
struct
mg_context
*
ctx
)
{
struct
mg_request_info
request_info
;
int
i
,
size
;
int
i
,
size
;
const
char
*
pem
=
ctx
->
config
[
SSL_CERTIFICATE
];
const
char
*
pem
=
ctx
->
config
[
SSL_CERTIFICATE
];
const
char
*
chain
=
ctx
->
config
[
SSL_CHAIN_FILE
];
#if !defined(NO_SSL_DL)
#if !defined(NO_SSL_DL)
if
(
!
load_dll
(
SSL_LIB
,
ssl_sw
)
||
!
load_dll
(
CRYPTO_LIB
,
crypto_sw
))
{
if
(
!
load_dll
(
ctx
,
SSL_LIB
,
ssl_sw
)
||
!
load_dll
(
ctx
,
CRYPTO_LIB
,
crypto_sw
))
{
return
0
;
return
0
;
}
}
#endif // NO_SSL_DL
#endif // NO_SSL_DL
...
@@ -3863,38 +4120,32 @@ static int set_ssl_option(struct mg_context *ctx) {
...
@@ -3863,38 +4120,32 @@ static int set_ssl_option(struct mg_context *ctx) {
SSL_load_error_strings
();
SSL_load_error_strings
();
if
((
ctx
->
client_ssl_ctx
=
SSL_CTX_new
(
SSLv23_client_method
()))
==
NULL
)
{
if
((
ctx
->
client_ssl_ctx
=
SSL_CTX_new
(
SSLv23_client_method
()))
==
NULL
)
{
cry
(
fc
(
),
"SSL_CTX_new
error: %s"
,
ssl_error
());
cry
(
fc
(
ctx
),
"SSL_CTX_new (client)
error: %s"
,
ssl_error
());
}
}
if
((
ctx
->
ssl_ctx
=
SSL_CTX_new
(
SSLv23_server_method
()))
==
NULL
)
{
if
((
ctx
->
ssl_ctx
=
SSL_CTX_new
(
SSLv23_server_method
()))
==
NULL
)
{
cry
(
fc
(),
"SSL_CTX_new error: %s"
,
ssl_error
());
cry
(
fc
(
ctx
),
"SSL_CTX_new (server) error: %s"
,
ssl_error
());
}
else
if
(
ctx
->
user_callback
!=
NULL
)
{
memset
(
&
request_info
,
0
,
sizeof
(
request_info
));
request_info
.
user_data
=
ctx
->
user_data
;
ctx
->
user_callback
(
MG_INIT_SSL
,
(
struct
mg_connection
*
)
ctx
->
ssl_ctx
);
}
if
(
ctx
->
ssl_ctx
!=
NULL
&&
pem
!=
NULL
&&
SSL_CTX_use_certificate_file
(
ctx
->
ssl_ctx
,
pem
,
SSL_FILETYPE_PEM
)
==
0
)
{
cry
(
fc
(),
"%s: cannot open %s: %s"
,
__func__
,
pem
,
ssl_error
());
return
0
;
return
0
;
}
}
if
(
ctx
->
ssl_ctx
!=
NULL
&&
pem
!=
NULL
&&
SSL_CTX_use_PrivateKey_file
(
ctx
->
ssl_ctx
,
pem
,
SSL_FILETYPE_PEM
)
==
0
)
{
// If user callback returned non-NULL, that means that user callback has
cry
(
fc
(),
"%s: cannot open %s: %s"
,
__func__
,
pem
,
ssl_error
());
// set up certificate itself. In this case, skip sertificate setting.
if
(
call_user
(
fc
(
ctx
),
MG_INIT_SSL
)
==
NULL
&&
pem
!=
NULL
&&
(
SSL_CTX_use_certificate_file
(
ctx
->
ssl_ctx
,
pem
,
SSL_FILETYPE_PEM
)
==
0
||
SSL_CTX_use_PrivateKey_file
(
ctx
->
ssl_ctx
,
pem
,
SSL_FILETYPE_PEM
)
==
0
))
{
cry
(
fc
(
ctx
),
"%s: cannot open %s: %s"
,
__func__
,
pem
,
ssl_error
());
return
0
;
return
0
;
}
}
if
(
ctx
->
ssl_ctx
!=
NULL
&&
chain
!=
NULL
&&
SSL_CTX_use_certificate_chain_file
(
ctx
->
ssl_ctx
,
chain
)
==
0
)
{
if
(
pem
!=
NULL
)
{
cry
(
fc
(),
"%s: cannot open %s: %s"
,
__func__
,
chain
,
ssl_error
());
(
void
)
SSL_CTX_use_certificate_chain_file
(
ctx
->
ssl_ctx
,
pem
);
return
0
;
}
}
// Initialize locking callbacks, needed for thread safety.
// Initialize locking callbacks, needed for thread safety.
// http://www.openssl.org/support/faq.html#PROG1
// http://www.openssl.org/support/faq.html#PROG1
size
=
sizeof
(
pthread_mutex_t
)
*
CRYPTO_num_locks
();
size
=
sizeof
(
pthread_mutex_t
)
*
CRYPTO_num_locks
();
if
((
ssl_mutexes
=
(
pthread_mutex_t
*
)
malloc
((
size_t
)
size
))
==
NULL
)
{
if
((
ssl_mutexes
=
(
pthread_mutex_t
*
)
malloc
((
size_t
)
size
))
==
NULL
)
{
cry
(
fc
(),
"%s: cannot allocate mutexes: %s"
,
__func__
,
ssl_error
());
cry
(
fc
(
ctx
),
"%s: cannot allocate mutexes: %s"
,
__func__
,
ssl_error
());
return
0
;
return
0
;
}
}
...
@@ -3933,11 +4184,12 @@ static int set_acl_option(struct mg_context *ctx) {
...
@@ -3933,11 +4184,12 @@ static int set_acl_option(struct mg_context *ctx) {
}
}
static
void
reset_per_request_attributes
(
struct
mg_connection
*
conn
)
{
static
void
reset_per_request_attributes
(
struct
mg_connection
*
conn
)
{
conn
->
path_info
=
conn
->
body
=
conn
->
next_request
=
NULL
;
conn
->
path_info
=
conn
->
body
=
conn
->
next_request
=
conn
->
log_message
=
NULL
;
conn
->
num_bytes_sent
=
conn
->
consumed_content
=
0
;
conn
->
num_bytes_sent
=
conn
->
consumed_content
=
0
;
conn
->
content_len
=
-
1
;
conn
->
content_len
=
-
1
;
conn
->
request_len
=
conn
->
data_len
=
0
;
conn
->
request_len
=
conn
->
data_len
=
0
;
conn
->
must_close
=
0
;
conn
->
must_close
=
0
;
conn
->
status_code
=
-
1
;
}
}
static
void
close_socket_gracefully
(
struct
mg_connection
*
conn
)
{
static
void
close_socket_gracefully
(
struct
mg_connection
*
conn
)
{
...
@@ -3992,22 +4244,22 @@ struct mg_connection *mg_connect(struct mg_context *ctx,
...
@@ -3992,22 +4244,22 @@ struct mg_connection *mg_connect(struct mg_context *ctx,
int
sock
;
int
sock
;
if
(
use_ssl
&&
(
ctx
==
NULL
||
ctx
->
client_ssl_ctx
==
NULL
))
{
if
(
use_ssl
&&
(
ctx
==
NULL
||
ctx
->
client_ssl_ctx
==
NULL
))
{
cry
(
fc
(),
"%s: SSL is not initialized"
,
__func__
);
cry
(
fc
(
ctx
),
"%s: SSL is not initialized"
,
__func__
);
}
else
if
((
he
=
gethostbyname
(
host
))
==
NULL
)
{
}
else
if
((
he
=
gethostbyname
(
host
))
==
NULL
)
{
cry
(
fc
(),
"%s: gethostbyname(%s): %s"
,
__func__
,
host
,
strerror
(
ERRNO
));
cry
(
fc
(
ctx
),
"%s: gethostbyname(%s): %s"
,
__func__
,
host
,
strerror
(
ERRNO
));
}
else
if
((
sock
=
socket
(
PF_INET
,
SOCK_STREAM
,
0
))
==
INVALID_SOCKET
)
{
}
else
if
((
sock
=
socket
(
PF_INET
,
SOCK_STREAM
,
0
))
==
INVALID_SOCKET
)
{
cry
(
fc
(),
"%s: socket: %s"
,
__func__
,
strerror
(
ERRNO
));
cry
(
fc
(
ctx
),
"%s: socket: %s"
,
__func__
,
strerror
(
ERRNO
));
}
else
{
}
else
{
sin
.
sin_family
=
AF_INET
;
sin
.
sin_family
=
AF_INET
;
sin
.
sin_port
=
htons
((
uint16_t
)
port
);
sin
.
sin_port
=
htons
((
uint16_t
)
port
);
sin
.
sin_addr
=
*
(
struct
in_addr
*
)
he
->
h_addr_list
[
0
];
sin
.
sin_addr
=
*
(
struct
in_addr
*
)
he
->
h_addr_list
[
0
];
if
(
connect
(
sock
,
(
struct
sockaddr
*
)
&
sin
,
sizeof
(
sin
))
!=
0
)
{
if
(
connect
(
sock
,
(
struct
sockaddr
*
)
&
sin
,
sizeof
(
sin
))
!=
0
)
{
cry
(
fc
(),
"%s: connect(%s:%d): %s"
,
__func__
,
host
,
port
,
cry
(
fc
(
ctx
),
"%s: connect(%s:%d): %s"
,
__func__
,
host
,
port
,
strerror
(
ERRNO
));
strerror
(
ERRNO
));
closesocket
(
sock
);
closesocket
(
sock
);
}
else
if
((
newconn
=
(
struct
mg_connection
*
)
}
else
if
((
newconn
=
(
struct
mg_connection
*
)
calloc
(
1
,
sizeof
(
*
newconn
)))
==
NULL
)
{
calloc
(
1
,
sizeof
(
*
newconn
)))
==
NULL
)
{
cry
(
fc
(),
"%s: calloc: %s"
,
__func__
,
strerror
(
ERRNO
));
cry
(
fc
(
ctx
),
"%s: calloc: %s"
,
__func__
,
strerror
(
ERRNO
));
closesocket
(
sock
);
closesocket
(
sock
);
}
else
{
}
else
{
newconn
->
ctx
=
ctx
;
newconn
->
ctx
=
ctx
;
...
@@ -4034,29 +4286,29 @@ FILE *mg_fetch(struct mg_context *ctx, const char *url, const char *path,
...
@@ -4034,29 +4286,29 @@ FILE *mg_fetch(struct mg_context *ctx, const char *url, const char *path,
}
else
if
(
sscanf
(
url
,
"%9[htps]://%1024[^/]/%n"
,
proto
,
host
,
&
n
)
==
2
)
{
}
else
if
(
sscanf
(
url
,
"%9[htps]://%1024[^/]/%n"
,
proto
,
host
,
&
n
)
==
2
)
{
port
=
mg_strcasecmp
(
proto
,
"https"
)
==
0
?
443
:
80
;
port
=
mg_strcasecmp
(
proto
,
"https"
)
==
0
?
443
:
80
;
}
else
{
}
else
{
cry
(
fc
(),
"%s: invalid URL: [%s]"
,
__func__
,
url
);
cry
(
fc
(
ctx
),
"%s: invalid URL: [%s]"
,
__func__
,
url
);
return
NULL
;
return
NULL
;
}
}
if
((
newconn
=
mg_connect
(
ctx
,
host
,
port
,
if
((
newconn
=
mg_connect
(
ctx
,
host
,
port
,
!
strcmp
(
proto
,
"https"
)))
==
NULL
)
{
!
strcmp
(
proto
,
"https"
)))
==
NULL
)
{
cry
(
fc
(),
"%s: mg_connect(%s): %s"
,
__func__
,
url
,
strerror
(
ERRNO
));
cry
(
fc
(
ctx
),
"%s: mg_connect(%s): %s"
,
__func__
,
url
,
strerror
(
ERRNO
));
}
else
{
}
else
{
mg_printf
(
newconn
,
"GET /%s HTTP/1.0
\r\n
Host: %s
\r\n\r\n
"
,
url
+
n
,
host
);
mg_printf
(
newconn
,
"GET /%s HTTP/1.0
\r\n
Host: %s
\r\n\r\n
"
,
url
+
n
,
host
);
data_length
=
0
;
data_length
=
0
;
req_length
=
read_request
(
NULL
,
newconn
,
buf
,
buf_len
,
&
data_length
);
req_length
=
read_request
(
NULL
,
newconn
,
buf
,
buf_len
,
&
data_length
);
if
(
req_length
<=
0
)
{
if
(
req_length
<=
0
)
{
cry
(
fc
(),
"%s(%s): invalid HTTP reply"
,
__func__
,
url
);
cry
(
fc
(
ctx
),
"%s(%s): invalid HTTP reply"
,
__func__
,
url
);
}
else
if
(
parse_http_response
(
buf
,
req_length
,
ri
)
<=
0
)
{
}
else
if
(
parse_http_response
(
buf
,
req_length
,
ri
)
<=
0
)
{
cry
(
fc
(),
"%s(%s): cannot parse HTTP headers"
,
__func__
,
url
);
cry
(
fc
(
ctx
),
"%s(%s): cannot parse HTTP headers"
,
__func__
,
url
);
}
else
if
((
fp
=
fopen
(
path
,
"w+b"
))
==
NULL
)
{
}
else
if
((
fp
=
fopen
(
path
,
"w+b"
))
==
NULL
)
{
cry
(
fc
(),
"%s: fopen(%s): %s"
,
__func__
,
path
,
strerror
(
ERRNO
));
cry
(
fc
(
ctx
),
"%s: fopen(%s): %s"
,
__func__
,
path
,
strerror
(
ERRNO
));
}
else
{
}
else
{
// Write chunk of data that may be in the user's buffer
// Write chunk of data that may be in the user's buffer
data_length
-=
req_length
;
data_length
-=
req_length
;
if
(
data_length
>
0
&&
if
(
data_length
>
0
&&
fwrite
(
buf
+
req_length
,
1
,
data_length
,
fp
)
!=
(
size_t
)
data_length
)
{
fwrite
(
buf
+
req_length
,
1
,
data_length
,
fp
)
!=
(
size_t
)
data_length
)
{
cry
(
fc
(),
"%s: fwrite(%s): %s"
,
__func__
,
path
,
strerror
(
ERRNO
));
cry
(
fc
(
ctx
),
"%s: fwrite(%s): %s"
,
__func__
,
path
,
strerror
(
ERRNO
));
fclose
(
fp
);
fclose
(
fp
);
fp
=
NULL
;
fp
=
NULL
;
}
}
...
@@ -4064,7 +4316,7 @@ FILE *mg_fetch(struct mg_context *ctx, const char *url, const char *path,
...
@@ -4064,7 +4316,7 @@ FILE *mg_fetch(struct mg_context *ctx, const char *url, const char *path,
// mg_read() cause we didn't set newconn->content_len properly.
// mg_read() cause we didn't set newconn->content_len properly.
while
(
fp
&&
(
data_length
=
pull
(
0
,
newconn
,
buf2
,
sizeof
(
buf2
)))
>
0
)
{
while
(
fp
&&
(
data_length
=
pull
(
0
,
newconn
,
buf2
,
sizeof
(
buf2
)))
>
0
)
{
if
(
fwrite
(
buf2
,
1
,
data_length
,
fp
)
!=
(
size_t
)
data_length
)
{
if
(
fwrite
(
buf2
,
1
,
data_length
,
fp
)
!=
(
size_t
)
data_length
)
{
cry
(
fc
(),
"%s: fwrite(%s): %s"
,
__func__
,
path
,
strerror
(
ERRNO
));
cry
(
fc
(
ctx
),
"%s: fwrite(%s): %s"
,
__func__
,
path
,
strerror
(
ERRNO
));
fclose
(
fp
);
fclose
(
fp
);
fp
=
NULL
;
fp
=
NULL
;
break
;
break
;
...
@@ -4180,13 +4432,12 @@ static int consume_socket(struct mg_context *ctx, struct socket *sp) {
...
@@ -4180,13 +4432,12 @@ static int consume_socket(struct mg_context *ctx, struct socket *sp) {
static
void
worker_thread
(
struct
mg_context
*
ctx
)
{
static
void
worker_thread
(
struct
mg_context
*
ctx
)
{
struct
mg_connection
*
conn
;
struct
mg_connection
*
conn
;
int
buf_size
=
atoi
(
ctx
->
config
[
MAX_REQUEST_SIZE
]);
conn
=
(
struct
mg_connection
*
)
calloc
(
1
,
sizeof
(
*
conn
)
+
buf_size
);
conn
=
(
struct
mg_connection
*
)
calloc
(
1
,
sizeof
(
*
conn
)
+
MAX_REQUEST_SIZE
);
if
(
conn
==
NULL
)
{
if
(
conn
==
NULL
)
{
cry
(
fc
(),
"%s"
,
"Cannot create new connection struct, OOM"
);
cry
(
fc
(
ctx
),
"%s"
,
"Cannot create new connection struct, OOM"
);
}
else
{
}
else
{
conn
->
buf_size
=
buf_size
;
conn
->
buf_size
=
MAX_REQUEST_SIZE
;
conn
->
buf
=
(
char
*
)
(
conn
+
1
);
conn
->
buf
=
(
char
*
)
(
conn
+
1
);
// Call consume_socket() even when ctx->stop_flag > 0, to let it signal
// Call consume_socket() even when ctx->stop_flag > 0, to let it signal
...
@@ -4266,7 +4517,7 @@ static void accept_new_connection(const struct socket *listener,
...
@@ -4266,7 +4517,7 @@ static void accept_new_connection(const struct socket *listener,
produce_socket
(
ctx
,
&
accepted
);
produce_socket
(
ctx
,
&
accepted
);
}
else
{
}
else
{
sockaddr_to_string
(
src_addr
,
sizeof
(
src_addr
),
&
accepted
.
rsa
);
sockaddr_to_string
(
src_addr
,
sizeof
(
src_addr
),
&
accepted
.
rsa
);
cry
(
fc
(),
"%s: %s is not allowed to connect"
,
__func__
,
src_addr
);
cry
(
fc
(
ctx
),
"%s: %s is not allowed to connect"
,
__func__
,
src_addr
);
(
void
)
closesocket
(
accepted
.
sock
);
(
void
)
closesocket
(
accepted
.
sock
);
}
}
}
}
...
@@ -4410,16 +4661,16 @@ struct mg_context *mg_start(mg_callback_t user_callback, void *user_data,
...
@@ -4410,16 +4661,16 @@ struct mg_context *mg_start(mg_callback_t user_callback, void *user_data,
while
(
options
&&
(
name
=
*
options
++
)
!=
NULL
)
{
while
(
options
&&
(
name
=
*
options
++
)
!=
NULL
)
{
if
((
i
=
get_option_index
(
name
))
==
-
1
)
{
if
((
i
=
get_option_index
(
name
))
==
-
1
)
{
cry
(
fc
(),
"Invalid option: %s"
,
name
);
cry
(
fc
(
ctx
),
"Invalid option: %s"
,
name
);
free_context
(
ctx
);
free_context
(
ctx
);
return
NULL
;
return
NULL
;
}
else
if
((
value
=
*
options
++
)
==
NULL
)
{
}
else
if
((
value
=
*
options
++
)
==
NULL
)
{
cry
(
fc
(),
"%s: option value cannot be NULL"
,
name
);
cry
(
fc
(
ctx
),
"%s: option value cannot be NULL"
,
name
);
free_context
(
ctx
);
free_context
(
ctx
);
return
NULL
;
return
NULL
;
}
}
if
(
ctx
->
config
[
i
]
!=
NULL
)
{
if
(
ctx
->
config
[
i
]
!=
NULL
)
{
cry
(
fc
(),
"warning: %s: duplicate option"
,
name
);
cry
(
fc
(
ctx
),
"warning: %s: duplicate option"
,
name
);
}
}
ctx
->
config
[
i
]
=
mg_strdup
(
value
);
ctx
->
config
[
i
]
=
mg_strdup
(
value
);
DEBUG_TRACE
((
"[%s] -> [%s]"
,
name
,
value
));
DEBUG_TRACE
((
"[%s] -> [%s]"
,
name
,
value
));
...
@@ -4440,7 +4691,7 @@ struct mg_context *mg_start(mg_callback_t user_callback, void *user_data,
...
@@ -4440,7 +4691,7 @@ struct mg_context *mg_start(mg_callback_t user_callback, void *user_data,
// be initialized before listening ports. UID must be set last.
// be initialized before listening ports. UID must be set last.
if
(
!
set_gpass_option
(
ctx
)
||
if
(
!
set_gpass_option
(
ctx
)
||
#if !defined(NO_SSL)
#if !defined(NO_SSL)
(
ctx
->
config
[
SSL_CERTIFICATE
]
!=
NULL
&&
!
set_ssl_option
(
ctx
)
)
||
!
set_ssl_option
(
ctx
)
||
#endif
#endif
!
set_ports_option
(
ctx
)
||
!
set_ports_option
(
ctx
)
||
#if !defined(_WIN32)
#if !defined(_WIN32)
...
@@ -4470,7 +4721,7 @@ struct mg_context *mg_start(mg_callback_t user_callback, void *user_data,
...
@@ -4470,7 +4721,7 @@ struct mg_context *mg_start(mg_callback_t user_callback, void *user_data,
// Start worker threads
// Start worker threads
for
(
i
=
0
;
i
<
atoi
(
ctx
->
config
[
NUM_THREADS
]);
i
++
)
{
for
(
i
=
0
;
i
<
atoi
(
ctx
->
config
[
NUM_THREADS
]);
i
++
)
{
if
(
mg_start_thread
((
mg_thread_func_t
)
worker_thread
,
ctx
)
!=
0
)
{
if
(
mg_start_thread
((
mg_thread_func_t
)
worker_thread
,
ctx
)
!=
0
)
{
cry
(
fc
(),
"Cannot start worker thread: %d"
,
ERRNO
);
cry
(
fc
(
ctx
),
"Cannot start worker thread: %d"
,
ERRNO
);
}
else
{
}
else
{
ctx
->
num_threads
++
;
ctx
->
num_threads
++
;
}
}
...
...
mongoose.h
View file @
2260c8dd
...
@@ -34,16 +34,13 @@ struct mg_connection; // Handle for the individual connection
...
@@ -34,16 +34,13 @@ struct mg_connection; // Handle for the individual connection
// This structure contains information about the HTTP request.
// This structure contains information about the HTTP request.
struct
mg_request_info
{
struct
mg_request_info
{
void
*
user_data
;
// User-defined pointer passed to mg_start()
char
*
request_method
;
// "GET", "POST", etc
char
*
request_method
;
// "GET", "POST", etc
char
*
uri
;
// URL-decoded URI
char
*
uri
;
// URL-decoded URI
char
*
http_version
;
// E.g. "1.0", "1.1"
char
*
http_version
;
// E.g. "1.0", "1.1"
char
*
query_string
;
// URL part after '?' (not including '?') or NULL
char
*
query_string
;
// URL part after '?' (not including '?') or NULL
char
*
remote_user
;
// Authenticated user, or NULL if no auth used
char
*
remote_user
;
// Authenticated user, or NULL if no auth used
char
*
log_message
;
// Mongoose error log message, MG_EVENT_LOG only
long
remote_ip
;
// Client's IP address
long
remote_ip
;
// Client's IP address
int
remote_port
;
// Client's port
int
remote_port
;
// Client's port
int
status_code
;
// HTTP reply status code, e.g. 200
int
is_ssl
;
// 1 if SSL-ed, 0 if not
int
is_ssl
;
// 1 if SSL-ed, 0 if not
int
num_headers
;
// Number of headers
int
num_headers
;
// Number of headers
struct
mg_header
{
struct
mg_header
{
...
@@ -52,16 +49,23 @@ struct mg_request_info {
...
@@ -52,16 +49,23 @@ struct mg_request_info {
}
http_headers
[
64
];
// Maximum 64 headers
}
http_headers
[
64
];
// Maximum 64 headers
};
};
// Various events on which user-defined function is called by Mongoose.
// Various events on which user-defined function is called by Mongoose.
enum
mg_event
{
enum
mg_event
{
MG_NEW_REQUEST
,
// New HTTP request has arrived from the client
MG_NEW_REQUEST
,
// New HTTP request has arrived from the client
MG_REQUEST_COMPLETE
,
// Mongoose has finished handling the request
MG_REQUEST_COMPLETE
,
// Mongoose has finished handling the request
MG_HTTP_ERROR
,
// HTTP error must be returned to the client
MG_HTTP_ERROR
,
// HTTP error must be returned to the client
MG_EVENT_LOG
,
// Mongoose logs an event, request_info.log_message
MG_EVENT_LOG
,
// Mongoose logs an event, request_info.log_message
MG_INIT_SSL
// Mongoose initializes SSL. Instead of mg_connection *,
MG_INIT_SSL
,
// SSL initialization, sent before certificate setup
// SSL context is passed to the callback function.
MG_WEBSOCKET_CONNECT
,
// Sent on HTTP connect, before websocket handshake.
// If user callback returns NULL, then mongoose proceeds
// with handshake, otherwise it closes the connection.
MG_WEBSOCKET_READY
,
// Handshake has been successfully completed.
MG_WEBSOCKET_MESSAGE
,
// Incoming message from the client
MG_WEBSOCKET_CLOSE
,
// Client has sent FIN frame
};
};
// Prototype for the user-defined function. Mongoose calls this function
// Prototype for the user-defined function. Mongoose calls this function
// on every MG_* event.
// on every MG_* event.
//
//
...
@@ -77,8 +81,7 @@ enum mg_event {
...
@@ -77,8 +81,7 @@ enum mg_event {
// If handler returns NULL, that means that handler has not processed
// If handler returns NULL, that means that handler has not processed
// the request. Handler must not send any data to the client in this case.
// the request. Handler must not send any data to the client in this case.
// Mongoose proceeds with request handling as if nothing happened.
// Mongoose proceeds with request handling as if nothing happened.
typedef
void
*
(
*
mg_callback_t
)(
enum
mg_event
event
,
typedef
void
*
(
*
mg_callback_t
)(
enum
mg_event
event
,
struct
mg_connection
*
conn
);
struct
mg_connection
*
conn
);
// Start web server.
// Start web server.
...
@@ -151,9 +154,13 @@ int mg_modify_passwords_file(const char *passwords_file_name,
...
@@ -151,9 +154,13 @@ int mg_modify_passwords_file(const char *passwords_file_name,
const
char
*
password
);
const
char
*
password
);
// Return
mg_request_info structure
associated with the request.
// Return
information
associated with the request.
//
Always succeeds
.
//
These functions always succeed
.
const
struct
mg_request_info
*
mg_get_request_info
(
const
struct
mg_connection
*
);
const
struct
mg_request_info
*
mg_get_request_info
(
const
struct
mg_connection
*
);
void
*
mg_get_user_data
(
struct
mg_connection
*
);
const
char
*
mg_get_log_message
(
const
struct
mg_connection
*
);
int
mg_get_reply_status_code
(
const
struct
mg_connection
*
);
void
*
mg_get_ssl_context
(
const
struct
mg_connection
*
);
// Send data to the client.
// Send data to the client.
...
...
test/embed.c
View file @
2260c8dd
...
@@ -119,9 +119,10 @@ static void test_get_request_info(struct mg_connection *conn,
...
@@ -119,9 +119,10 @@ static void test_get_request_info(struct mg_connection *conn,
static
void
test_error
(
struct
mg_connection
*
conn
,
static
void
test_error
(
struct
mg_connection
*
conn
,
const
struct
mg_request_info
*
ri
)
{
const
struct
mg_request_info
*
ri
)
{
(
void
)
ri
;
mg_printf
(
conn
,
"HTTP/1.1 %d XX
\r\n
"
mg_printf
(
conn
,
"HTTP/1.1 %d XX
\r\n
"
"Conntection: close
\r\n\r\n
"
,
ri
->
status_code
);
"Conntection: close
\r\n\r\n
"
,
mg_get_reply_status_code
(
conn
)
);
mg_printf
(
conn
,
"Error: [%d]"
,
ri
->
status_code
);
mg_printf
(
conn
,
"Error: [%d]"
,
mg_get_reply_status_code
(
conn
)
);
}
}
static
void
test_post
(
struct
mg_connection
*
conn
,
static
void
test_post
(
struct
mg_connection
*
conn
,
...
...
test/unit_test.c
View file @
2260c8dd
...
@@ -64,10 +64,10 @@ static void test_should_keep_alive(void) {
...
@@ -64,10 +64,10 @@ static void test_should_keep_alive(void) {
parse_http_request
(
req4
,
sizeof
(
req4
),
&
conn
.
request_info
);
parse_http_request
(
req4
,
sizeof
(
req4
),
&
conn
.
request_info
);
ASSERT
(
should_keep_alive
(
&
conn
)
==
1
);
ASSERT
(
should_keep_alive
(
&
conn
)
==
1
);
conn
.
request_info
.
status_code
=
401
;
conn
.
status_code
=
401
;
ASSERT
(
should_keep_alive
(
&
conn
)
==
0
);
ASSERT
(
should_keep_alive
(
&
conn
)
==
0
);
conn
.
request_info
.
status_code
=
200
;
conn
.
status_code
=
200
;
conn
.
must_close
=
1
;
conn
.
must_close
=
1
;
ASSERT
(
should_keep_alive
(
&
conn
)
==
0
);
ASSERT
(
should_keep_alive
(
&
conn
)
==
0
);
}
}
...
@@ -138,7 +138,7 @@ static void *event_handler(enum mg_event event,
...
@@ -138,7 +138,7 @@ static void *event_handler(enum mg_event event,
"%s"
,
(
int
)
strlen
(
fetch_data
),
fetch_data
);
"%s"
,
(
int
)
strlen
(
fetch_data
),
fetch_data
);
return
""
;
return
""
;
}
else
if
(
event
==
MG_EVENT_LOG
)
{
}
else
if
(
event
==
MG_EVENT_LOG
)
{
printf
(
"%s
\n
"
,
request_info
->
log_message
);
printf
(
"%s
\n
"
,
mg_get_log_message
(
conn
)
);
}
}
return
NULL
;
return
NULL
;
...
@@ -196,7 +196,21 @@ static void test_mg_fetch(void) {
...
@@ -196,7 +196,21 @@ static void test_mg_fetch(void) {
mg_stop
(
ctx
);
mg_stop
(
ctx
);
}
}
static
void
test_base64_encode
(
void
)
{
const
char
*
in
[]
=
{
"a"
,
"ab"
,
"abc"
,
"abcd"
,
NULL
};
const
char
*
out
[]
=
{
"YQ=="
,
"YWI="
,
"YWJj"
,
"YWJjZA=="
};
char
buf
[
100
];
int
i
;
for
(
i
=
0
;
in
[
i
]
!=
NULL
;
i
++
)
{
base64_encode
((
unsigned
char
*
)
in
[
i
],
strlen
(
in
[
i
]),
buf
);
printf
(
"[%s] [%s]
\n
"
,
out
[
i
],
buf
);
ASSERT
(
!
strcmp
(
buf
,
out
[
i
]));
}
}
int
main
(
void
)
{
int
main
(
void
)
{
test_base64_encode
();
test_match_prefix
();
test_match_prefix
();
test_remove_double_dots
();
test_remove_double_dots
();
test_should_keep_alive
();
test_should_keep_alive
();
...
...
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