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
75d9a6c8
Commit
75d9a6c8
authored
Jan 23, 2013
by
Sergey Lyubka
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Removed mg_connect() and mg_fetch(). Added mg_download()
parent
179761fd
Changes
4
Hide whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
257 additions
and
222 deletions
+257
-222
mongoose.c
mongoose.c
+115
-110
mongoose.h
mongoose.h
+22
-26
test.pl
test/test.pl
+7
-8
unit_test.c
test/unit_test.c
+113
-78
No files found.
mongoose.c
View file @
75d9a6c8
...
@@ -1627,16 +1627,13 @@ int mg_write(struct mg_connection *conn, const void *buf, size_t len) {
...
@@ -1627,16 +1627,13 @@ int mg_write(struct mg_connection *conn, const void *buf, size_t len) {
return
(
int
)
total
;
return
(
int
)
total
;
}
}
int
mg_
printf
(
struct
mg_connection
*
conn
,
const
char
*
fmt
,
...
)
{
int
mg_
vprintf
(
struct
mg_connection
*
conn
,
const
char
*
fmt
,
va_list
ap
)
{
char
mem
[
MG_BUF_LEN
],
*
buf
=
mem
;
char
mem
[
MG_BUF_LEN
],
*
buf
=
mem
;
int
len
;
int
len
;
va_list
ap
;
// Print in a local buffer first, hoping that it is large enough to
// Print in a local buffer first, hoping that it is large enough to
// hold the whole message
// hold the whole message
va_start
(
ap
,
fmt
);
len
=
vsnprintf
(
mem
,
sizeof
(
mem
),
fmt
,
ap
);
len
=
vsnprintf
(
mem
,
sizeof
(
mem
),
fmt
,
ap
);
va_end
(
ap
);
if
(
len
==
0
)
{
if
(
len
==
0
)
{
// Do nothing. mg_printf(conn, "%s", "") was called.
// Do nothing. mg_printf(conn, "%s", "") was called.
...
@@ -1647,9 +1644,7 @@ int mg_printf(struct mg_connection *conn, const char *fmt, ...) {
...
@@ -1647,9 +1644,7 @@ int mg_printf(struct mg_connection *conn, const char *fmt, ...) {
}
else
if
(
len
>
(
int
)
sizeof
(
mem
)
&&
}
else
if
(
len
>
(
int
)
sizeof
(
mem
)
&&
(
buf
=
(
char
*
)
malloc
(
len
+
1
))
!=
NULL
)
{
(
buf
=
(
char
*
)
malloc
(
len
+
1
))
!=
NULL
)
{
// Local buffer is not large enough, allocate big buffer on heap
// Local buffer is not large enough, allocate big buffer on heap
va_start
(
ap
,
fmt
);
vsnprintf
(
buf
,
len
+
1
,
fmt
,
ap
);
vsnprintf
(
buf
,
len
+
1
,
fmt
,
ap
);
va_end
(
ap
);
len
=
mg_write
(
conn
,
buf
,
(
size_t
)
len
);
len
=
mg_write
(
conn
,
buf
,
(
size_t
)
len
);
free
(
buf
);
free
(
buf
);
}
else
if
(
len
>
(
int
)
sizeof
(
mem
))
{
}
else
if
(
len
>
(
int
)
sizeof
(
mem
))
{
...
@@ -1665,6 +1660,12 @@ int mg_printf(struct mg_connection *conn, const char *fmt, ...) {
...
@@ -1665,6 +1660,12 @@ int mg_printf(struct mg_connection *conn, const char *fmt, ...) {
return
len
;
return
len
;
}
}
int
mg_printf
(
struct
mg_connection
*
conn
,
const
char
*
fmt
,
...)
{
va_list
ap
;
va_start
(
ap
,
fmt
);
return
mg_vprintf
(
conn
,
fmt
,
ap
);
}
// URL-decode input buffer into destination buffer.
// URL-decode input buffer into destination buffer.
// 0-terminate the destination buffer. Return the length of decoded data.
// 0-terminate the destination buffer. Return the length of decoded data.
// form-url-encoded data differs from URI encoding in a way that it
// form-url-encoded data differs from URI encoding in a way that it
...
@@ -2811,7 +2812,7 @@ static void handle_file_request(struct mg_connection *conn, const char *path,
...
@@ -2811,7 +2812,7 @@ static void handle_file_request(struct mg_connection *conn, const char *path,
if
(
!
mg_fopen
(
conn
,
path
,
"rb"
,
filep
))
{
if
(
!
mg_fopen
(
conn
,
path
,
"rb"
,
filep
))
{
send_http_error
(
conn
,
500
,
http_500_error
,
send_http_error
(
conn
,
500
,
http_500_error
,
"fopen(%s): %s"
,
path
,
strerror
(
ERRNO
));
"fopen(%s): %s"
,
path
,
strerror
(
ERRNO
));
return
;
return
;
}
}
fclose_on_exec
(
filep
);
fclose_on_exec
(
filep
);
...
@@ -2891,7 +2892,7 @@ static int is_valid_http_method(const char *method) {
...
@@ -2891,7 +2892,7 @@ static int is_valid_http_method(const char *method) {
// This function modifies the buffer by NUL-terminating
// This function modifies the buffer by NUL-terminating
// HTTP request components, header names and header values.
// HTTP request components, header names and header values.
static
int
parse_http_message
(
char
*
buf
,
int
len
,
struct
mg_request_info
*
ri
)
{
static
int
parse_http_message
(
char
*
buf
,
int
len
,
struct
mg_request_info
*
ri
)
{
int
request_length
=
get_request_len
(
buf
,
len
);
int
is_request
,
request_length
=
get_request_len
(
buf
,
len
);
if
(
request_length
>
0
)
{
if
(
request_length
>
0
)
{
// 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
;
...
@@ -2906,28 +2907,20 @@ static int parse_http_message(char *buf, int len, struct mg_request_info *ri) {
...
@@ -2906,28 +2907,20 @@ static int parse_http_message(char *buf, int len, struct mg_request_info *ri) {
ri
->
request_method
=
skip
(
&
buf
,
" "
);
ri
->
request_method
=
skip
(
&
buf
,
" "
);
ri
->
uri
=
skip
(
&
buf
,
" "
);
ri
->
uri
=
skip
(
&
buf
,
" "
);
ri
->
http_version
=
skip
(
&
buf
,
"
\r\n
"
);
ri
->
http_version
=
skip
(
&
buf
,
"
\r\n
"
);
parse_http_headers
(
&
buf
,
ri
);
if
(((
is_request
=
is_valid_http_method
(
ri
->
request_method
))
&&
memcmp
(
ri
->
http_version
,
"HTTP/"
,
5
)
!=
0
)
||
(
!
is_request
&&
memcmp
(
ri
->
request_method
,
"HTTP/"
,
5
))
!=
0
)
{
request_length
=
-
1
;
}
else
{
if
(
is_request
)
{
ri
->
http_version
+=
5
;
}
parse_http_headers
(
&
buf
,
ri
);
}
}
}
return
request_length
;
return
request_length
;
}
}
static
int
parse_http_request
(
char
*
buf
,
int
len
,
struct
mg_request_info
*
ri
)
{
int
result
=
parse_http_message
(
buf
,
len
,
ri
);
if
(
result
>
0
&&
is_valid_http_method
(
ri
->
request_method
)
&&
!
strncmp
(
ri
->
http_version
,
"HTTP/"
,
5
))
{
ri
->
http_version
+=
5
;
// Skip "HTTP/"
}
else
{
result
=
-
1
;
}
return
result
;
}
static
int
parse_http_response
(
char
*
buf
,
int
len
,
struct
mg_request_info
*
ri
)
{
int
result
=
parse_http_message
(
buf
,
len
,
ri
);
return
result
>
0
&&
!
strncmp
(
ri
->
request_method
,
"HTTP/"
,
5
)
?
result
:
-
1
;
}
// Keep reading the input (either opened file descriptor fd, or socket sock,
// Keep reading the input (either opened file descriptor fd, or socket sock,
// or SSL descriptor ssl) into buffer buf, until \r\n\r\n appears in the
// or SSL descriptor ssl) into buffer buf, until \r\n\r\n appears in the
// buffer (which marks the end of HTTP request). Buffer buf may already
// buffer (which marks the end of HTTP request). Buffer buf may already
...
@@ -4734,74 +4727,109 @@ void mg_close_connection(struct mg_connection *conn) {
...
@@ -4734,74 +4727,109 @@ void mg_close_connection(struct mg_connection *conn) {
free
(
conn
);
free
(
conn
);
}
}
struct
mg_connection
*
mg_connect
(
struct
mg_context
*
ctx
,
struct
mg_connection
*
mg_connect
(
const
char
*
host
,
int
port
,
int
use_ssl
,
const
char
*
host
,
int
port
,
int
use_ssl
)
{
char
*
ebuf
,
size_t
ebuf_len
)
{
struct
mg_connection
*
newconn
=
NULL
;
static
struct
mg_context
fake_ctx
;
struct
mg_connection
*
conn
=
NULL
;
struct
sockaddr_in
sin
;
struct
sockaddr_in
sin
;
struct
hostent
*
he
;
struct
hostent
*
he
;
SSL_CTX
*
ssl
=
NULL
;
int
sock
;
int
sock
;
if
(
use_ssl
&&
(
ctx
==
NULL
||
ctx
->
client_ssl_ctx
==
NULL
))
{
if
(
host
==
NULL
)
{
cry
(
fc
(
ctx
),
"%s: SSL is not initialized"
,
__func__
);
snprintf
(
ebuf
,
ebuf_len
,
"%s"
,
"NULL host"
);
}
else
if
(
use_ssl
&&
SSLv23_client_method
==
NULL
)
{
snprintf
(
ebuf
,
ebuf_len
,
"%s"
,
"SSL is not initialized"
);
#ifndef NO_SSL
}
else
if
(
use_ssl
&&
(
ssl
=
SSL_CTX_new
(
SSLv23_client_method
()))
==
NULL
)
{
snprintf
(
ebuf
,
ebuf_len
,
"SSL_CTX_new: %s"
,
ssl_error
());
#endif // NO_SSL
}
else
if
((
he
=
gethostbyname
(
host
))
==
NULL
)
{
}
else
if
((
he
=
gethostbyname
(
host
))
==
NULL
)
{
cry
(
fc
(
ctx
),
"%s: gethostbyname(%s): %s"
,
__func__
,
host
,
strerror
(
ERRNO
));
snprintf
(
ebuf
,
ebuf_len
,
"gethostbyname(%s): %s"
,
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
(
ctx
),
"%s: socket: %s"
,
__func__
,
strerror
(
ERRNO
));
snprintf
(
ebuf
,
ebuf_len
,
"socket(): %s"
,
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
(
ctx
),
"%s: connect(%s:%d): %s"
,
__func__
,
host
,
port
,
snprintf
(
ebuf
,
ebuf_len
,
"connect(%s:%d): %s"
,
strerror
(
ERRNO
));
host
,
port
,
strerror
(
ERRNO
));
closesocket
(
sock
);
closesocket
(
sock
);
}
else
if
((
new
conn
=
(
struct
mg_connection
*
)
}
else
if
((
conn
=
(
struct
mg_connection
*
)
calloc
(
1
,
sizeof
(
*
newconn
)
))
==
NULL
)
{
calloc
(
1
,
sizeof
(
*
conn
)
+
MAX_REQUEST_SIZE
))
==
NULL
)
{
cry
(
fc
(
ctx
),
"%s: calloc: %s"
,
__func__
,
strerror
(
ERRNO
));
snprintf
(
ebuf
,
ebuf_len
,
"calloc(): %s"
,
strerror
(
ERRNO
));
closesocket
(
sock
);
closesocket
(
sock
);
}
else
{
}
else
{
newconn
->
ctx
=
ctx
;
conn
->
buf_size
=
MAX_REQUEST_SIZE
;
newconn
->
client
.
sock
=
sock
;
conn
->
buf
=
(
char
*
)
(
conn
+
1
);
newconn
->
client
.
rsa
.
sin
=
sin
;
conn
->
ctx
=
&
fake_ctx
;
newconn
->
client
.
is_ssl
=
use_ssl
;
conn
->
client
.
sock
=
sock
;
conn
->
client
.
rsa
.
sin
=
sin
;
conn
->
client
.
is_ssl
=
use_ssl
;
if
(
use_ssl
)
{
if
(
use_ssl
)
{
sslize
(
newconn
,
ctx
->
client_ssl_ctx
,
SSL_connect
);
sslize
(
conn
,
ssl
,
SSL_connect
);
}
}
}
}
}
}
if
(
ssl
!=
NULL
)
{
SSL_CTX_free
(
ssl
);
}
return
new
conn
;
return
conn
;
}
}
FILE
*
mg_fetch
(
struct
mg_context
*
ctx
,
const
char
*
url
,
const
char
*
path
,
static
int
is_valid_uri
(
const
char
*
uri
)
{
char
*
buf
,
size_t
buf_len
,
struct
mg_request_info
*
ri
)
{
// Conform to http://www.w3.org/Protocols/rfc2616/rfc2616-sec5.html#sec5.1.2
struct
mg_connection
*
newconn
;
// URI can be an asterisk (*) or should start with slash.
int
n
,
req_length
,
data_length
,
port
;
return
uri
[
0
]
==
'/'
||
(
uri
[
0
]
==
'*'
&&
uri
[
1
]
==
'\0'
);
char
host
[
1025
],
proto
[
10
],
buf2
[
MG_BUF_LEN
];
}
FILE
*
fp
=
NULL
;
if
(
sscanf
(
url
,
"%9[htps]://%1024[^:]:%d/%n"
,
proto
,
host
,
&
port
,
&
n
)
==
3
)
{
static
int
getreq
(
struct
mg_connection
*
conn
,
char
*
ebuf
,
size_t
ebuf_len
)
{
}
else
if
(
sscanf
(
url
,
"%9[htps]://%1024[^/]/%n"
,
proto
,
host
,
&
n
)
==
2
)
{
const
char
*
cl
;
port
=
mg_strcasecmp
(
proto
,
"https"
)
==
0
?
443
:
80
;
ebuf
[
0
]
=
'\0'
;
reset_per_request_attributes
(
conn
);
conn
->
request_len
=
read_request
(
NULL
,
conn
,
conn
->
buf
,
conn
->
buf_size
,
&
conn
->
data_len
);
assert
(
conn
->
request_len
<
0
||
conn
->
data_len
>=
conn
->
request_len
);
if
(
conn
->
request_len
==
0
&&
conn
->
data_len
==
conn
->
buf_size
)
{
snprintf
(
ebuf
,
ebuf_len
,
"%s"
,
"Request Too Large"
);
}
if
(
conn
->
request_len
<=
0
)
{
snprintf
(
ebuf
,
ebuf_len
,
"%s"
,
"Client closed connection"
);
}
else
if
(
parse_http_message
(
conn
->
buf
,
conn
->
buf_size
,
&
conn
->
request_info
)
<=
0
)
{
snprintf
(
ebuf
,
ebuf_len
,
"Bad request: [%.*s]"
,
conn
->
data_len
,
conn
->
buf
);
}
else
{
}
else
{
cry
(
fc
(
ctx
),
"%s: invalid URL: [%s]"
,
__func__
,
url
);
// Request is valid
return
NULL
;
if
((
cl
=
get_header
(
&
conn
->
request_info
,
"Content-Length"
))
!=
NULL
)
{
conn
->
content_len
=
strtoll
(
cl
,
NULL
,
10
);
}
else
if
(
!
mg_strcasecmp
(
conn
->
request_info
.
request_method
,
"POST"
)
||
!
mg_strcasecmp
(
conn
->
request_info
.
request_method
,
"PUT"
))
{
conn
->
content_len
=
-
1
;
}
else
{
conn
->
content_len
=
0
;
}
conn
->
birth_time
=
time
(
NULL
);
}
}
return
ebuf
[
0
]
==
'\0'
;
}
struct
mg_connection
*
mg_download
(
const
char
*
host
,
int
port
,
int
use_ssl
,
char
*
ebuf
,
size_t
ebuf_len
,
const
char
*
fmt
,
...)
{
struct
mg_connection
*
conn
;
va_list
ap
;
if
((
newconn
=
mg_connect
(
ctx
,
host
,
port
,
va_start
(
ap
,
fmt
);
!
strcmp
(
proto
,
"https"
)))
==
NULL
)
{
ebuf
[
0
]
=
'\0'
;
cry
(
fc
(
ctx
),
"%s: mg_connect(%s): %s"
,
__func__
,
url
,
strerror
(
ERRNO
));
if
((
conn
=
mg_connect
(
host
,
port
,
use_ssl
,
ebuf
,
ebuf_len
))
==
NULL
)
{
}
else
if
(
mg_vprintf
(
conn
,
fmt
,
ap
)
<=
0
)
{
snprintf
(
ebuf
,
ebuf_len
,
"%s"
,
"Error sending request"
);
}
else
if
(
!
getreq
(
conn
,
ebuf
,
ebuf_len
))
{
}
else
{
}
else
{
mg_printf
(
newconn
,
"GET /%s HTTP/1.0
\r\n
Host: %s
\r\n\r\n
"
,
url
+
n
,
host
);
#if 0
data_length
=
0
;
req_length
=
read_request
(
NULL
,
newconn
,
buf
,
buf_len
,
&
data_length
);
if
(
req_length
<=
0
)
{
cry
(
fc
(
ctx
),
"%s(%s): invalid HTTP reply"
,
__func__
,
url
);
}
else
if
(
parse_http_response
(
buf
,
req_length
,
ri
)
<=
0
)
{
cry
(
fc
(
ctx
),
"%s(%s): cannot parse HTTP headers"
,
__func__
,
url
);
}
else
if
((
fp
=
fopen
(
path
,
"w+b"
))
==
NULL
)
{
cry
(
fc
(
ctx
),
"%s: fopen(%s): %s"
,
__func__
,
path
,
strerror
(
ERRNO
));
}
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 &&
...
@@ -4811,8 +4839,8 @@ FILE *mg_fetch(struct mg_context *ctx, const char *url, const char *path,
...
@@ -4811,8 +4839,8 @@ FILE *mg_fetch(struct mg_context *ctx, const char *url, const char *path,
fp = NULL;
fp = NULL;
}
}
// Read the rest of the response and write it to the file. Do not use
// Read the rest of the response and write it to the file. Do not use
// mg_read() cause we didn't set
new
conn->content_len properly.
// mg_read() cause we didn't set conn->content_len properly.
while
(
fp
&&
(
data_length
=
pull
(
0
,
new
conn
,
buf2
,
sizeof
(
buf2
)))
>
0
)
{
while (fp && (data_length = pull(0, conn, 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(ctx), "%s: fwrite(%s): %s", __func__, path, strerror(ERRNO));
cry(fc(ctx), "%s: fwrite(%s): %s", __func__, path, strerror(ERRNO));
fclose(fp);
fclose(fp);
...
@@ -4820,23 +4848,20 @@ FILE *mg_fetch(struct mg_context *ctx, const char *url, const char *path,
...
@@ -4820,23 +4848,20 @@ FILE *mg_fetch(struct mg_context *ctx, const char *url, const char *path,
break;
break;
}
}
}
}
}
#endif
mg_close_connection
(
newconn
);
}
if
(
ebuf
[
0
]
!=
'\0'
&&
conn
!=
NULL
)
{
mg_close_connection
(
conn
);
conn
=
NULL
;
}
}
return
fp
;
return
conn
;
}
static
int
is_valid_uri
(
const
char
*
uri
)
{
// Conform to http://www.w3.org/Protocols/rfc2616/rfc2616-sec5.html#sec5.1.2
// URI can be an asterisk (*) or should start with slash.
return
uri
[
0
]
==
'/'
||
(
uri
[
0
]
==
'*'
&&
uri
[
1
]
==
'\0'
);
}
}
static
void
process_new_connection
(
struct
mg_connection
*
conn
)
{
static
void
process_new_connection
(
struct
mg_connection
*
conn
)
{
struct
mg_request_info
*
ri
=
&
conn
->
request_info
;
struct
mg_request_info
*
ri
=
&
conn
->
request_info
;
int
keep_alive_enabled
,
keep_alive
,
discard_len
;
int
keep_alive_enabled
,
keep_alive
,
discard_len
;
c
onst
char
*
cl
;
c
har
ebuf
[
100
]
;
keep_alive_enabled
=
!
strcmp
(
conn
->
ctx
->
config
[
ENABLE_KEEP_ALIVE
],
"yes"
);
keep_alive_enabled
=
!
strcmp
(
conn
->
ctx
->
config
[
ENABLE_KEEP_ALIVE
],
"yes"
);
keep_alive
=
0
;
keep_alive
=
0
;
...
@@ -4845,38 +4870,18 @@ static void process_new_connection(struct mg_connection *conn) {
...
@@ -4845,38 +4870,18 @@ static void process_new_connection(struct mg_connection *conn) {
// to crule42.
// to crule42.
conn
->
data_len
=
0
;
conn
->
data_len
=
0
;
do
{
do
{
reset_per_request_attributes
(
conn
);
if
(
!
getreq
(
conn
,
ebuf
,
sizeof
(
ebuf
)))
{
conn
->
request_len
=
read_request
(
NULL
,
conn
,
conn
->
buf
,
conn
->
buf_size
,
send_http_error
(
conn
,
500
,
"Server Error"
,
"%s"
,
ebuf
);
&
conn
->
data_len
);
}
else
if
(
!
is_valid_uri
(
conn
->
request_info
.
uri
))
{
assert
(
conn
->
request_len
<
0
||
conn
->
data_len
>=
conn
->
request_len
);
snprintf
(
ebuf
,
sizeof
(
ebuf
),
"Invalid URI: [%s]"
,
ri
->
uri
);
if
(
conn
->
request_len
==
0
&&
conn
->
data_len
==
conn
->
buf_size
)
{
send_http_error
(
conn
,
400
,
"Bad Request"
,
"%s"
,
ebuf
);
send_http_error
(
conn
,
413
,
"Request Too Large"
,
"%s"
,
""
);
return
;
}
if
(
conn
->
request_len
<=
0
)
{
return
;
// Remote end closed the connection
}
if
(
parse_http_request
(
conn
->
buf
,
conn
->
buf_size
,
ri
)
<=
0
||
!
is_valid_uri
(
ri
->
uri
))
{
// Do not put garbage in the access log, just send it back to the client
send_http_error
(
conn
,
400
,
"Bad Request"
,
"Cannot parse HTTP request: [%.*s]"
,
conn
->
data_len
,
conn
->
buf
);
conn
->
must_close
=
1
;
}
else
if
(
strcmp
(
ri
->
http_version
,
"1.0"
)
&&
}
else
if
(
strcmp
(
ri
->
http_version
,
"1.0"
)
&&
strcmp
(
ri
->
http_version
,
"1.1"
))
{
strcmp
(
ri
->
http_version
,
"1.1"
))
{
// Request seems valid, but HTTP version is strange
snprintf
(
ebuf
,
sizeof
(
ebuf
),
"Bad HTTP version: [%s]"
,
ri
->
http_version
);
send_http_error
(
conn
,
505
,
"HTTP version not supported"
,
"%s"
,
""
);
send_http_error
(
conn
,
505
,
"Bad HTTP version"
,
"%s"
,
ebuf
);
log_access
(
conn
);
}
}
else
{
// Request is valid, handle it
if
(
ebuf
[
0
]
==
'\0'
)
{
if
((
cl
=
get_header
(
ri
,
"Content-Length"
))
!=
NULL
)
{
conn
->
content_len
=
strtoll
(
cl
,
NULL
,
10
);
}
else
if
(
!
mg_strcasecmp
(
ri
->
request_method
,
"POST"
)
||
!
mg_strcasecmp
(
ri
->
request_method
,
"PUT"
))
{
conn
->
content_len
=
-
1
;
}
else
{
conn
->
content_len
=
0
;
}
conn
->
birth_time
=
time
(
NULL
);
handle_request
(
conn
);
handle_request
(
conn
);
conn
->
request_info
.
ev_data
=
(
void
*
)
(
long
)
conn
->
status_code
;
conn
->
request_info
.
ev_data
=
(
void
*
)
(
long
)
conn
->
status_code
;
call_user
(
conn
,
MG_REQUEST_COMPLETE
);
call_user
(
conn
,
MG_REQUEST_COMPLETE
);
...
...
mongoose.h
View file @
75d9a6c8
...
@@ -236,12 +236,6 @@ struct mg_request_info *mg_get_request_info(struct mg_connection *);
...
@@ -236,12 +236,6 @@ struct mg_request_info *mg_get_request_info(struct mg_connection *);
int
mg_write
(
struct
mg_connection
*
,
const
void
*
buf
,
size_t
len
);
int
mg_write
(
struct
mg_connection
*
,
const
void
*
buf
,
size_t
len
);
// Send data to the browser using printf() semantics.
//
// Works exactly like mg_write(), but allows to do message formatting.
// Below are the macros for enabling compiler-specific checks for
// printf-like arguments.
#undef PRINTF_FORMAT_STRING
#undef PRINTF_FORMAT_STRING
#if _MSC_VER >= 1400
#if _MSC_VER >= 1400
#include <sal.h>
#include <sal.h>
...
@@ -260,6 +254,11 @@ int mg_write(struct mg_connection *, const void *buf, size_t len);
...
@@ -260,6 +254,11 @@ int mg_write(struct mg_connection *, const void *buf, size_t len);
#define PRINTF_ARGS(x, y)
#define PRINTF_ARGS(x, y)
#endif
#endif
// Send data to the browser using printf() semantics.
//
// Works exactly like mg_write(), but allows to do message formatting.
// Below are the macros for enabling compiler-specific checks for
// printf-like arguments.
int
mg_printf
(
struct
mg_connection
*
,
int
mg_printf
(
struct
mg_connection
*
,
PRINTF_FORMAT_STRING
(
const
char
*
fmt
),
...)
PRINTF_ARGS
(
2
,
3
);
PRINTF_FORMAT_STRING
(
const
char
*
fmt
),
...)
PRINTF_ARGS
(
2
,
3
);
...
@@ -316,32 +315,29 @@ int mg_get_cookie(const struct mg_connection *,
...
@@ -316,32 +315,29 @@ int mg_get_cookie(const struct mg_connection *,
const
char
*
cookie_name
,
char
*
buf
,
size_t
buf_len
);
const
char
*
cookie_name
,
char
*
buf
,
size_t
buf_len
);
// Connect to the remote web server.
// Download data from the remote web server.
// host: host name to connect to, e.g. "foo.com", or "10.12.40.1".
// port: port number, e.g. 80.
// use_ssl: wether to use SSL connection.
// error_buffer, error_buffer_size: error message placeholder.
// request_fmt,...: HTTP request.
// Return:
// Return:
// On success, valid pointer to the new connection
// On success, valid pointer to the new connection, suitable for mg_read().
// On error, NULL
// On error, NULL.
struct
mg_connection
*
mg_connect
(
struct
mg_context
*
ctx
,
// Example:
const
char
*
host
,
int
port
,
int
use_ssl
);
// char ebuf[100];
// struct mg_connection *conn;
// conn = mg_download("google.com", 80, 0, ebuf, sizeof(ebuf),
// "%s", "GET / HTTP/1.0\r\n\r\nHost: google.com\r\n\r\n");
struct
mg_connection
*
mg_download
(
const
char
*
host
,
int
port
,
int
use_ssl
,
char
*
error_buffer
,
size_t
error_buffer_size
,
const
char
*
request_fmt
,
...);
// Close the connection opened by mg_
connect
().
// Close the connection opened by mg_
download
().
void
mg_close_connection
(
struct
mg_connection
*
conn
);
void
mg_close_connection
(
struct
mg_connection
*
conn
);
// Download given URL to a given file.
// url: URL to download
// path: file name where to save the data
// request_info: pointer to a structure that will hold parsed reply headers
// buf, bul_len: a buffer for the reply headers
// Return:
// On error, NULL
// On success, opened file stream to the downloaded contents. The stream
// is positioned to the end of the file. It is the user's responsibility
// to fclose() the opened file stream.
FILE
*
mg_fetch
(
struct
mg_context
*
ctx
,
const
char
*
url
,
const
char
*
path
,
char
*
buf
,
size_t
buf_len
,
struct
mg_request_info
*
request_info
);
// File upload functionality. Each uploaded file gets saved into a temporary
// File upload functionality. Each uploaded file gets saved into a temporary
// file and MG_UPLOAD event is sent.
// file and MG_UPLOAD event is sent.
// Return number of uploaded files.
// Return number of uploaded files.
...
...
test/test.pl
View file @
75d9a6c8
...
@@ -167,7 +167,7 @@ kill_spawned_child();
...
@@ -167,7 +167,7 @@ kill_spawned_child();
# Spawn the server on port $port
# Spawn the server on port $port
my
$cmd
=
"$exe "
.
my
$cmd
=
"$exe "
.
"-listening_ports $port "
.
"-listening_ports
127.0.0.1:
$port "
.
"-access_log_file access.log "
.
"-access_log_file access.log "
.
"-error_log_file debug.log "
.
"-error_log_file debug.log "
.
"-cgi_environment CGI_FOO=foo,CGI_BAR=bar,CGI_BAZ=baz "
.
"-cgi_environment CGI_FOO=foo,CGI_BAR=bar,CGI_BAZ=baz "
.
...
@@ -220,11 +220,10 @@ write_file("$root/a+.txt", '');
...
@@ -220,11 +220,10 @@ write_file("$root/a+.txt", '');
o
(
"GET /a+.txt HTTP/1.0\n\n"
,
'HTTP/1.1 200 OK'
,
'URL-decoding, + in URI'
);
o
(
"GET /a+.txt HTTP/1.0\n\n"
,
'HTTP/1.1 200 OK'
,
'URL-decoding, + in URI'
);
# Test HTTP version parsing
# Test HTTP version parsing
o
(
"GET / HTTPX/1.0\r\n\r\n"
,
'400 Bad Request'
,
'Bad HTTP Version'
,
0
);
o
(
"GET / HTTPX/1.0\r\n\r\n"
,
'^HTTP/1.1 500'
,
'Bad HTTP Version'
,
0
);
o
(
"GET / HTTP/x.1\r\n\r\n"
,
'505 HTTP'
,
'Bad HTTP maj Version'
);
o
(
"GET / HTTP/x.1\r\n\r\n"
,
'^HTTP/1.1 505'
,
'Bad HTTP maj Version'
,
0
);
o
(
"GET / HTTP/1.1z\r\n\r\n"
,
'505 HTTP'
,
'Bad HTTP min Version'
);
o
(
"GET / HTTP/1.1z\r\n\r\n"
,
'^HTTP/1.1 505'
,
'Bad HTTP min Version'
,
0
);
o
(
"GET / HTTP/02.0\r\n\r\n"
,
'505 HTTP version not supported'
,
o
(
"GET / HTTP/02.0\r\n\r\n"
,
'^HTTP/1.1 505'
,
'HTTP Version >1.1'
,
0
);
'HTTP Version >1.1'
);
# File with leading single dot
# File with leading single dot
o
(
"GET /.leading.dot.txt HTTP/1.0\n\n"
,
'abc123'
,
'Leading dot 1'
);
o
(
"GET /.leading.dot.txt HTTP/1.0\n\n"
,
'abc123'
,
'Leading dot 1'
);
...
@@ -463,7 +462,7 @@ sub do_unit_test {
...
@@ -463,7 +462,7 @@ sub do_unit_test {
sub
do_embedded_test
{
sub
do_embedded_test
{
my
$cmd
=
"cc -W -Wall -o $embed_exe $root/embed.c mongoose.c -I. "
.
my
$cmd
=
"cc -W -Wall -o $embed_exe $root/embed.c mongoose.c -I. "
.
"-pthread -DNO_SSL -DLISTENING_PORT=\\\"$port\\\""
;
"-pthread -DNO_SSL -DLISTENING_PORT=\\\"
127.0.0.1:
$port\\\""
;
if
(
on_windows
())
{
if
(
on_windows
())
{
$cmd
=
"cl $root/embed.c mongoose.c /I. /nologo /DNO_SSL "
.
$cmd
=
"cl $root/embed.c mongoose.c /I. /nologo /DNO_SSL "
.
"/DLISTENING_PORT=\\\"$port\\\" /link /out:$embed_exe.exe ws2_32.lib "
;
"/DLISTENING_PORT=\\\"$port\\\" /link /out:$embed_exe.exe ws2_32.lib "
;
...
@@ -514,7 +513,7 @@ sub do_embedded_test {
...
@@ -514,7 +513,7 @@ sub do_embedded_test {
'Remote user: \[\]'
'Remote user: \[\]'
,
'request_info'
,
0
);
,
'request_info'
,
0
);
o
(
"GET /not_exist HTTP/1.0\n\n"
,
'Error: \[404\]'
,
'404 handler'
,
0
);
o
(
"GET /not_exist HTTP/1.0\n\n"
,
'Error: \[404\]'
,
'404 handler'
,
0
);
o
(
"bad request\n\n"
,
'Error: \[
4
00\]'
,
'* error handler'
,
0
);
o
(
"bad request\n\n"
,
'Error: \[
5
00\]'
,
'* error handler'
,
0
);
# o("GET /foo/secret HTTP/1.0\n\n",
# o("GET /foo/secret HTTP/1.0\n\n",
# '401 Unauthorized', 'mg_protect_uri', 0);
# '401 Unauthorized', 'mg_protect_uri', 0);
# o("GET /foo/secret HTTP/1.0\nAuthorization: Digest username=bill\n\n",
# o("GET /foo/secret HTTP/1.0\nAuthorization: Digest username=bill\n\n",
...
...
test/unit_test.c
View file @
75d9a6c8
...
@@ -21,6 +21,7 @@
...
@@ -21,6 +21,7 @@
// Unit test for the mongoose web server. Tests embedded API.
// Unit test for the mongoose web server. Tests embedded API.
#define USE_WEBSOCKET
#define USE_WEBSOCKET
#define USE_LUA
#include "mongoose.c"
#include "mongoose.c"
#define FATAL(str, line) do { \
#define FATAL(str, line) do { \
...
@@ -29,24 +30,35 @@
...
@@ -29,24 +30,35 @@
} while (0)
} while (0)
#define ASSERT(expr) do { if (!(expr)) FATAL(#expr, __LINE__); } while (0)
#define ASSERT(expr) do { if (!(expr)) FATAL(#expr, __LINE__); } while (0)
#define LISTENING_ADDR "127.0.0.1:56789"
#define HTTP_PORT "56789"
#define HTTPS_PORT "56790"
#define LISTENING_ADDR "127.0.0.1:" HTTP_PORT "r,127.0.0.1:" HTTPS_PORT "s"
static
void
test_parse_http_
request
()
{
static
void
test_parse_http_
message
()
{
struct
mg_request_info
ri
;
struct
mg_request_info
ri
;
char
req1
[]
=
"GET / HTTP/1.1
\r\n\r\n
"
;
char
req1
[]
=
"GET / HTTP/1.1
\r\n\r\n
"
;
char
req2
[]
=
"BLAH / HTTP/1.1
\r\n\r\n
"
;
char
req2
[]
=
"BLAH / HTTP/1.1
\r\n\r\n
"
;
char
req3
[]
=
"GET / HTTP/1.1
\r\n
Bah
\r\n
"
;
char
req3
[]
=
"GET / HTTP/1.1
\r\n
Bah
\r\n
"
;
char
req4
[]
=
"GET / HTTP/1.1
\r\n
A: foo bar
\r\n
B: bar
\r\n
baz
\r\n\r\n
"
;
char
req4
[]
=
"GET / HTTP/1.1
\r\n
A: foo bar
\r\n
B: bar
\r\n
baz
\r\n\r\n
"
;
char
req5
[]
=
"GET / HTTP/1.1
\r\n\r\n
"
;
char
req6
[]
=
"G"
;
char
req7
[]
=
" blah "
;
char
req8
[]
=
" HTTP/1.1 200 OK
\n\n
"
;
ASSERT
(
parse_http_
request
(
req1
,
sizeof
(
req1
),
&
ri
)
==
sizeof
(
req1
)
-
1
);
ASSERT
(
parse_http_
message
(
req1
,
sizeof
(
req1
),
&
ri
)
==
sizeof
(
req1
)
-
1
);
ASSERT
(
strcmp
(
ri
.
http_version
,
"1.1"
)
==
0
);
ASSERT
(
strcmp
(
ri
.
http_version
,
"1.1"
)
==
0
);
ASSERT
(
ri
.
num_headers
==
0
);
ASSERT
(
ri
.
num_headers
==
0
);
ASSERT
(
parse_http_request
(
req2
,
sizeof
(
req2
),
&
ri
)
==
-
1
);
ASSERT
(
parse_http_message
(
req2
,
sizeof
(
req2
),
&
ri
)
==
-
1
);
ASSERT
(
parse_http_request
(
req3
,
sizeof
(
req3
),
&
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
(
""
,
0
,
&
ri
)
==
0
);
ASSERT
(
parse_http_message
(
req8
,
sizeof
(
req8
),
&
ri
)
==
sizeof
(
req8
)
-
1
);
// TODO(lsm): Fix this. Header value may span multiple lines.
// TODO(lsm): Fix this. Header value may span multiple lines.
ASSERT
(
parse_http_request
(
req4
,
sizeof
(
req4
),
&
ri
)
==
sizeof
(
req4
)
-
1
);
ASSERT
(
parse_http_message
(
req4
,
sizeof
(
req4
),
&
ri
)
==
sizeof
(
req4
)
-
1
);
ASSERT
(
strcmp
(
ri
.
http_version
,
"1.1"
)
==
0
);
ASSERT
(
ri
.
num_headers
==
3
);
ASSERT
(
ri
.
num_headers
==
3
);
ASSERT
(
strcmp
(
ri
.
http_headers
[
0
].
name
,
"A"
)
==
0
);
ASSERT
(
strcmp
(
ri
.
http_headers
[
0
].
name
,
"A"
)
==
0
);
ASSERT
(
strcmp
(
ri
.
http_headers
[
0
].
value
,
"foo bar"
)
==
0
);
ASSERT
(
strcmp
(
ri
.
http_headers
[
0
].
value
,
"foo bar"
)
==
0
);
...
@@ -55,7 +67,9 @@ static void test_parse_http_request() {
...
@@ -55,7 +67,9 @@ static void test_parse_http_request() {
ASSERT
(
strcmp
(
ri
.
http_headers
[
2
].
name
,
"baz
\r\n\r
"
)
==
0
);
ASSERT
(
strcmp
(
ri
.
http_headers
[
2
].
name
,
"baz
\r\n\r
"
)
==
0
);
ASSERT
(
strcmp
(
ri
.
http_headers
[
2
].
value
,
""
)
==
0
);
ASSERT
(
strcmp
(
ri
.
http_headers
[
2
].
value
,
""
)
==
0
);
// TODO(lsm): add more tests.
ASSERT
(
parse_http_message
(
req5
,
sizeof
(
req5
),
&
ri
)
==
sizeof
(
req5
)
-
1
);
ASSERT
(
strcmp
(
ri
.
request_method
,
"GET"
)
==
0
);
ASSERT
(
strcmp
(
ri
.
http_version
,
"1.1"
)
==
0
);
}
}
static
void
test_should_keep_alive
(
void
)
{
static
void
test_should_keep_alive
(
void
)
{
...
@@ -68,7 +82,8 @@ static void test_should_keep_alive(void) {
...
@@ -68,7 +82,8 @@ static void test_should_keep_alive(void) {
memset
(
&
conn
,
0
,
sizeof
(
conn
));
memset
(
&
conn
,
0
,
sizeof
(
conn
));
conn
.
ctx
=
&
ctx
;
conn
.
ctx
=
&
ctx
;
parse_http_request
(
req1
,
sizeof
(
req1
),
&
conn
.
request_info
);
ASSERT
(
parse_http_message
(
req1
,
sizeof
(
req1
),
&
conn
.
request_info
)
==
sizeof
(
req1
)
-
1
);
ctx
.
config
[
ENABLE_KEEP_ALIVE
]
=
"no"
;
ctx
.
config
[
ENABLE_KEEP_ALIVE
]
=
"no"
;
ASSERT
(
should_keep_alive
(
&
conn
)
==
0
);
ASSERT
(
should_keep_alive
(
&
conn
)
==
0
);
...
@@ -80,13 +95,13 @@ static void test_should_keep_alive(void) {
...
@@ -80,13 +95,13 @@ static void test_should_keep_alive(void) {
ASSERT
(
should_keep_alive
(
&
conn
)
==
0
);
ASSERT
(
should_keep_alive
(
&
conn
)
==
0
);
conn
.
must_close
=
0
;
conn
.
must_close
=
0
;
parse_http_
request
(
req2
,
sizeof
(
req2
),
&
conn
.
request_info
);
parse_http_
message
(
req2
,
sizeof
(
req2
),
&
conn
.
request_info
);
ASSERT
(
should_keep_alive
(
&
conn
)
==
0
);
ASSERT
(
should_keep_alive
(
&
conn
)
==
0
);
parse_http_
request
(
req3
,
sizeof
(
req3
),
&
conn
.
request_info
);
parse_http_
message
(
req3
,
sizeof
(
req3
),
&
conn
.
request_info
);
ASSERT
(
should_keep_alive
(
&
conn
)
==
0
);
ASSERT
(
should_keep_alive
(
&
conn
)
==
0
);
parse_http_
request
(
req4
,
sizeof
(
req4
),
&
conn
.
request_info
);
parse_http_
message
(
req4
,
sizeof
(
req4
),
&
conn
.
request_info
);
ASSERT
(
should_keep_alive
(
&
conn
)
==
1
);
ASSERT
(
should_keep_alive
(
&
conn
)
==
1
);
conn
.
status_code
=
401
;
conn
.
status_code
=
401
;
...
@@ -143,7 +158,9 @@ static void test_remove_double_dots() {
...
@@ -143,7 +158,9 @@ static void test_remove_double_dots() {
size_t
i
;
size_t
i
;
for
(
i
=
0
;
i
<
ARRAY_SIZE
(
data
);
i
++
)
{
for
(
i
=
0
;
i
<
ARRAY_SIZE
(
data
);
i
++
)
{
#if 0
printf("[%s] -> [%s]\n", data[i].before, data[i].after);
printf("[%s] -> [%s]\n", data[i].before, data[i].after);
#endif
remove_double_dots_and_double_slashes
(
data
[
i
].
before
);
remove_double_dots_and_double_slashes
(
data
[
i
].
before
);
ASSERT
(
strcmp
(
data
[
i
].
before
,
data
[
i
].
after
)
==
0
);
ASSERT
(
strcmp
(
data
[
i
].
before
,
data
[
i
].
after
)
==
0
);
}
}
...
@@ -162,14 +179,12 @@ static void *event_handler(enum mg_event event, struct mg_connection *conn) {
...
@@ -162,14 +179,12 @@ static void *event_handler(enum mg_event event, struct mg_connection *conn) {
return
""
;
return
""
;
}
else
if
(
event
==
MG_OPEN_FILE
)
{
}
else
if
(
event
==
MG_OPEN_FILE
)
{
const
char
*
path
=
request_info
->
ev_data
;
const
char
*
path
=
request_info
->
ev_data
;
printf
(
"%s: [%s]
\n
"
,
__func__
,
path
);
if
(
strcmp
(
path
,
"./blah"
)
==
0
)
{
if
(
strcmp
(
path
,
"./blah"
)
==
0
)
{
mg_get_request_info
(
conn
)
->
ev_data
=
mg_get_request_info
(
conn
)
->
ev_data
=
(
void
*
)
(
int
)
strlen
(
inmemory_file_data
);
(
void
*
)
(
int
)
strlen
(
inmemory_file_data
);
return
(
void
*
)
inmemory_file_data
;
return
(
void
*
)
inmemory_file_data
;
}
}
}
else
if
(
event
==
MG_EVENT_LOG
)
{
}
else
if
(
event
==
MG_EVENT_LOG
)
{
printf
(
"%s
\n
"
,
(
const
char
*
)
mg_get_request_info
(
conn
)
->
ev_data
);
}
}
return
NULL
;
return
NULL
;
...
@@ -178,6 +193,7 @@ static void *event_handler(enum mg_event event, struct mg_connection *conn) {
...
@@ -178,6 +193,7 @@ static void *event_handler(enum mg_event event, struct mg_connection *conn) {
static
const
char
*
OPTIONS
[]
=
{
static
const
char
*
OPTIONS
[]
=
{
"document_root"
,
"."
,
"document_root"
,
"."
,
"listening_ports"
,
LISTENING_ADDR
,
"listening_ports"
,
LISTENING_ADDR
,
"ssl_certificate"
,
"build/ssl_cert.pem"
,
NULL
,
NULL
,
};
};
...
@@ -187,69 +203,87 @@ static void test_mg_upload(void) {
...
@@ -187,69 +203,87 @@ static void test_mg_upload(void) {
mg_stop
(
ctx
);
mg_stop
(
ctx
);
}
}
static
void
test_mg_fetch
(
void
)
{
static
char
*
read_file
(
const
char
*
path
,
int
*
size
)
{
char
buf
[
2000
],
buf2
[
2000
];
int
n
,
length
;
struct
mg_context
*
ctx
;
struct
mg_request_info
ri
;
const
char
*
tmp_file
=
"temporary_file_name_for_unit_test.txt"
;
struct
file
file
;
FILE
*
fp
;
FILE
*
fp
;
struct
stat
st
;
char
*
data
=
NULL
;
if
((
fp
=
fopen
(
path
,
"r"
))
!=
NULL
&&
!
fstat
(
fileno
(
fp
),
&
st
))
{
*
size
=
st
.
st_size
;
ASSERT
((
data
=
malloc
(
*
size
))
!=
NULL
);
ASSERT
(
fread
(
data
,
1
,
*
size
,
fp
)
==
(
size_t
)
*
size
);
fclose
(
fp
);
}
return
data
;
}
static
char
*
read_conn
(
struct
mg_connection
*
conn
,
int
*
size
)
{
char
buf
[
100
],
*
data
=
NULL
;
int
len
;
*
size
=
0
;
while
((
len
=
mg_read
(
conn
,
buf
,
sizeof
(
buf
)))
>
0
)
{
*
size
+=
len
;
ASSERT
((
data
=
realloc
(
data
,
*
size
))
!=
NULL
);
memcpy
(
data
+
*
size
-
len
,
buf
,
len
);
}
return
data
;
}
static
void
test_mg_download
(
void
)
{
char
*
p1
,
*
p2
,
ebuf
[
100
];
int
len1
,
len2
,
port
=
atoi
(
HTTPS_PORT
);
struct
mg_connection
*
conn
;
struct
mg_context
*
ctx
;
ASSERT
((
ctx
=
mg_start
(
event_handler
,
NULL
,
OPTIONS
))
!=
NULL
);
ASSERT
((
ctx
=
mg_start
(
event_handler
,
NULL
,
OPTIONS
))
!=
NULL
);
// Failed fetch, pass invalid URL
ASSERT
(
mg_download
(
NULL
,
port
,
0
,
ebuf
,
sizeof
(
ebuf
),
""
)
==
NULL
);
ASSERT
(
mg_fetch
(
ctx
,
"localhost"
,
tmp_file
,
buf
,
sizeof
(
buf
),
&
ri
)
==
NULL
);
ASSERT
(
mg_download
(
"localhost"
,
0
,
0
,
ebuf
,
sizeof
(
ebuf
),
""
)
==
NULL
);
ASSERT
(
mg_fetch
(
ctx
,
LISTENING_ADDR
,
tmp_file
,
ASSERT
(
mg_download
(
"localhost"
,
port
,
1
,
ebuf
,
sizeof
(
ebuf
),
""
)
==
NULL
);
buf
,
sizeof
(
buf
),
&
ri
)
==
NULL
);
ASSERT
(
mg_fetch
(
ctx
,
"http://$$$.$$$"
,
tmp_file
,
// Fetch nonexistent file, should see 404
buf
,
sizeof
(
buf
),
&
ri
)
==
NULL
);
ASSERT
((
conn
=
mg_download
(
"localhost"
,
port
,
1
,
ebuf
,
sizeof
(
ebuf
),
"%s"
,
"GET /gimbec HTTP/1.0
\r\n\r\n
"
))
!=
NULL
);
// Failed fetch, pass invalid file name
ASSERT
(
strcmp
(
conn
->
request_info
.
uri
,
"404"
)
==
0
);
ASSERT
(
mg_fetch
(
ctx
,
"http://"
LISTENING_ADDR
"/data"
,
mg_close_connection
(
conn
);
"/this/file/must/not/exist/ever"
,
buf
,
sizeof
(
buf
),
&
ri
)
==
NULL
);
// Fetch mongoose.c, should succeed
ASSERT
((
conn
=
mg_download
(
"localhost"
,
port
,
1
,
ebuf
,
sizeof
(
ebuf
),
"%s"
,
// Successful fetch
"GET /mongoose.c HTTP/1.0
\r\n\r\n
"
))
!=
NULL
);
ASSERT
((
fp
=
mg_fetch
(
ctx
,
"http://"
LISTENING_ADDR
"/data"
,
ASSERT
(
!
strcmp
(
conn
->
request_info
.
uri
,
"200"
));
tmp_file
,
buf
,
sizeof
(
buf
),
&
ri
))
!=
NULL
);
ASSERT
((
p1
=
read_conn
(
conn
,
&
len1
))
!=
NULL
);
ASSERT
(
ri
.
num_headers
==
2
);
ASSERT
((
p2
=
read_file
(
"mongoose.c"
,
&
len2
))
!=
NULL
);
ASSERT
(
!
strcmp
(
ri
.
request_method
,
"HTTP/1.1"
));
ASSERT
(
len1
==
len2
);
ASSERT
(
!
strcmp
(
ri
.
uri
,
"200"
));
ASSERT
(
memcmp
(
p1
,
p2
,
len1
)
==
0
);
ASSERT
(
!
strcmp
(
ri
.
http_version
,
"OK"
));
free
(
p1
),
free
(
p2
);
ASSERT
((
length
=
ftell
(
fp
))
==
(
int
)
strlen
(
fetch_data
));
mg_close_connection
(
conn
);
fseek
(
fp
,
0
,
SEEK_SET
);
ASSERT
(
fread
(
buf2
,
1
,
length
,
fp
)
==
(
size_t
)
length
);
// Fetch in-memory file, should succeed.
ASSERT
(
memcmp
(
buf2
,
fetch_data
,
length
)
==
0
);
ASSERT
((
conn
=
mg_download
(
"localhost"
,
port
,
1
,
ebuf
,
sizeof
(
ebuf
),
"%s"
,
fclose
(
fp
);
"GET /blah HTTP/1.1
\r\n\r\n
"
))
!=
NULL
);
ASSERT
((
p1
=
read_conn
(
conn
,
&
len1
))
!=
NULL
);
// Fetch big file, mongoose.c
ASSERT
(
len1
==
(
int
)
strlen
(
inmemory_file_data
));
ASSERT
((
fp
=
mg_fetch
(
ctx
,
"http://"
LISTENING_ADDR
"/mongoose.c"
,
ASSERT
(
memcmp
(
p1
,
inmemory_file_data
,
len1
)
==
0
);
tmp_file
,
buf
,
sizeof
(
buf
),
&
ri
))
!=
NULL
);
free
(
p1
);
ASSERT
(
mg_stat
(
fc
(
ctx
),
"mongoose.c"
,
&
file
));
mg_close_connection
(
conn
);
ASSERT
(
file
.
size
==
ftell
(
fp
));
ASSERT
(
!
strcmp
(
ri
.
request_method
,
"HTTP/1.1"
));
// Test SSL redirect, IP address
ASSERT
((
conn
=
mg_download
(
"localhost"
,
atoi
(
HTTP_PORT
),
0
,
// Fetch nonexistent file, /blah
ebuf
,
sizeof
(
ebuf
),
"%s"
,
ASSERT
((
fp
=
mg_fetch
(
ctx
,
"http://"
LISTENING_ADDR
"/boo"
,
"GET /foo HTTP/1.1
\r\n\r\n
"
))
!=
NULL
);
tmp_file
,
buf
,
sizeof
(
buf
),
&
ri
))
!=
NULL
);
ASSERT
(
strcmp
(
conn
->
request_info
.
uri
,
"302"
)
==
0
);
ASSERT
(
!
mg_strcasecmp
(
ri
.
uri
,
"404"
));
ASSERT
(
strcmp
(
mg_get_header
(
conn
,
"Location"
),
fclose
(
fp
);
"https://127.0.0.1:"
HTTPS_PORT
"/foo"
)
==
0
);
mg_close_connection
(
conn
);
// Fetch existing inmemory file
ASSERT
((
fp
=
mg_fetch
(
ctx
,
"http://"
LISTENING_ADDR
"/blah"
,
// Test SSL redirect, Host:
tmp_file
,
buf
,
sizeof
(
buf
),
&
ri
))
!=
NULL
);
ASSERT
((
conn
=
mg_download
(
"localhost"
,
atoi
(
HTTP_PORT
),
0
,
ASSERT
(
!
mg_strcasecmp
(
ri
.
uri
,
"200"
));
ebuf
,
sizeof
(
ebuf
),
"%s"
,
n
=
ftell
(
fp
);
"GET /foo HTTP/1.1
\r\n
Host: a.b:77
\n\n
"
))
!=
NULL
);
fseek
(
fp
,
0
,
SEEK_SET
);
ASSERT
(
strcmp
(
conn
->
request_info
.
uri
,
"302"
)
==
0
);
printf
(
"%s %d %d [%.*s]
\n
"
,
__func__
,
n
,
(
int
)
feof
(
fp
),
n
,
buf2
);
ASSERT
(
strcmp
(
mg_get_header
(
conn
,
"Location"
),
n
=
fread
(
buf2
,
1
,
n
,
fp
);
"https://a.b:"
HTTPS_PORT
"/foo"
)
==
0
);
printf
(
"%s %d %d [%.*s]
\n
"
,
__func__
,
n
,
(
int
)
feof
(
fp
),
n
,
buf2
);
mg_close_connection
(
conn
);
ASSERT
((
size_t
)
ftell
(
fp
)
==
(
size_t
)
strlen
(
inmemory_file_data
));
ASSERT
(
!
memcmp
(
inmemory_file_data
,
buf2
,
ftell
(
fp
)));
fclose
(
fp
);
remove
(
tmp_file
);
mg_stop
(
ctx
);
mg_stop
(
ctx
);
}
}
...
@@ -261,7 +295,6 @@ static void test_base64_encode(void) {
...
@@ -261,7 +295,6 @@ static void test_base64_encode(void) {
for
(
i
=
0
;
in
[
i
]
!=
NULL
;
i
++
)
{
for
(
i
=
0
;
in
[
i
]
!=
NULL
;
i
++
)
{
base64_encode
((
unsigned
char
*
)
in
[
i
],
strlen
(
in
[
i
]),
buf
);
base64_encode
((
unsigned
char
*
)
in
[
i
],
strlen
(
in
[
i
]),
buf
);
printf
(
"[%s] [%s]
\n
"
,
out
[
i
],
buf
);
ASSERT
(
!
strcmp
(
buf
,
out
[
i
]));
ASSERT
(
!
strcmp
(
buf
,
out
[
i
]));
}
}
}
}
...
@@ -329,7 +362,9 @@ static void check_lua_expr(lua_State *L, const char *expr, const char *value) {
...
@@ -329,7 +362,9 @@ static void check_lua_expr(lua_State *L, const char *expr, const char *value) {
luaL_dostring
(
L
,
buf
);
luaL_dostring
(
L
,
buf
);
lua_getglobal
(
L
,
var_name
);
lua_getglobal
(
L
,
var_name
);
v
=
lua_tostring
(
L
,
-
1
);
v
=
lua_tostring
(
L
,
-
1
);
#if 0
printf("%s: %s: [%s] [%s]\n", __func__, expr, v == NULL ? "null" : v, value);
printf("%s: %s: [%s] [%s]\n", __func__, expr, v == NULL ? "null" : v, value);
#endif
ASSERT
((
value
==
NULL
&&
v
==
NULL
)
||
ASSERT
((
value
==
NULL
&&
v
==
NULL
)
||
(
value
!=
NULL
&&
v
!=
NULL
&&
!
strcmp
(
value
,
v
)));
(
value
!=
NULL
&&
v
!=
NULL
&&
!
strcmp
(
value
,
v
)));
}
}
...
@@ -341,13 +376,12 @@ static void test_lua(void) {
...
@@ -341,13 +376,12 @@ static void test_lua(void) {
char
http_request
[]
=
"POST /foo/bar HTTP/1.1
\r\n
"
char
http_request
[]
=
"POST /foo/bar HTTP/1.1
\r\n
"
"Content-Length: 12
\r\n
"
"Content-Length: 12
\r\n
"
"Connection: close
\r\n\r\n
hello world!"
;
"Connection: close
\r\n\r\n
hello world!"
;
const
char
*
page
=
"<? print('hi') ?>"
;
lua_State
*
L
=
luaL_newstate
();
lua_State
*
L
=
luaL_newstate
();
conn
.
ctx
=
&
ctx
;
conn
.
ctx
=
&
ctx
;
conn
.
buf
=
http_request
;
conn
.
buf
=
http_request
;
conn
.
buf_size
=
conn
.
data_len
=
strlen
(
http_request
);
conn
.
buf_size
=
conn
.
data_len
=
strlen
(
http_request
);
conn
.
request_len
=
parse_http_
request
(
conn
.
buf
,
conn
.
data_len
,
conn
.
request_len
=
parse_http_
message
(
conn
.
buf
,
conn
.
data_len
,
&
conn
.
request_info
);
&
conn
.
request_info
);
conn
.
content_len
=
conn
.
data_len
-
conn
.
request_len
;
conn
.
content_len
=
conn
.
data_len
-
conn
.
request_len
;
...
@@ -371,7 +405,7 @@ static void test_lua(void) {
...
@@ -371,7 +405,7 @@ static void test_lua(void) {
static
void
*
user_data_tester
(
enum
mg_event
event
,
struct
mg_connection
*
conn
)
{
static
void
*
user_data_tester
(
enum
mg_event
event
,
struct
mg_connection
*
conn
)
{
struct
mg_request_info
*
ri
=
mg_get_request_info
(
conn
);
struct
mg_request_info
*
ri
=
mg_get_request_info
(
conn
);
ASSERT
(
ri
->
user_data
==
(
void
*
)
123
);
ASSERT
(
ri
->
user_data
==
(
void
*
)
123
);
ASSERT
(
event
==
MG_NEW_REQUEST
);
ASSERT
(
event
==
MG_NEW_REQUEST
||
event
==
MG_INIT_SSL
);
return
NULL
;
return
NULL
;
}
}
...
@@ -413,8 +447,8 @@ int __cdecl main(void) {
...
@@ -413,8 +447,8 @@ int __cdecl main(void) {
test_match_prefix
();
test_match_prefix
();
test_remove_double_dots
();
test_remove_double_dots
();
test_should_keep_alive
();
test_should_keep_alive
();
test_parse_http_
request
();
test_parse_http_
message
();
test_mg_
fetch
();
test_mg_
download
();
test_mg_get_var
();
test_mg_get_var
();
test_set_throttle
();
test_set_throttle
();
test_next_option
();
test_next_option
();
...
@@ -425,5 +459,6 @@ int __cdecl main(void) {
...
@@ -425,5 +459,6 @@ int __cdecl main(void) {
test_lua
();
test_lua
();
#endif
#endif
test_skip_quoted
();
test_skip_quoted
();
printf
(
"%s
\n
"
,
"PASSED"
);
return
0
;
return
0
;
}
}
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