Skip to content
Projects
Groups
Snippets
Help
Loading...
Sign in / Register
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
Packages
Packages
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
submodule
mongoose
Commits
ecbf7913
Commit
ecbf7913
authored
Sep 28, 2013
by
Sergey Lyubka
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Refactored API, returned back to event-based handlers. Upload and Websocket API simplified
parent
96eb4890
Hide whitespace changes
Inline
Side-by-side
Showing
9 changed files
with
410 additions
and
490 deletions
+410
-490
main.c
build/main.c
+5
-7
chat.c
examples/chat.c
+10
-9
hello.c
examples/hello.c
+27
-27
post.c
examples/post.c
+31
-29
upload.c
examples/upload.c
+32
-41
websocket.c
examples/websocket.c
+35
-21
mongoose.c
mongoose.c
+153
-170
mongoose.h
mongoose.h
+33
-42
unit_test.c
test/unit_test.c
+84
-144
No files found.
build/main.c
View file @
ecbf7913
...
...
@@ -271,9 +271,10 @@ static void init_server_name(void) {
mg_version
());
}
static
int
log_message
(
const
struct
mg_connection
*
conn
,
const
char
*
message
)
{
(
void
)
conn
;
printf
(
"%s
\n
"
,
message
);
static
int
event_handler
(
struct
mg_event
*
event
)
{
if
(
event
->
type
==
MG_EVENT_LOG
)
{
printf
(
"%s
\n
"
,
(
const
char
*
)
event
->
event_param
);
}
return
0
;
}
...
...
@@ -341,7 +342,6 @@ static void set_absolute_path(char *options[], const char *option_name,
}
static
void
start_mongoose
(
int
argc
,
char
*
argv
[])
{
struct
mg_callbacks
callbacks
;
char
*
options
[
MAX_OPTIONS
];
int
i
;
...
...
@@ -385,9 +385,7 @@ static void start_mongoose(int argc, char *argv[]) {
signal
(
SIGINT
,
signal_handler
);
// Start Mongoose
memset
(
&
callbacks
,
0
,
sizeof
(
callbacks
));
callbacks
.
log_message
=
&
log_message
;
ctx
=
mg_start
(
&
callbacks
,
NULL
,
(
const
char
**
)
options
);
ctx
=
mg_start
((
const
char
**
)
options
,
event_handler
,
NULL
);
for
(
i
=
0
;
options
[
i
]
!=
NULL
;
i
++
)
{
free
(
options
[
i
]);
}
...
...
examples/chat.c
View file @
ecbf7913
...
...
@@ -326,9 +326,12 @@ static void redirect_to_ssl(struct mg_connection *conn,
}
}
static
int
begin_request_handler
(
struct
mg_connection
*
conn
)
{
const
struct
mg_request_info
*
request_info
=
mg_get_request_info
(
conn
);
int
processed
=
1
;
static
int
event_handler
(
struct
mg_event
*
event
)
{
struct
mg_request_info
*
request_info
=
event
->
request_info
;
struct
mg_connection
*
conn
=
event
->
conn
;
int
result
=
1
;
if
(
event
->
type
!=
MG_REQUEST_BEGIN
)
return
0
;
if
(
!
request_info
->
is_ssl
)
{
redirect_to_ssl
(
conn
,
request_info
);
...
...
@@ -343,9 +346,10 @@ static int begin_request_handler(struct mg_connection *conn) {
}
else
{
// No suitable handler found, mark as not processed. Mongoose will
// try to serve the request.
processed
=
0
;
result
=
0
;
}
return
processed
;
return
result
;
}
static
const
char
*
options
[]
=
{
...
...
@@ -357,7 +361,6 @@ static const char *options[] = {
};
int
main
(
void
)
{
struct
mg_callbacks
callbacks
;
struct
mg_context
*
ctx
;
// Initialize random number generator. It will be used later on for
...
...
@@ -365,9 +368,7 @@ int main(void) {
srand
((
unsigned
)
time
(
0
));
// Setup and start Mongoose
memset
(
&
callbacks
,
0
,
sizeof
(
callbacks
));
callbacks
.
begin_request
=
begin_request_handler
;
if
((
ctx
=
mg_start
(
&
callbacks
,
NULL
,
options
))
==
NULL
)
{
if
((
ctx
=
mg_start
(
options
,
event_handler
,
NULL
))
==
NULL
)
{
printf
(
"%s
\n
"
,
"Cannot start chat server, fatal exit"
);
exit
(
EXIT_FAILURE
);
}
...
...
examples/hello.c
View file @
ecbf7913
...
...
@@ -3,42 +3,42 @@
#include "mongoose.h"
// This function will be called by mongoose on every new request.
static
int
begin_request_handler
(
struct
mg_connection
*
conn
)
{
const
struct
mg_request_info
*
request_info
=
mg_get_request_info
(
conn
);
char
content
[
100
];
// Prepare the message we're going to send
int
content_length
=
snprintf
(
content
,
sizeof
(
content
),
"Hello from mongoose! Remote port: %d"
,
request_info
->
remote_port
);
// Send HTTP reply to the client
mg_printf
(
conn
,
"HTTP/1.1 200 OK
\r\n
"
"Content-Type: text/plain
\r\n
"
"Content-Length: %d
\r\n
"
// Always set Content-Length
"
\r\n
"
"%s"
,
content_length
,
content
);
// Returning non-zero tells mongoose that our function has replied to
// the client, and mongoose should not send client any more data.
return
1
;
static
int
event_handler
(
struct
mg_event
*
event
)
{
if
(
event
->
type
==
MG_REQUEST_BEGIN
)
{
char
content
[
100
];
// Prepare the message we're going to send
int
content_length
=
snprintf
(
content
,
sizeof
(
content
),
"Hello from mongoose! Requested: [%s] [%s]"
,
event
->
request_info
->
request_method
,
event
->
request_info
->
uri
);
// Send HTTP reply to the client
mg_printf
(
event
->
conn
,
"HTTP/1.1 200 OK
\r\n
"
"Content-Type: text/plain
\r\n
"
"Content-Length: %d
\r\n
"
// Always set Content-Length
"
\r\n
"
"%s"
,
content_length
,
content
);
// Returning non-zero tells mongoose that our function has replied to
// the client, and mongoose should not send client any more data.
return
1
;
}
// We do not handle any other event
return
0
;
}
int
main
(
void
)
{
struct
mg_context
*
ctx
;
struct
mg_callbacks
callbacks
;
// List of options. Last element must be NULL.
const
char
*
options
[]
=
{
"listening_ports"
,
"8080"
,
NULL
};
// Prepare callbacks structure. We have only one callback, the rest are NULL.
memset
(
&
callbacks
,
0
,
sizeof
(
callbacks
));
callbacks
.
begin_request
=
begin_request_handler
;
// Start the web server.
ctx
=
mg_start
(
&
callbacks
,
NULL
,
options
);
ctx
=
mg_start
(
options
,
&
event_handler
,
NULL
);
// Wait until user hits "enter". Server is running in separate thread.
// Navigating to http://localhost:8080 will invoke begin_request_handler().
...
...
examples/post.c
View file @
ecbf7913
...
...
@@ -10,45 +10,47 @@ static const char *html_form =
"<input type=
\"
submit
\"
/>"
"</form></body></html>"
;
static
int
begin_request_handler
(
struct
mg_connection
*
conn
)
{
const
struct
mg_request_info
*
ri
=
mg_get_request_info
(
conn
);
static
int
event_handler
(
struct
mg_event
*
event
)
{
char
post_data
[
1024
],
input1
[
sizeof
(
post_data
)],
input2
[
sizeof
(
post_data
)];
int
post_data_len
;
if
(
!
strcmp
(
ri
->
uri
,
"/handle_post_request"
))
{
// User has submitted a form, show submitted data and a variable value
post_data_len
=
mg_read
(
conn
,
post_data
,
sizeof
(
post_data
));
// Parse form data. input1 and input2 are guaranteed to be NUL-terminated
mg_get_var
(
post_data
,
post_data_len
,
"input_1"
,
input1
,
sizeof
(
input1
));
mg_get_var
(
post_data
,
post_data_len
,
"input_2"
,
input2
,
sizeof
(
input2
));
// Send reply to the client, showing submitted form values.
mg_printf
(
conn
,
"HTTP/1.0 200 OK
\r\n
"
"Content-Type: text/plain
\r\n\r\n
"
"Submitted data: [%.*s]
\n
"
"Submitted data length: %d bytes
\n
"
"input_1: [%s]
\n
"
"input_2: [%s]
\n
"
,
post_data_len
,
post_data
,
post_data_len
,
input1
,
input2
);
}
else
{
// Show HTML form.
mg_printf
(
conn
,
"HTTP/1.0 200 OK
\r\n
"
"Content-Length: %d
\r\n
"
"Content-Type: text/html
\r\n\r\n
%s"
,
(
int
)
strlen
(
html_form
),
html_form
);
if
(
event
->
type
==
MG_REQUEST_BEGIN
)
{
if
(
!
strcmp
(
event
->
request_info
->
uri
,
"/handle_post_request"
))
{
// User has submitted a form, show submitted data and a variable value
post_data_len
=
mg_read
(
event
->
conn
,
post_data
,
sizeof
(
post_data
));
// Parse form data. input1 and input2 are guaranteed to be NUL-terminated
mg_get_var
(
post_data
,
post_data_len
,
"input_1"
,
input1
,
sizeof
(
input1
));
mg_get_var
(
post_data
,
post_data_len
,
"input_2"
,
input2
,
sizeof
(
input2
));
// Send reply to the client, showing submitted form values.
mg_printf
(
event
->
conn
,
"HTTP/1.0 200 OK
\r\n
"
"Content-Type: text/plain
\r\n\r\n
"
"Submitted data: [%.*s]
\n
"
"Submitted data length: %d bytes
\n
"
"input_1: [%s]
\n
"
"input_2: [%s]
\n
"
,
post_data_len
,
post_data
,
post_data_len
,
input1
,
input2
);
}
else
{
// Show HTML form.
mg_printf
(
event
->
conn
,
"HTTP/1.0 200 OK
\r\n
"
"Content-Length: %d
\r\n
"
"Content-Type: text/html
\r\n\r\n
%s"
,
(
int
)
strlen
(
html_form
),
html_form
);
}
return
1
;
// Mark event as processed
}
return
1
;
// Mark request as processed
// All other events are left not processed
return
0
;
}
int
main
(
void
)
{
struct
mg_context
*
ctx
;
const
char
*
options
[]
=
{
"listening_ports"
,
"8080"
,
NULL
};
struct
mg_callbacks
callbacks
;
memset
(
&
callbacks
,
0
,
sizeof
(
callbacks
));
callbacks
.
begin_request
=
begin_request_handler
;
ctx
=
mg_start
(
&
callbacks
,
NULL
,
options
);
ctx
=
mg_start
(
options
,
&
event_handler
,
NULL
);
getchar
();
// Wait until user hits "enter"
mg_stop
(
ctx
);
...
...
examples/upload.c
View file @
ecbf7913
...
...
@@ -3,57 +3,48 @@
#include <stdio.h>
#include <string.h>
#include <fcntl.h>
#include <stdlib.h>
#ifdef _WIN32
#include <windows.h>
#include <io.h>
#define strtoll strtol
typedef
__int64
int64_t
;
#else
#include <inttypes.h>
#include <unistd.h>
#endif // !_WIN32
#include "mongoose.h"
static
int
begin_request_handler
(
struct
mg_connection
*
conn
)
{
if
(
!
strcmp
(
mg_get_request_info
(
conn
)
->
uri
,
"/handle_post_request"
))
{
mg_printf
(
conn
,
"%s"
,
"HTTP/1.0 200 OK
\r\n\r\n
"
);
mg_upload
(
conn
,
"/tmp"
);
}
else
{
// Show HTML form. Make sure it has enctype="multipart/form-data" attr.
static
const
char
*
html_form
=
"<html><body>Upload example."
"<form method=
\"
POST
\"
action=
\"
/handle_post_request
\"
"
" enctype=
\"
multipart/form-data
\"
>"
"<input type=
\"
file
\"
name=
\"
file
\"
/> <br/>"
"<input type=
\"
submit
\"
value=
\"
Upload
\"
/>"
"</form></body></html>"
;
mg_printf
(
conn
,
"HTTP/1.0 200 OK
\r\n
"
"Content-Length: %d
\r\n
"
"Content-Type: text/html
\r\n\r\n
%s"
,
(
int
)
strlen
(
html_form
),
html_form
);
static
int
event_handler
(
struct
mg_event
*
event
)
{
if
(
event
->
type
==
MG_REQUEST_BEGIN
)
{
if
(
!
strcmp
(
event
->
request_info
->
uri
,
"/handle_post_request"
))
{
char
path
[
200
];
FILE
*
fp
=
mg_upload
(
event
->
conn
,
"/tmp"
,
path
,
sizeof
(
path
));
if
(
fp
!=
NULL
)
{
fclose
(
fp
);
mg_printf
(
event
->
conn
,
"HTTP/1.0 200 OK
\r\n\r\n
Saved: [%s]"
,
path
);
}
else
{
mg_printf
(
event
->
conn
,
"%s"
,
"HTTP/1.0 200 OK
\r\n\r\n
No files sent"
);
}
}
else
{
// Show HTML form. Make sure it has enctype="multipart/form-data" attr.
static
const
char
*
html_form
=
"<html><body>Upload example."
"<form method=
\"
POST
\"
action=
\"
/handle_post_request
\"
"
" enctype=
\"
multipart/form-data
\"
>"
"<input type=
\"
file
\"
name=
\"
file
\"
/> <br/>"
"<input type=
\"
submit
\"
value=
\"
Upload
\"
/>"
"</form></body></html>"
;
mg_printf
(
event
->
conn
,
"HTTP/1.0 200 OK
\r\n
"
"Content-Length: %d
\r\n
"
"Content-Type: text/html
\r\n\r\n
%s"
,
(
int
)
strlen
(
html_form
),
html_form
);
}
// Mark request as processed
return
1
;
}
//
Mark request as
processed
//
All other events left un
processed
return
1
;
}
static
void
upload_handler
(
struct
mg_connection
*
conn
,
const
char
*
path
)
{
mg_printf
(
conn
,
"Saved [%s]"
,
path
);
}
int
main
(
void
)
{
struct
mg_context
*
ctx
;
const
char
*
options
[]
=
{
"listening_ports"
,
"8080"
,
NULL
};
struct
mg_callbacks
callbacks
;
memset
(
&
callbacks
,
0
,
sizeof
(
callbacks
));
callbacks
.
begin_request
=
begin_request_handler
;
callbacks
.
upload
=
upload_handler
;
ctx
=
mg_start
(
&
callbacks
,
NULL
,
options
);
ctx
=
mg_start
(
options
,
event_handler
,
NULL
);
getchar
();
// Wait until user hits "enter"
mg_stop
(
ctx
);
...
...
examples/websocket.c
View file @
ecbf7913
...
...
@@ -5,38 +5,52 @@
#include <string.h>
#include "mongoose.h"
static
void
websocket_ready_handler
(
struct
mg_connection
*
conn
)
{
static
const
char
*
message
=
"server ready"
;
mg_websocket_write
(
conn
,
WEBSOCKET_OPCODE_TEXT
,
message
,
strlen
(
message
));
}
static
int
event_handler
(
struct
mg_event
*
event
)
{
if
(
event
->
type
==
MG_REQUEST_BEGIN
)
{
const
char
*
version_header
=
mg_get_header
(
event
->
conn
,
"Sec-WebSocket-Version"
);
if
(
version_header
!=
NULL
)
{
// Websocket request, process it
if
(
strcmp
(
version_header
,
"13"
)
!=
0
)
{
mg_printf
(
event
->
conn
,
"%s"
,
"HTTP/1.1 426 Upgrade Required
\r\n\r\n
"
);
}
else
{
static
const
char
*
server_ready_message
=
"server ready"
;
char
*
data
;
int
bits
,
len
;
// Arguments:
// flags: first byte of websocket frame, see websocket RFC,
// http://tools.ietf.org/html/rfc6455, section 5.2
// data, data_len: payload data. Mask, if any, is already applied.
static
int
websocket_data_handler
(
struct
mg_connection
*
conn
,
int
flags
,
char
*
data
,
size_t
data_len
)
{
(
void
)
flags
;
// Unused
mg_websocket_write
(
conn
,
WEBSOCKET_OPCODE_TEXT
,
data
,
data_len
);
// Returning zero means stoping websocket conversation.
// Close the conversation if client has sent us "exit" string.
return
memcmp
(
data
,
"exit"
,
4
);
// Handshake, and send initial server message
mg_websocket_handshake
(
event
->
conn
);
mg_websocket_write
(
event
->
conn
,
WEBSOCKET_OPCODE_TEXT
,
server_ready_message
,
strlen
(
server_ready_message
));
while
((
len
=
mg_websocket_read
(
event
->
conn
,
&
bits
,
&
data
))
>
0
)
{
// Echo message back to the client
mg_websocket_write
(
event
->
conn
,
WEBSOCKET_OPCODE_TEXT
,
data
,
len
);
if
(
memcmp
(
data
,
"exit"
,
4
)
==
0
)
{
mg_websocket_write
(
event
->
conn
,
WEBSOCKET_OPCODE_CONNECTION_CLOSE
,
""
,
0
);
break
;
}
}
}
return
1
;
}
}
return
0
;
}
int
main
(
void
)
{
struct
mg_context
*
ctx
;
struct
mg_callbacks
callbacks
;
const
char
*
options
[]
=
{
"listening_ports"
,
"8080"
,
"document_root"
,
"websocket_html_root"
,
NULL
};
memset
(
&
callbacks
,
0
,
sizeof
(
callbacks
));
callbacks
.
websocket_ready
=
websocket_ready_handler
;
callbacks
.
websocket_data
=
websocket_data_handler
;
ctx
=
mg_start
(
&
callbacks
,
NULL
,
options
);
ctx
=
mg_start
(
options
,
&
event_handler
,
NULL
);
getchar
();
// Wait until user hits "enter"
mg_stop
(
ctx
);
...
...
mongoose.c
View file @
ecbf7913
...
...
@@ -345,7 +345,7 @@ struct ssl_func {
#define SSL_CTX_use_certificate_file (* (int (*)(SSL_CTX *, \
const
char
*
,
int
))
ssl_sw
[
12
].
ptr
)
#define SSL_CTX_set_default_passwd_cb \
(
*
(
void
(
*
)(
SSL_CTX
*
,
mg_
callback
_t
))
ssl_sw
[
13
].
ptr
)
(
*
(
void
(
*
)(
SSL_CTX
*
,
mg_
event_handler
_t
))
ssl_sw
[
13
].
ptr
)
#define SSL_CTX_free (* (void (*)(SSL_CTX *)) ssl_sw[14].ptr)
#define SSL_load_error_strings (* (void (*)(void)) ssl_sw[15].ptr)
#define SSL_CTX_use_certificate_chain_file \
...
...
@@ -490,8 +490,7 @@ struct mg_context {
volatile
int
stop_flag
;
// Should we stop event loop
SSL_CTX
*
ssl_ctx
;
// SSL context
char
*
config
[
NUM_OPTIONS
];
// Mongoose configuration parameters
struct
mg_callbacks
callbacks
;
// User-defined callback function
mg_callback_t
user_callback
;
// User-defined callback function
mg_event_handler_t
event_handler
;
// User-defined callback function
void
*
user_data
;
// User-defined data
struct
socket
*
listening_sockets
;
...
...
@@ -510,6 +509,7 @@ struct mg_context {
struct
mg_connection
{
struct
mg_request_info
request_info
;
struct
mg_event
event
;
struct
mg_context
*
ctx
;
SSL
*
ssl
;
// SSL descriptor
SSL_CTX
*
client_ssl_ctx
;
// SSL context for client connections
...
...
@@ -517,7 +517,7 @@ struct mg_connection {
time_t
birth_time
;
// Time when request was received
int64_t
num_bytes_sent
;
// Total bytes sent to client
int64_t
content_len
;
// Content-Length header value
int64_t
consumed_content
;
// How many bytes of content have been read
int64_t
num_bytes_read
;
// Bytes read from a remote socket
char
*
buf
;
// Buffer for received data
char
*
path_info
;
// PATH_INFO part of the URL
int
must_close
;
// 1 if connection must be closed
...
...
@@ -537,16 +537,25 @@ struct de {
struct
file
file
;
};
// Return number of bytes left to read for this connection
static
int64_t
left_to_read
(
const
struct
mg_connection
*
conn
)
{
return
conn
->
content_len
+
conn
->
request_len
-
conn
->
num_bytes_read
;
}
const
char
**
mg_get_valid_option_names
(
void
)
{
return
config_options
;
}
static
int
call_user
(
enum
mg_event
ev
,
struct
mg_connection
*
conn
,
void
*
p
)
{
static
int
call_user
(
int
type
,
struct
mg_connection
*
conn
,
void
*
p
)
{
if
(
conn
!=
NULL
&&
conn
->
ctx
!=
NULL
)
{
conn
->
request_info
.
user_data
=
conn
->
ctx
->
user_data
;
conn
->
event
.
user_data
=
conn
->
ctx
->
user_data
;
conn
->
event
.
type
=
type
;
conn
->
event
.
event_param
=
p
;
conn
->
event
.
request_info
=
&
conn
->
request_info
;
conn
->
event
.
conn
=
conn
;
}
return
conn
==
NULL
||
conn
->
ctx
==
NULL
||
conn
->
ctx
->
user_callback
==
NULL
?
0
:
conn
->
ctx
->
user_callback
(
ev
,
conn
,
p
);
return
conn
==
NULL
||
conn
->
ctx
==
NULL
||
conn
->
ctx
->
event_handler
==
NULL
?
0
:
conn
->
ctx
->
event_handler
(
&
conn
->
event
);
}
static
FILE
*
mg_fopen
(
const
char
*
path
,
const
char
*
mode
)
{
...
...
@@ -614,8 +623,7 @@ static void cry(struct mg_connection *conn, const char *fmt, ...) {
// Do not lock when getting the callback value, here and below.
// I suppose this is fine, since function cannot disappear in the
// same way string option can.
if
(
conn
->
ctx
->
callbacks
.
log_message
==
NULL
||
conn
->
ctx
->
callbacks
.
log_message
(
conn
,
buf
)
==
0
)
{
if
(
call_user
(
MG_EVENT_LOG
,
conn
,
buf
)
==
0
)
{
fp
=
conn
->
ctx
==
NULL
||
conn
->
ctx
->
config
[
ERROR_LOG_FILE
]
==
NULL
?
NULL
:
fopen
(
conn
->
ctx
->
config
[
ERROR_LOG_FILE
],
"a+"
);
...
...
@@ -646,7 +654,7 @@ static struct mg_connection *fc(struct mg_context *ctx) {
static
struct
mg_connection
fake_connection
;
fake_connection
.
ctx
=
ctx
;
// See https://github.com/cesanta/mongoose/issues/236
fake_connection
.
request_info
.
user_data
=
ctx
->
user_data
;
fake_connection
.
event
.
user_data
=
ctx
->
user_data
;
return
&
fake_connection
;
}
...
...
@@ -1497,6 +1505,7 @@ static int64_t push(FILE *fp, SOCKET sock, SSL *ssl, const char *buf,
static
int
pull
(
FILE
*
fp
,
struct
mg_connection
*
conn
,
char
*
buf
,
int
len
)
{
int
nread
;
if
(
len
<=
0
)
return
0
;
if
(
fp
!=
NULL
)
{
// Use read() instead of fread(), because if we're reading from the CGI
// pipe, fread() may block until IO buffer is filled up. We cannot afford
...
...
@@ -1509,6 +1518,9 @@ static int pull(FILE *fp, struct mg_connection *conn, char *buf, int len) {
}
else
{
nread
=
recv
(
conn
->
client
.
sock
,
buf
,
(
size_t
)
len
,
0
);
}
if
(
nread
>
0
)
{
conn
->
num_bytes_read
+=
nread
;
}
return
conn
->
ctx
->
stop_flag
?
-
1
:
nread
;
}
...
...
@@ -1524,7 +1536,6 @@ static int pull_all(FILE *fp, struct mg_connection *conn, char *buf, int len) {
}
else
if
(
n
==
0
)
{
break
;
// No more data to read
}
else
{
conn
->
consumed_content
+=
n
;
nread
+=
n
;
len
-=
n
;
}
...
...
@@ -1533,46 +1544,48 @@ static int pull_all(FILE *fp, struct mg_connection *conn, char *buf, int len) {
return
nread
;
}
int
mg_read
(
struct
mg_connection
*
conn
,
void
*
buf
,
size_
t
len
)
{
int
n
,
buffered_len
,
nread
;
const
char
*
body
;
int
mg_read
(
struct
mg_connection
*
conn
,
void
*
buf
,
in
t
len
)
{
int
n
,
buffered_len
,
nread
=
0
;
int64_t
left
;
// If Content-Length is not set, read until socket is closed
if
(
conn
->
con
sumed_content
==
0
&&
conn
->
content_len
=
=
0
)
{
if
(
conn
->
con
tent_len
<
=
0
)
{
conn
->
content_len
=
INT64_MAX
;
conn
->
must_close
=
1
;
}
nread
=
0
;
if
(
conn
->
consumed_content
<
conn
->
content_len
)
{
// Adjust number of bytes to read.
int64_t
to_read
=
conn
->
content_len
-
conn
->
consumed_content
;
if
(
to_read
<
(
int64_t
)
len
)
{
len
=
(
size_t
)
to_read
;
}
// conn->buf body
// |=================|==========|===============|
// |<--request_len-->| |
// |<-----------data_len------->| conn->buf + conn->buf_size
// Return buffered data
body
=
conn
->
buf
+
conn
->
request_len
+
conn
->
consumed_content
;
buffered_len
=
&
conn
->
buf
[
conn
->
data_len
]
-
body
;
if
(
buffered_len
>
0
)
{
if
(
len
<
(
size_t
)
buffered_len
)
{
buffered_len
=
(
int
)
len
;
}
memcpy
(
buf
,
body
,
(
size_t
)
buffered_len
);
len
-=
buffered_len
;
conn
->
consumed_content
+=
buffered_len
;
nread
+=
buffered_len
;
buf
=
(
char
*
)
buf
+
buffered_len
;
}
// First, check for data buffered in conn->buf by read_request().
if
(
len
>
0
&&
(
buffered_len
=
conn
->
data_len
-
conn
->
request_len
)
>
0
)
{
char
*
body
=
conn
->
buf
+
conn
->
request_len
;
if
(
buffered_len
>
len
)
buffered_len
=
len
;
if
(
buffered_len
>
conn
->
content_len
)
buffered_len
=
conn
->
content_len
;
// We have returned all buffered data. Read new data from the remote socket.
n
=
pull_all
(
NULL
,
conn
,
(
char
*
)
buf
,
(
int
)
len
);
memcpy
(
buf
,
body
,
(
size_t
)
buffered_len
);
memmove
(
body
,
body
+
buffered_len
,
&
conn
->
buf
[
conn
->
data_len
]
-
&
body
[
buffered_len
]);
len
-=
buffered_len
;
conn
->
data_len
-=
buffered_len
;
nread
+=
buffered_len
;
}
// Read data from the socket.
if
(
len
>
0
&&
(
left
=
left_to_read
(
conn
))
>
0
)
{
if
(
left
<
len
)
{
len
=
(
int
)
left
;
}
n
=
pull_all
(
NULL
,
conn
,
(
char
*
)
buf
+
nread
,
(
int
)
len
);
nread
=
n
>=
0
?
nread
+
n
:
n
;
}
return
nread
;
}
int
mg_write
(
struct
mg_connection
*
conn
,
const
void
*
buf
,
size_
t
len
)
{
int
mg_write
(
struct
mg_connection
*
conn
,
const
void
*
buf
,
in
t
len
)
{
time_t
now
;
int64_t
n
,
total
,
allowed
;
...
...
@@ -1864,25 +1877,25 @@ static int convert_uri_to_file_name(struct mg_connection *conn, char *buf,
// -1 if request is malformed
// 0 if request is not yet fully buffered
// >0 actual request length, including last \r\n\r\n
static
int
get_request_len
(
const
char
*
buf
,
int
buflen
)
{
const
char
*
s
,
*
e
;
int
len
=
0
;
static
int
get_request_len
(
const
char
*
buf
,
int
buf_len
)
{
int
i
;
for
(
s
=
buf
,
e
=
s
+
buflen
-
1
;
len
<=
0
&&
s
<
e
;
s
++
)
for
(
i
=
0
;
i
<
buf_len
;
i
++
)
{
// Control characters are not allowed but >=128 is.
if
(
!
isprint
(
*
(
const
unsigned
char
*
)
s
)
&&
*
s
!=
'\r'
&&
*
s
!=
'\n'
&&
*
(
const
unsigned
char
*
)
s
<
128
)
{
len
=
-
1
;
break
;
// [i_a] abort scan as soon as one malformed character is found;
// don't let subsequent \r\n\r\n win us over anyhow
}
else
if
(
s
[
0
]
==
'\n'
&&
s
[
1
]
==
'\n'
)
{
len
=
(
int
)
(
s
-
buf
)
+
2
;
}
else
if
(
s
[
0
]
==
'\n'
&&
&
s
[
1
]
<
e
&&
s
[
1
]
==
'\r'
&&
s
[
2
]
==
'\n'
)
{
len
=
(
int
)
(
s
-
buf
)
+
3
;
// Abort scan as soon as one malformed character is found;
// don't let subsequent \r\n\r\n win us over anyhow
if
(
!
isprint
(
*
(
const
unsigned
char
*
)
&
buf
[
i
])
&&
buf
[
i
]
!=
'\r'
&&
buf
[
i
]
!=
'\n'
&&
*
(
const
unsigned
char
*
)
&
buf
[
i
]
<
128
)
{
return
-
1
;
}
else
if
(
buf
[
i
]
==
'\n'
&&
i
+
1
<
buf_len
&&
buf
[
i
+
1
]
==
'\n'
)
{
return
i
+
2
;
}
else
if
(
buf
[
i
]
==
'\n'
&&
i
+
2
<
buf_len
&&
buf
[
i
+
1
]
==
'\r'
&&
buf
[
i
+
2
]
==
'\n'
)
{
return
i
+
3
;
}
}
return
len
;
return
0
;
}
// Convert month to the month number. Return -1 on error, or month number
...
...
@@ -2840,7 +2853,7 @@ static void handle_directory_request(struct mg_connection *conn,
static
void
send_file_data
(
struct
mg_connection
*
conn
,
FILE
*
fp
,
int64_t
offset
,
int64_t
len
)
{
char
buf
[
MG_BUF_LEN
];
int
to_read
,
num_read
,
num_written
;
int
num_read
,
num_written
,
to_read
;
// If offset is beyond file boundaries, don't send anything
if
(
offset
>
0
&&
fseeko
(
fp
,
offset
,
SEEK_SET
)
!=
0
)
{
...
...
@@ -3054,7 +3067,8 @@ static int read_request(FILE *fp, struct mg_connection *conn,
request_len
=
get_request_len
(
buf
,
*
nread
);
while
(
conn
->
ctx
->
stop_flag
==
0
&&
*
nread
<
bufsiz
&&
request_len
==
0
&&
*
nread
<
bufsiz
&&
request_len
==
0
&&
(
n
=
pull
(
fp
,
conn
,
buf
+
*
nread
,
bufsiz
-
*
nread
))
>
0
)
{
*
nread
+=
n
;
assert
(
*
nread
<=
bufsiz
);
...
...
@@ -3126,7 +3140,8 @@ static int forward_body_data(struct mg_connection *conn, FILE *fp,
SOCKET
sock
,
SSL
*
ssl
)
{
const
char
*
expect
,
*
body
;
char
buf
[
MG_BUF_LEN
];
int
to_read
,
nread
,
buffered_len
,
success
=
0
;
int
nread
,
buffered_len
,
success
=
0
;
int64_t
left
;
expect
=
mg_get_header
(
conn
,
"Expect"
);
assert
(
fp
!=
NULL
);
...
...
@@ -3140,33 +3155,32 @@ static int forward_body_data(struct mg_connection *conn, FILE *fp,
(
void
)
mg_printf
(
conn
,
"%s"
,
"HTTP/1.1 100 Continue
\r\n\r\n
"
);
}
b
ody
=
conn
->
buf
+
conn
->
request_len
+
conn
->
consumed_content
;
b
uffered_len
=
&
conn
->
buf
[
conn
->
data_len
]
-
body
;
b
uffered_len
=
conn
->
data_len
-
conn
->
request_len
;
b
ody
=
conn
->
buf
+
conn
->
request_len
;
assert
(
buffered_len
>=
0
);
assert
(
conn
->
consumed_content
==
0
);
if
(
buffered_len
>
0
)
{
if
((
int64_t
)
buffered_len
>
conn
->
content_len
)
{
buffered_len
=
(
int
)
conn
->
content_len
;
}
push
(
fp
,
sock
,
ssl
,
body
,
(
int64_t
)
buffered_len
);
conn
->
consumed_content
+=
buffered_len
;
memmove
((
char
*
)
body
,
body
+
buffered_len
,
buffered_len
);
conn
->
data_len
-=
buffered_len
;
}
nread
=
0
;
while
(
conn
->
consumed_content
<
conn
->
conten
t_len
)
{
to_read
=
sizeof
(
buf
);
if
(
(
int64_t
)
to_read
>
conn
->
content_len
-
conn
->
consumed_content
)
{
to_read
=
(
int
)
(
conn
->
content_len
-
conn
->
consumed_content
);
while
(
conn
->
num_bytes_read
<
conn
->
content_len
+
conn
->
reques
t_len
)
{
left
=
left_to_read
(
conn
);
if
(
left
>
(
int64_t
)
sizeof
(
buf
)
)
{
left
=
sizeof
(
buf
);
}
nread
=
pull
(
NULL
,
conn
,
buf
,
to_read
);
nread
=
pull
(
NULL
,
conn
,
buf
,
left
);
if
(
nread
<=
0
||
push
(
fp
,
sock
,
ssl
,
buf
,
nread
)
!=
nread
)
{
break
;
}
conn
->
consumed_content
+=
nread
;
}
if
(
conn
->
consumed_content
==
conn
->
content_len
)
{
if
(
left_to_read
(
conn
)
==
0
)
{
success
=
nread
>=
0
;
}
...
...
@@ -3980,7 +3994,7 @@ static void base64_encode(const unsigned char *src, int src_len, char *dst) {
dst
[
j
++
]
=
'\0'
;
}
static
void
send
_websocket_handshake
(
struct
mg_connection
*
conn
)
{
void
mg
_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
;
...
...
@@ -3998,17 +4012,14 @@ static void send_websocket_handshake(struct mg_connection *conn) {
"Sec-WebSocket-Accept: "
,
b64_sha
,
"
\r\n\r\n
"
);
}
static
void
read_websocket
(
struct
mg_connection
*
conn
)
{
int
mg_websocket_read
(
struct
mg_connection
*
conn
,
int
*
bits
,
char
**
data
)
{
// Pointer to the beginning of the portion of the incoming websocket message
// queue. The original websocket upgrade request is never removed,
// so the queue begins after it.
unsigned
char
*
buf
=
(
unsigned
char
*
)
conn
->
buf
+
conn
->
request_len
;
int
bits
,
n
,
stop
=
0
;
int
n
,
stop
=
0
;
size_t
i
,
len
,
mask_len
,
data_len
,
header_len
,
body_len
;
// data points to the place where the message is stored when passed to the
// websocket_data callback. This is either mem on the stack,
// or a dynamically allocated buffer if it is too large.
char
mem
[
4
*
1024
],
mask
[
4
],
*
data
;
char
mask
[
4
];
assert
(
conn
->
content_len
==
0
);
...
...
@@ -4046,28 +4057,28 @@ static void read_websocket(struct mg_connection *conn) {
if
(
header_len
>
0
)
{
// Allocate space to hold websocket payload
data
=
mem
;
if
(
data_len
>
sizeof
(
mem
)
&&
(
data
=
malloc
(
data_len
))
==
NULL
)
{
if
((
*
data
=
malloc
(
data_len
))
==
NULL
)
{
// Allocation failed, exit the loop and then close the connection
// TODO: notify user about the failure
data_len
=
0
;
break
;
}
// Save mask and bits, otherwise it may be clobbered by memmove below
bits
=
buf
[
0
];
*
bits
=
buf
[
0
];
memcpy
(
mask
,
buf
+
header_len
-
mask_len
,
mask_len
);
// Read frame payload into the allocated buffer.
assert
(
body_len
>=
header_len
);
if
(
data_len
+
header_len
>
body_len
)
{
len
=
body_len
-
header_len
;
memcpy
(
data
,
buf
+
header_len
,
len
);
memcpy
(
*
data
,
buf
+
header_len
,
len
);
// TODO: handle pull error
pull_all
(
NULL
,
conn
,
data
+
len
,
data_len
-
len
);
pull_all
(
NULL
,
conn
,
*
data
+
len
,
data_len
-
len
);
conn
->
data_len
=
conn
->
request_len
;
}
else
{
len
=
data_len
+
header_len
;
memcpy
(
data
,
buf
+
header_len
,
data_len
);
memcpy
(
*
data
,
buf
+
header_len
,
data_len
);
memmove
(
buf
,
buf
+
len
,
body_len
-
len
);
conn
->
data_len
-=
len
;
}
...
...
@@ -4075,21 +4086,17 @@ static void read_websocket(struct mg_connection *conn) {
// Apply mask if necessary
if
(
mask_len
>
0
)
{
for
(
i
=
0
;
i
<
data_len
;
i
++
)
{
data
[
i
]
^=
mask
[
i
%
4
];
(
*
data
)
[
i
]
^=
mask
[
i
%
4
];
}
}
// Exit the loop if callback signalled to exit,
// or "connection close" opcode received.
if
(((
bits
&
0x0f
)
==
WEBSOCKET_OPCODE_CONNECTION_CLOSE
)
||
(
conn
->
ctx
->
callbacks
.
websocket_data
!=
NULL
&&
!
conn
->
ctx
->
callbacks
.
websocket_data
(
conn
,
bits
,
data
,
data_len
)))
{
if
((
*
bits
&
0x0f
)
==
WEBSOCKET_OPCODE_CONNECTION_CLOSE
)
{
return
data_len
;
stop
=
1
;
}
if
(
data
!=
mem
)
{
free
(
data
);
}
// Not breaking the loop, process next websocket frame.
}
else
{
// Buffering websocket request
...
...
@@ -4100,6 +4107,8 @@ static void read_websocket(struct mg_connection *conn) {
conn
->
data_len
+=
n
;
}
}
return
0
;
}
int
mg_websocket_write
(
struct
mg_connection
*
conn
,
int
opcode
,
...
...
@@ -4143,37 +4152,6 @@ int mg_websocket_write(struct mg_connection* conn, int opcode,
return
retval
;
}
static
void
handle_websocket_request
(
struct
mg_connection
*
conn
)
{
const
char
*
version
=
mg_get_header
(
conn
,
"Sec-WebSocket-Version"
);
if
(
version
==
NULL
||
strcmp
(
version
,
"13"
)
!=
0
)
{
send_http_error
(
conn
,
426
,
"Upgrade Required"
,
"%s"
,
"Upgrade Required"
);
}
else
if
(
conn
->
ctx
->
callbacks
.
websocket_connect
!=
NULL
&&
conn
->
ctx
->
callbacks
.
websocket_connect
(
conn
)
!=
0
)
{
// Callback has returned non-zero, do not proceed with handshake
}
else
{
send_websocket_handshake
(
conn
);
if
(
conn
->
ctx
->
callbacks
.
websocket_ready
!=
NULL
)
{
conn
->
ctx
->
callbacks
.
websocket_ready
(
conn
);
}
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_strcasestr
(
upgrade
,
"websocket"
)
!=
NULL
&&
mg_strcasestr
(
connection
,
"Upgrade"
)
!=
NULL
;
}
#endif // !USE_WEBSOCKET
static
int
isbyte
(
int
n
)
{
...
...
@@ -4231,12 +4209,12 @@ static uint32_t get_remote_ip(const struct mg_connection *conn) {
#include "build/mod_lua.c"
#endif // USE_LUA
int
mg_upload
(
struct
mg_connection
*
conn
,
const
char
*
destination_dir
)
{
FILE
*
mg_upload
(
struct
mg_connection
*
conn
,
const
char
*
destination_dir
,
char
*
path
,
int
path_len
)
{
const
char
*
content_type_header
,
*
boundary_start
;
char
buf
[
MG_BUF_LEN
],
path
[
PATH_MAX
],
fname
[
1024
],
boundary
[
100
],
*
s
;
char
*
buf
,
fname
[
1024
],
boundary
[
100
],
*
s
;
int
bl
,
n
,
i
,
j
,
headers_len
,
boundary_len
,
eof
,
buf_len
,
to_read
,
len
=
0
;
FILE
*
fp
;
int
bl
,
n
,
i
,
j
,
headers_len
,
boundary_len
,
eof
,
len
=
0
,
num_uploaded_files
=
0
;
// Request looks like this:
//
...
...
@@ -4260,15 +4238,31 @@ int mg_upload(struct mg_connection *conn, const char *destination_dir) {
(
sscanf
(
boundary_start
,
"boundary=
\"
%99[^
\"
]
\"
"
,
boundary
)
==
0
&&
sscanf
(
boundary_start
,
"boundary=%99s"
,
boundary
)
==
0
)
||
boundary
[
0
]
==
'\0'
)
{
return
num_uploaded_files
;
return
NULL
;
}
boundary_len
=
strlen
(
boundary
);
bl
=
boundary_len
+
4
;
// \r\n--<boundary>
// buf
// conn->buf |<--------- buf_len ------>|
// |=================|==========|===============|
// |<--request_len-->|<--len--->| |
// |<-----------data_len------->| conn->buf + conn->buf_size
buf
=
conn
->
buf
+
conn
->
request_len
;
buf_len
=
conn
->
buf_size
-
conn
->
request_len
;
len
=
conn
->
data_len
-
conn
->
request_len
;
for
(;;)
{
// Pull in headers
assert
(
len
>=
0
&&
len
<=
(
int
)
sizeof
(
buf
));
while
((
n
=
mg_read
(
conn
,
buf
+
len
,
sizeof
(
buf
)
-
len
))
>
0
)
{
assert
(
len
>=
0
&&
len
<=
buf_len
);
to_read
=
buf_len
-
len
;
if
(
to_read
>
left_to_read
(
conn
))
{
to_read
=
left_to_read
(
conn
);
}
while
(
len
<
buf_len
&&
(
n
=
pull
(
NULL
,
conn
,
buf
+
len
,
to_read
))
>
0
)
{
len
+=
n
;
}
if
((
headers_len
=
get_request_len
(
buf
,
len
))
<=
0
)
{
...
...
@@ -4297,10 +4291,12 @@ int mg_upload(struct mg_connection *conn, const char *destination_dir) {
assert
(
len
>=
headers_len
);
memmove
(
buf
,
&
buf
[
headers_len
],
len
-
headers_len
);
len
-=
headers_len
;
conn
->
data_len
=
conn
->
request_len
+
len
;
// We open the file with exclusive lock held. This guarantee us
// there is no other thread can save into the same file simultaneously.
fp
=
NULL
;
// Construct destination file name. Do not allow paths to have slashes.
if
((
s
=
strrchr
(
fname
,
'/'
))
==
NULL
&&
(
s
=
strrchr
(
fname
,
'\\'
))
==
NULL
)
{
...
...
@@ -4308,7 +4304,7 @@ int mg_upload(struct mg_connection *conn, const char *destination_dir) {
}
// Open file in binary mode. TODO: set an exclusive lock.
snprintf
(
path
,
sizeof
(
path
)
,
"%s/%s"
,
destination_dir
,
s
);
snprintf
(
path
,
path_len
,
"%s/%s"
,
destination_dir
,
s
);
if
((
fp
=
fopen
(
path
,
"wb"
))
==
NULL
)
{
break
;
}
...
...
@@ -4333,17 +4329,22 @@ int mg_upload(struct mg_connection *conn, const char *destination_dir) {
memmove
(
buf
,
&
buf
[
len
-
bl
],
bl
);
len
=
bl
;
}
}
while
(
!
eof
&&
(
n
=
mg_read
(
conn
,
buf
+
len
,
sizeof
(
buf
)
-
len
))
>
0
);
fclose
(
fp
);
if
(
eof
)
{
num_uploaded_files
++
;
if
(
conn
->
ctx
->
callbacks
.
upload
!=
NULL
)
{
conn
->
ctx
->
callbacks
.
upload
(
conn
,
path
);
to_read
=
buf_len
-
len
;
if
(
to_read
>
left_to_read
(
conn
))
{
to_read
=
left_to_read
(
conn
);
}
}
while
(
!
eof
&&
(
n
=
pull
(
NULL
,
conn
,
buf
+
len
,
to_read
))
>
0
);
conn
->
data_len
=
conn
->
request_len
+
len
;
if
(
eof
)
{
rewind
(
fp
);
return
fp
;
}
else
{
fclose
(
fp
);
}
}
return
num_uploaded_files
;
return
NULL
;
}
static
int
is_put_or_delete_request
(
const
struct
mg_connection
*
conn
)
{
...
...
@@ -4417,7 +4418,6 @@ static void handle_request(struct mg_connection *conn) {
path
[
0
]
=
'\0'
;
convert_uri_to_file_name
(
conn
,
path
,
sizeof
(
path
),
&
file
);
DEBUG_TRACE
((
"%s"
,
ri
->
uri
));
// Perform redirect and auth checks before calling begin_request() handler.
// Otherwise, begin_request() would need to perform auth checks and redirects.
if
(
!
conn
->
client
.
is_ssl
&&
conn
->
client
.
ssl_redir
&&
...
...
@@ -4426,13 +4426,8 @@ static void handle_request(struct mg_connection *conn) {
}
else
if
(
!
is_put_or_delete_request
(
conn
)
&&
!
check_authorization
(
conn
,
path
))
{
send_authorization_request
(
conn
);
}
else
if
(
conn
->
ctx
->
callbacks
.
begin_request
!=
NULL
&&
conn
->
ctx
->
callbacks
.
begin_request
(
conn
))
{
}
else
if
(
call_user
(
MG_REQUEST_BEGIN
,
conn
,
(
void
*
)
ri
->
uri
)
==
1
)
{
// Do nothing, callback has served the request
#if defined(USE_WEBSOCKET)
}
else
if
(
is_websocket_request
(
conn
))
{
handle_websocket_request
(
conn
);
#endif
}
else
if
(
!
strcmp
(
ri
->
request_method
,
"OPTIONS"
))
{
handle_options_request
(
conn
);
}
else
if
(
conn
->
ctx
->
config
[
DOCUMENT_ROOT
]
==
NULL
)
{
...
...
@@ -4767,8 +4762,9 @@ static int set_ssl_option(struct mg_context *ctx) {
// If PEM file is not specified and the init_ssl callback
// is not specified, skip SSL initialization.
if
((
pem
=
ctx
->
config
[
SSL_CERTIFICATE
])
==
NULL
&&
ctx
->
callbacks
.
init_ssl
==
NULL
)
{
if
((
pem
=
ctx
->
config
[
SSL_CERTIFICATE
])
==
NULL
)
{
// MG_INIT_SSL
// ctx->callbacks.init_ssl == NULL) {
return
1
;
}
...
...
@@ -4790,10 +4786,9 @@ static int set_ssl_option(struct mg_context *ctx) {
// If user callback returned non-NULL, that means that user callback has
// set up certificate itself. In this case, skip sertificate setting.
if
((
ctx
->
callbacks
.
init_ssl
==
NULL
||
!
ctx
->
callbacks
.
init_ssl
(
ctx
->
ssl_ctx
,
ctx
->
user_data
))
&&
(
SSL_CTX_use_certificate_file
(
ctx
->
ssl_ctx
,
pem
,
1
)
==
0
||
SSL_CTX_use_PrivateKey_file
(
ctx
->
ssl_ctx
,
pem
,
1
)
==
0
))
{
// MG_INIT_SSL
if
(
SSL_CTX_use_certificate_file
(
ctx
->
ssl_ctx
,
pem
,
1
)
==
0
||
SSL_CTX_use_PrivateKey_file
(
ctx
->
ssl_ctx
,
pem
,
1
)
==
0
)
{
cry
(
fc
(
ctx
),
"%s: cannot open %s: %s"
,
__func__
,
pem
,
ssl_error
());
return
0
;
}
...
...
@@ -4849,7 +4844,7 @@ static int set_acl_option(struct mg_context *ctx) {
static
void
reset_per_request_attributes
(
struct
mg_connection
*
conn
)
{
conn
->
path_info
=
NULL
;
conn
->
num_bytes_sent
=
conn
->
consumed_content
=
0
;
conn
->
num_bytes_sent
=
conn
->
num_bytes_read
=
0
;
conn
->
status_code
=
-
1
;
conn
->
must_close
=
conn
->
request_len
=
conn
->
throttle
=
0
;
}
...
...
@@ -5039,9 +5034,7 @@ static void process_new_connection(struct mg_connection *conn) {
if
(
ebuf
[
0
]
==
'\0'
)
{
handle_request
(
conn
);
if
(
conn
->
ctx
->
callbacks
.
end_request
!=
NULL
)
{
conn
->
ctx
->
callbacks
.
end_request
(
conn
,
conn
->
status_code
);
}
call_user
(
MG_REQUEST_END
,
conn
,
(
void
*
)
conn
->
status_code
);
log_access
(
conn
);
}
if
(
ri
->
remote_user
!=
NULL
)
{
...
...
@@ -5111,12 +5104,9 @@ static void *worker_thread(void *thread_func_param) {
conn
->
buf_size
=
MAX_REQUEST_SIZE
;
conn
->
buf
=
(
char
*
)
(
conn
+
1
);
conn
->
ctx
=
ctx
;
conn
->
request_info
.
user_data
=
ctx
->
user_data
;
conn
->
event
.
user_data
=
ctx
->
user_data
;
if
(
ctx
->
callbacks
.
thread_start
!=
NULL
)
{
ctx
->
callbacks
.
thread_start
(
&
conn
->
request_info
.
user_data
,
&
conn
->
request_info
.
conn_data
);
}
call_user
(
MG_THREAD_BEGIN
,
conn
,
NULL
);
// Call consume_socket() even when ctx->stop_flag > 0, to let it signal
// sq_empty condvar to wake up the master waiting in produce_socket()
...
...
@@ -5143,10 +5133,7 @@ static void *worker_thread(void *thread_func_param) {
close_connection
(
conn
);
}
if
(
ctx
->
callbacks
.
thread_stop
!=
NULL
)
{
ctx
->
callbacks
.
thread_stop
(
&
conn
->
request_info
.
user_data
,
&
conn
->
request_info
.
conn_data
);
}
call_user
(
MG_THREAD_END
,
conn
,
NULL
);
free
(
conn
);
}
...
...
@@ -5241,9 +5228,7 @@ static void *master_thread(void *thread_func_param) {
pthread_setschedparam
(
pthread_self
(),
SCHED_RR
,
&
sched_param
);
#endif
if
(
ctx
->
callbacks
.
thread_start
!=
NULL
)
{
ctx
->
callbacks
.
thread_start
(
&
ctx
->
user_data
,
NULL
);
}
call_user
(
MG_THREAD_BEGIN
,
fc
(
ctx
),
NULL
);
pfd
=
(
struct
pollfd
*
)
calloc
(
ctx
->
num_listening_sockets
,
sizeof
(
pfd
[
0
]));
while
(
pfd
!=
NULL
&&
ctx
->
stop_flag
==
0
)
{
...
...
@@ -5291,9 +5276,7 @@ static void *master_thread(void *thread_func_param) {
#endif
DEBUG_TRACE
((
"exiting"
));
if
(
ctx
->
callbacks
.
thread_stop
!=
NULL
)
{
ctx
->
callbacks
.
thread_stop
(
&
ctx
->
user_data
,
NULL
);
}
call_user
(
MG_THREAD_END
,
fc
(
ctx
),
NULL
);
// Signal mg_stop() that we're done.
// WARNING: This must be the very last thing this
...
...
@@ -5340,9 +5323,9 @@ void mg_stop(struct mg_context *ctx) {
#endif // _WIN32
}
struct
mg_context
*
mg_start
(
const
struct
mg_callbacks
*
callback
s
,
void
*
user_data
,
const
char
**
options
)
{
struct
mg_context
*
mg_start
(
const
char
**
option
s
,
mg_event_handler_t
func
,
void
*
user_data
)
{
struct
mg_context
*
ctx
;
const
char
*
name
,
*
value
,
*
default_value
;
int
i
;
...
...
@@ -5357,7 +5340,7 @@ struct mg_context *mg_start(const struct mg_callbacks *callbacks,
if
((
ctx
=
(
struct
mg_context
*
)
calloc
(
1
,
sizeof
(
*
ctx
)))
==
NULL
)
{
return
NULL
;
}
ctx
->
callbacks
=
*
callbacks
;
ctx
->
event_handler
=
func
;
ctx
->
user_data
=
user_data
;
while
(
options
&&
(
name
=
*
options
++
)
!=
NULL
)
{
...
...
mongoose.h
View file @
ecbf7913
...
...
@@ -25,8 +25,8 @@
extern
"C"
{
#endif // __cplusplus
struct
mg_context
;
//
Handle for the HTTP service itself
struct
mg_connection
;
// H
andle for the individual connection
struct
mg_context
;
//
Web server instance
struct
mg_connection
;
// H
TTP request descriptor
// This structure contains information about the HTTP request.
...
...
@@ -39,8 +39,6 @@ struct mg_request_info {
long
remote_ip
;
// Client's IP address
int
remote_port
;
// Client's port
int
is_ssl
;
// 1 if SSL-ed, 0 if not
void
*
user_data
;
// User data pointer passed to mg_start()
void
*
conn_data
;
// Connection-specific, per-thread user data.
int
num_headers
;
// Number of HTTP headers
struct
mg_header
{
...
...
@@ -49,37 +47,32 @@ struct mg_request_info {
}
http_headers
[
64
];
// Maximum 64 headers
};
enum
mg_event
{
MG_REQUEST_BEGIN
,
MG_REQUEST_END
,
MG_HTTP_ERROR
,
MG_EVENT_LOG
,
MG_THREAD_BEGIN
,
MG_THREAD_END
};
typedef
int
(
*
mg_callback_t
)(
enum
mg_event
event
,
struct
mg_connection
*
conn
,
void
*
data
);
struct
mg_callbacks
{
int
(
*
begin_request
)(
struct
mg_connection
*
);
void
(
*
end_request
)(
const
struct
mg_connection
*
,
int
reply_status_code
);
int
(
*
log_message
)(
const
struct
mg_connection
*
,
const
char
*
message
);
int
(
*
init_ssl
)(
void
*
ssl_context
,
void
*
user_data
);
int
(
*
websocket_connect
)(
const
struct
mg_connection
*
);
void
(
*
websocket_ready
)(
struct
mg_connection
*
);
int
(
*
websocket_data
)(
struct
mg_connection
*
,
int
bits
,
char
*
data
,
size_t
data_len
);
void
(
*
upload
)(
struct
mg_connection
*
,
const
char
*
file_name
);
void
(
*
thread_start
)(
void
*
user_data
,
void
**
conn_data
);
void
(
*
thread_stop
)(
void
*
user_data
,
void
**
conn_data
);
struct
mg_event
{
int
type
;
// Event type, possible types are defined below
#define MG_REQUEST_BEGIN 1 // event_param: NULL
#define MG_REQUEST_END 2 // event_param: NULL
#define MG_HTTP_ERROR 3 // event_param: int status_code
#define MG_EVENT_LOG 4 // event_param: const char *message
#define MG_THREAD_BEGIN 5 // event_param: NULL
#define MG_THREAD_END 6 // event_param: NULL
void
*
user_data
;
// User data pointer passed to mg_start()
void
*
conn_data
;
// Connection-specific, per-thread user data.
void
*
event_param
;
// Event-specific parameter
struct
mg_connection
*
conn
;
struct
mg_request_info
*
request_info
;
};
struct
mg_context
*
mg_start
(
const
struct
mg_callbacks
*
callbacks
,
void
*
user_data
,
const
char
**
configuration_options
);
typedef
int
(
*
mg_event_handler_t
)(
struct
mg_event
*
event
);
struct
mg_context
*
mg_start
(
const
char
**
configuration_options
,
mg_event_handler_t
func
,
void
*
user_data
);
void
mg_stop
(
struct
mg_context
*
);
void
mg_websocket_handshake
(
struct
mg_connection
*
);
int
mg_websocket_read
(
struct
mg_connection
*
,
int
*
bits
,
char
**
data
);
// Get the value of particular configuration parameter.
// The value returned is read-only. Mongoose does not allow changing
...
...
@@ -114,17 +107,12 @@ int mg_modify_passwords_file(const char *passwords_file_name,
const
char
*
user
,
const
char
*
password
);
// Return information associated with the request.
struct
mg_request_info
*
mg_get_request_info
(
struct
mg_connection
*
);
// Send data to the client.
// Return:
// 0 when the connection has been closed
// -1 on error
// >0 number of bytes written on success
int
mg_write
(
struct
mg_connection
*
,
const
void
*
buf
,
size_
t
len
);
int
mg_write
(
struct
mg_connection
*
,
const
void
*
buf
,
in
t
len
);
// Send data to a websocket client wrapped in a websocket frame.
...
...
@@ -184,7 +172,7 @@ void mg_send_file(struct mg_connection *conn, const char *path);
// 0 connection has been closed by peer. No more data could be read.
// < 0 read error. No more data could be read from the connection.
// > 0 number of bytes read into the buffer.
int
mg_read
(
struct
mg_connection
*
,
void
*
buf
,
size_
t
len
);
int
mg_read
(
struct
mg_connection
*
,
void
*
buf
,
in
t
len
);
// Get the value of particular HTTP header.
...
...
@@ -258,10 +246,13 @@ struct mg_connection *mg_download(const char *host, int port, int use_ssl,
void
mg_close_connection
(
struct
mg_connection
*
conn
);
// File upload functionality. Each uploaded file gets saved into a temporary
// file and MG_UPLOAD event is sent.
// Return number of uploaded files.
int
mg_upload
(
struct
mg_connection
*
conn
,
const
char
*
destination_dir
);
// Read multipart-form-data POST buffer, save uploaded files into
// destination directory, and return path to the saved filed.
// This function can be called multiple times for the same connection,
// if more then one file is uploaded.
// Return: path to the uploaded file, or NULL if there are no more files.
FILE
*
mg_upload
(
struct
mg_connection
*
conn
,
const
char
*
destination_dir
,
char
*
path
,
int
path_len
);
// Convenience function -- create detached thread.
...
...
test/unit_test.c
View file @
ecbf7913
// Copyright (c) 2004-2013 Sergey Lyubka
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
// Unit test for the mongoose web server. Tests embedded API.
// Unit test for the mongoose web server.
#define USE_WEBSOCKET
#define USE_LUA
...
...
@@ -65,6 +45,14 @@ static void test_parse_http_message() {
char
req8
[]
=
" HTTP/1.1 200 OK
\n\n
"
;
char
req9
[]
=
"HTTP/1.1 200 OK
\r\n
Connection: close
\r\n\r\n
"
;
ASSERT
(
get_request_len
(
"
\r\n
"
,
3
)
==
-
1
);
ASSERT
(
get_request_len
(
"
\r\n
"
,
2
)
==
0
);
ASSERT
(
get_request_len
(
"GET"
,
3
)
==
0
);
ASSERT
(
get_request_len
(
"
\n\n
"
,
2
)
==
2
);
ASSERT
(
get_request_len
(
"
\n\r\n
"
,
3
)
==
3
);
ASSERT
(
get_request_len
(
"
\xdd\xdd
"
,
2
)
==
0
);
ASSERT
(
get_request_len
(
"
\xdd\x03
"
,
2
)
==
-
1
);
ASSERT
(
parse_http_message
(
req9
,
sizeof
(
req9
),
&
ri
)
==
sizeof
(
req9
)
-
1
);
ASSERT
(
ri
.
num_headers
==
1
);
...
...
@@ -72,15 +60,15 @@ static void test_parse_http_message() {
ASSERT
(
strcmp
(
ri
.
http_version
,
"1.1"
)
==
0
);
ASSERT
(
ri
.
num_headers
==
0
);
ASSERT
(
parse_http_message
(
req2
,
sizeof
(
req2
),
&
ri
)
==
-
1
);
ASSERT
(
parse_http_message
(
req3
,
sizeof
(
req3
),
&
ri
)
==
0
);
ASSERT
(
parse_http_message
(
req6
,
sizeof
(
req6
),
&
ri
)
==
0
);
ASSERT
(
parse_http_message
(
req7
,
sizeof
(
req7
),
&
ri
)
==
0
);
ASSERT
(
parse_http_message
(
req2
,
sizeof
(
req2
)
-
1
,
&
ri
)
==
-
1
);
ASSERT
(
parse_http_message
(
req3
,
sizeof
(
req3
)
-
1
,
&
ri
)
==
0
);
ASSERT
(
parse_http_message
(
req6
,
sizeof
(
req6
)
-
1
,
&
ri
)
==
0
);
ASSERT
(
parse_http_message
(
req7
,
sizeof
(
req7
)
-
1
,
&
ri
)
==
0
);
ASSERT
(
parse_http_message
(
""
,
0
,
&
ri
)
==
0
);
ASSERT
(
parse_http_message
(
req8
,
sizeof
(
req8
),
&
ri
)
==
sizeof
(
req8
)
-
1
);
ASSERT
(
parse_http_message
(
req8
,
sizeof
(
req8
)
-
1
,
&
ri
)
==
sizeof
(
req8
)
-
1
);
// TODO(lsm): Fix this. Header value may span multiple lines.
ASSERT
(
parse_http_message
(
req4
,
sizeof
(
req4
),
&
ri
)
==
sizeof
(
req4
)
-
1
);
ASSERT
(
parse_http_message
(
req4
,
sizeof
(
req4
)
-
1
,
&
ri
)
==
sizeof
(
req4
)
-
1
);
ASSERT
(
strcmp
(
ri
.
http_version
,
"1.1"
)
==
0
);
ASSERT
(
ri
.
num_headers
==
3
);
ASSERT
(
strcmp
(
ri
.
http_headers
[
0
].
name
,
"A"
)
==
0
);
...
...
@@ -90,7 +78,7 @@ static void test_parse_http_message() {
ASSERT
(
strcmp
(
ri
.
http_headers
[
2
].
name
,
"baz
\r\n\r
"
)
==
0
);
ASSERT
(
strcmp
(
ri
.
http_headers
[
2
].
value
,
""
)
==
0
);
ASSERT
(
parse_http_message
(
req5
,
sizeof
(
req5
),
&
ri
)
==
sizeof
(
req5
)
-
1
);
ASSERT
(
parse_http_message
(
req5
,
sizeof
(
req5
)
-
1
,
&
ri
)
==
sizeof
(
req5
)
-
1
);
ASSERT
(
strcmp
(
ri
.
request_method
,
"GET"
)
==
0
);
ASSERT
(
strcmp
(
ri
.
http_version
,
"1.1"
)
==
0
);
}
...
...
@@ -201,79 +189,55 @@ static char *read_file(const char *path, int *size) {
}
static
const
char
*
fetch_data
=
"hello world!
\n
"
;
static
const
char
*
upload_filename
=
"upload_test.txt"
;
static
const
char
*
upload_filename2
=
"upload_test2.txt"
;
static
const
char
*
upload_ok_message
=
"upload successful"
;
static
void
upload_cb
(
struct
mg_connection
*
conn
,
const
char
*
path
)
{
const
struct
mg_request_info
*
ri
=
mg_get_request_info
(
conn
);
char
*
p1
,
*
p2
;
static
void
test_upload
(
struct
mg_connection
*
conn
,
const
char
*
orig_path
,
const
char
*
uploaded_path
)
{
int
len1
,
len2
;
char
path
[
500
],
*
p1
,
*
p2
;
FILE
*
fp
;
if
(
atoi
(
ri
->
query_string
)
==
1
)
{
ASSERT
(
!
strcmp
(
path
,
"./upload_test.txt"
));
ASSERT
((
p1
=
read_file
(
"main.c"
,
&
len1
))
!=
NULL
);
ASSERT
((
p2
=
read_file
(
path
,
&
len2
))
!=
NULL
);
ASSERT
(
len1
==
len2
);
ASSERT
(
memcmp
(
p1
,
p2
,
len1
)
==
0
);
free
(
p1
),
free
(
p2
);
remove
(
upload_filename
);
}
else
if
(
atoi
(
ri
->
query_string
)
==
2
)
{
if
(
!
strcmp
(
path
,
"./upload_test.txt"
))
{
ASSERT
((
p1
=
read_file
(
"lua_5.2.1.h"
,
&
len1
))
!=
NULL
);
ASSERT
((
p2
=
read_file
(
path
,
&
len2
))
!=
NULL
);
ASSERT
(
len1
==
len2
);
ASSERT
(
memcmp
(
p1
,
p2
,
len1
)
==
0
);
free
(
p1
),
free
(
p2
);
remove
(
upload_filename
);
}
else
if
(
!
strcmp
(
path
,
"./upload_test2.txt"
))
{
ASSERT
((
p1
=
read_file
(
"mod_lua.c"
,
&
len1
))
!=
NULL
);
ASSERT
((
p2
=
read_file
(
path
,
&
len2
))
!=
NULL
);
ASSERT
(
len1
==
len2
);
ASSERT
(
memcmp
(
p1
,
p2
,
len1
)
==
0
);
free
(
p1
),
free
(
p2
);
remove
(
upload_filename
);
}
else
{
ASSERT
(
0
);
}
}
else
{
ASSERT
(
0
);
}
mg_printf
(
conn
,
"HTTP/1.0 200 OK
\r\n
Content-Length: %d
\r\n\r\n
%s"
,
(
int
)
strlen
(
upload_ok_message
),
upload_ok_message
);
ASSERT
((
fp
=
mg_upload
(
conn
,
"."
,
path
,
sizeof
(
path
)))
!=
NULL
);
fclose
(
fp
);
ASSERT
(
!
strcmp
(
path
,
uploaded_path
));
ASSERT
((
p1
=
read_file
(
orig_path
,
&
len1
))
!=
NULL
);
ASSERT
((
p2
=
read_file
(
path
,
&
len2
))
!=
NULL
);
ASSERT
(
len1
==
len2
);
ASSERT
(
memcmp
(
p1
,
p2
,
len1
)
==
0
);
free
(
p1
),
free
(
p2
);
remove
(
path
);
}
static
int
begin_request_handler_cb
(
struct
mg_connection
*
conn
)
{
const
struct
mg_request_info
*
ri
=
mg_get_request_info
(
conn
)
;
static
int
event_handler
(
struct
mg_event
*
event
)
{
struct
mg_request_info
*
ri
=
event
->
request_info
;
if
(
!
strcmp
(
ri
->
uri
,
"/data"
))
{
mg_printf
(
conn
,
"HTTP/1.1 200 OK
\r\n
"
"Content-Type: text/plain
\r\n\r\n
"
"%s"
,
fetch_data
);
close_connection
(
conn
);
return
1
;
}
if
(
event
->
type
==
MG_REQUEST_BEGIN
)
{
if
(
!
strcmp
(
ri
->
uri
,
"/data"
))
{
mg_printf
(
event
->
conn
,
"HTTP/1.1 200 OK
\r\n
"
"Content-Type: text/plain
\r\n\r\n
"
"%s"
,
fetch_data
);
close_connection
(
event
->
conn
);
return
1
;
}
if
(
!
strcmp
(
ri
->
uri
,
"/upload"
))
{
ASSERT
(
ri
->
query_string
!=
NULL
);
ASSERT
(
mg_upload
(
conn
,
"."
)
==
atoi
(
ri
->
query_string
)
);
}
if
(
!
strcmp
(
ri
->
uri
,
"/upload"
))
{
test_upload
(
event
->
conn
,
"lua_5.2.1.h"
,
"./f1.txt"
);
test_upload
(
event
->
conn
,
"mod_lua.c"
,
"./f2.txt"
);
ASSERT
(
mg_upload
(
event
->
conn
,
"."
,
NULL
,
0
)
==
NULL
);
return
0
;
}
mg_printf
(
event
->
conn
,
"HTTP/1.0 200 OK
\r\n
"
"Content-Type: text/plain
\r\n\r\n
"
"%s"
,
upload_ok_message
);
close_connection
(
event
->
conn
);
return
1
;
}
}
else
if
(
event
->
type
==
MG_EVENT_LOG
)
{
printf
(
"%s
\n
"
,
(
const
char
*
)
event
->
event_param
);
}
static
int
log_message_cb
(
const
struct
mg_connection
*
conn
,
const
char
*
msg
)
{
(
void
)
conn
;
printf
(
"%s
\n
"
,
msg
);
return
0
;
}
static
const
struct
mg_callbacks
CALLBACKS
=
{
&
begin_request_handler_cb
,
NULL
,
&
log_message_cb
,
NULL
,
NULL
,
NULL
,
NULL
,
&
upload_cb
,
NULL
,
NULL
};
static
const
char
*
OPTIONS
[]
=
{
"document_root"
,
"."
,
"listening_ports"
,
LISTENING_ADDR
,
...
...
@@ -283,7 +247,7 @@ static const char *OPTIONS[] = {
};
static
char
*
read_conn
(
struct
mg_connection
*
conn
,
int
*
size
)
{
char
buf
[
100
],
*
data
=
NULL
;
char
buf
[
MG_BUF_LEN
],
*
data
=
NULL
;
int
len
;
*
size
=
0
;
while
((
len
=
mg_read
(
conn
,
buf
,
sizeof
(
buf
)))
>
0
)
{
...
...
@@ -300,7 +264,7 @@ static void test_mg_download(void) {
struct
mg_connection
*
conn
;
struct
mg_context
*
ctx
;
ASSERT
((
ctx
=
mg_start
(
&
CALLBACKS
,
NULL
,
OPTIONS
))
!=
NULL
);
ASSERT
((
ctx
=
mg_start
(
OPTIONS
,
event_handler
,
NULL
))
!=
NULL
);
ASSERT
(
mg_download
(
NULL
,
port
,
0
,
ebuf
,
sizeof
(
ebuf
),
"%s"
,
""
)
==
NULL
);
ASSERT
(
mg_download
(
"localhost"
,
0
,
0
,
ebuf
,
sizeof
(
ebuf
),
"%s"
,
""
)
==
NULL
);
...
...
@@ -362,33 +326,7 @@ static void test_mg_upload(void) {
char
ebuf
[
100
],
buf
[
20
],
*
file_data
,
*
file2_data
,
*
post_data
;
int
file_len
,
file2_len
,
post_data_len
;
ASSERT
((
ctx
=
mg_start
(
&
CALLBACKS
,
NULL
,
OPTIONS
))
!=
NULL
);
// Upload one file
ASSERT
((
file_data
=
read_file
(
"main.c"
,
&
file_len
))
!=
NULL
);
post_data
=
NULL
;
post_data_len
=
alloc_printf
(
&
post_data
,
0
,
"--%s
\r\n
"
"Content-Disposition: form-data; "
"name=
\"
file
\"
; "
"filename=
\"
%s
\"\r\n\r\n
"
"%.*s
\r\n
"
"--%s--
\r\n
"
,
boundary
,
upload_filename
,
file_len
,
file_data
,
boundary
);
ASSERT
(
post_data_len
>
0
);
ASSERT
((
conn
=
mg_download
(
"localhost"
,
atoi
(
HTTPS_PORT
),
1
,
ebuf
,
sizeof
(
ebuf
),
"POST /upload?1 HTTP/1.1
\r\n
"
"Content-Length: %d
\r\n
"
"Content-Type: multipart/form-data; "
"boundary=%s
\r\n\r\n
"
"%.*s"
,
post_data_len
,
boundary
,
post_data_len
,
post_data
))
!=
NULL
);
free
(
file_data
),
free
(
post_data
);
ASSERT
(
mg_read
(
conn
,
buf
,
sizeof
(
buf
))
==
(
int
)
strlen
(
upload_ok_message
));
ASSERT
(
memcmp
(
buf
,
upload_ok_message
,
strlen
(
upload_ok_message
))
==
0
);
mg_close_connection
(
conn
);
ASSERT
((
ctx
=
mg_start
(
OPTIONS
,
event_handler
,
NULL
))
!=
NULL
);
// Upload two files
ASSERT
((
file_data
=
read_file
(
"lua_5.2.1.h"
,
&
file_len
))
!=
NULL
);
...
...
@@ -411,21 +349,20 @@ static void test_mg_upload(void) {
// Final boundary
"--%s--
\r\n
"
,
boundary
,
upload_filename
,
boundary
,
"f1.txt"
,
file_len
,
file_data
,
boundary
,
upload_filename2
,
boundary
,
"f2.txt"
,
file2_len
,
file2_data
,
boundary
);
ASSERT
(
post_data_len
>
0
);
ASSERT
((
conn
=
mg_download
(
"localhost"
,
atoi
(
HTTPS_PORT
),
1
,
ebuf
,
sizeof
(
ebuf
),
"POST /upload
?2
HTTP/1.1
\r\n
"
"POST /upload HTTP/1.1
\r\n
"
"Content-Length: %d
\r\n
"
"Content-Type: multipart/form-data; "
"boundary=%s
\r\n\r\n
"
"%.*s"
,
post_data_len
,
boundary
,
post_data_len
,
post_data
))
!=
NULL
);
free
(
file_data
),
free
(
file2_data
),
free
(
post_data
);
ASSERT
(
mg_read
(
conn
,
buf
,
sizeof
(
buf
))
==
(
int
)
strlen
(
upload_ok_message
));
ASSERT
(
memcmp
(
buf
,
upload_ok_message
,
strlen
(
upload_ok_message
))
==
0
);
mg_close_connection
(
conn
);
...
...
@@ -523,7 +460,7 @@ static void test_lua(void) {
conn
.
ctx
=
&
ctx
;
conn
.
buf
=
http_request
;
conn
.
buf_size
=
conn
.
data_len
=
strlen
(
http_request
);
conn
.
buf_size
=
conn
.
data_len
=
conn
.
num_bytes_read
=
strlen
(
http_request
);
conn
.
request_len
=
parse_http_message
(
conn
.
buf
,
conn
.
data_len
,
&
conn
.
request_info
);
conn
.
content_len
=
conn
.
data_len
-
conn
.
request_len
;
...
...
@@ -595,7 +532,7 @@ static void test_request_replies(void) {
{
NULL
,
NULL
},
};
ASSERT
((
ctx
=
mg_start
(
&
CALLBACKS
,
NULL
,
OPTIONS
))
!=
NULL
);
ASSERT
((
ctx
=
mg_start
(
OPTIONS
,
event_handler
,
NULL
))
!=
NULL
);
for
(
i
=
0
;
tests
[
i
].
request
!=
NULL
;
i
++
)
{
ASSERT
((
conn
=
mg_download
(
"localhost"
,
port
,
1
,
ebuf
,
sizeof
(
ebuf
),
"%s"
,
tests
[
i
].
request
))
!=
NULL
);
...
...
@@ -604,39 +541,42 @@ static void test_request_replies(void) {
mg_stop
(
ctx
);
}
static
int
api_callback
(
struct
mg_connection
*
conn
)
{
struct
mg_request_info
*
ri
=
mg_get_request_info
(
conn
);
static
const
char
*
api_uri
=
"/?a=%20&b=&c=xx"
;
static
int
api_cb
(
struct
mg_event
*
event
)
{
struct
mg_request_info
*
ri
=
event
->
request_info
;
char
post_data
[
100
]
=
""
;
ASSERT
(
ri
->
user_data
==
(
void
*
)
123
);
ASSERT
(
ri
->
num_headers
==
2
);
ASSERT
(
strcmp
(
mg_get_header
(
conn
,
"host"
),
"blah.com"
)
==
0
);
ASSERT
(
mg_read
(
conn
,
post_data
,
sizeof
(
post_data
))
==
3
);
ASSERT
(
memcmp
(
post_data
,
"b=1"
,
3
)
==
0
);
ASSERT
(
ri
->
query_string
!=
NULL
);
ASSERT
(
ri
->
remote_ip
>
0
);
ASSERT
(
ri
->
remote_port
>
0
);
ASSERT
(
strcmp
(
ri
->
http_version
,
"1.0"
)
==
0
);
mg_printf
(
conn
,
"HTTP/1.0 200 OK
\r\n\r\n
"
);
return
1
;
if
(
event
->
type
==
MG_REQUEST_BEGIN
)
{
ASSERT
(
event
->
user_data
==
(
void
*
)
123
);
ASSERT
(
ri
->
num_headers
==
2
);
ASSERT
(
strcmp
(
mg_get_header
(
event
->
conn
,
"host"
),
"blah.com"
)
==
0
);
ASSERT
(
mg_read
(
event
->
conn
,
post_data
,
sizeof
(
post_data
))
==
3
);
ASSERT
(
memcmp
(
post_data
,
"b=1"
,
3
)
==
0
);
ASSERT
(
ri
->
query_string
!=
NULL
);
ASSERT
(
strcmp
(
ri
->
query_string
,
api_uri
+
2
)
==
0
);
ASSERT
(
ri
->
remote_ip
>
0
);
ASSERT
(
ri
->
remote_port
>
0
);
ASSERT
(
strcmp
(
ri
->
http_version
,
"1.0"
)
==
0
);
mg_printf
(
event
->
conn
,
"HTTP/1.0 200 OK
\r\n\r\n
"
);
return
1
;
}
return
0
;
}
static
void
test_api_calls
(
void
)
{
char
ebuf
[
100
];
struct
mg_callbacks
callbacks
;
struct
mg_connection
*
conn
;
struct
mg_context
*
ctx
;
static
const
char
*
request
=
"POST /?a=%20&b=&c=xx
HTTP/1.0
\r\n
"
static
const
char
*
fmt
=
"POST %s
HTTP/1.0
\r\n
"
"Host: blah.com
\n
"
// More spaces before
"content-length: 3
\r\n
"
// Lower case header name
"
\r\n
b=123456"
;
// Content size > content-length, test for mg_read()
memset
(
&
callbacks
,
0
,
sizeof
(
callbacks
));
callbacks
.
begin_request
=
api_callback
;
ASSERT
((
ctx
=
mg_start
(
&
callbacks
,
(
void
*
)
123
,
OPTIONS
))
!=
NULL
);
ASSERT
((
ctx
=
mg_start
(
OPTIONS
,
api_cb
,
(
void
*
)
123
))
!=
NULL
);
ASSERT
((
conn
=
mg_download
(
"localhost"
,
atoi
(
HTTPS_PORT
),
1
,
ebuf
,
sizeof
(
ebuf
),
"%s"
,
request
))
!=
NULL
);
ebuf
,
sizeof
(
ebuf
),
fmt
,
api_uri
))
!=
NULL
);
mg_close_connection
(
conn
);
mg_stop
(
ctx
);
}
...
...
@@ -732,8 +672,8 @@ int __cdecl main(void) {
test_base64_encode
();
test_match_prefix
();
test_remove_double_dots
();
test_should_keep_alive
();
test_parse_http_message
();
test_should_keep_alive
();
test_mg_download
();
test_mg_get_var
();
test_set_throttle
();
...
...
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