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
f9873eb8
Commit
f9873eb8
authored
Sep 19, 2012
by
Sergey Lyubka
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Experimentl websocket support
parent
358e4ffb
Changes
4
Hide whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
266 additions
and
4 deletions
+266
-4
Makefile
examples/Makefile
+1
-0
mongoose.c
mongoose.c
+241
-0
mongoose.h
mongoose.h
+10
-4
unit_test.c
test/unit_test.c
+14
-0
No files found.
examples/Makefile
View file @
f9873eb8
...
@@ -6,4 +6,5 @@ all:
...
@@ -6,4 +6,5 @@ all:
$(CC)
$(CFLAGS)
hello.c ../mongoose.c
$$
LIBS
$(ADD)
-o
hello
;
$(CC)
$(CFLAGS)
hello.c ../mongoose.c
$$
LIBS
$(ADD)
-o
hello
;
$(CC)
$(CFLAGS)
upload.c ../mongoose.c
$$
LIBS
$(ADD)
-o
upload
;
$(CC)
$(CFLAGS)
upload.c ../mongoose.c
$$
LIBS
$(ADD)
-o
upload
;
$(CC)
$(CFLAGS)
post.c ../mongoose.c
$$
LIBS
$(ADD)
-o
post
;
$(CC)
$(CFLAGS)
post.c ../mongoose.c
$$
LIBS
$(ADD)
-o
post
;
$(CC)
$(CFLAGS)
-DUSE_WEBSOCKET
websocket.c ../mongoose.c
$$
LIBS
$(ADD)
-o
websocket
;
$(CC)
$(CFLAGS)
chat.c ../mongoose.c
$$
LIBS
$(ADD)
-o
chat
$(CC)
$(CFLAGS)
chat.c ../mongoose.c
$$
LIBS
$(ADD)
-o
chat
mongoose.c
View file @
f9873eb8
...
@@ -3494,6 +3494,243 @@ static void handle_propfind(struct mg_connection *conn, const char* path,
...
@@ -3494,6 +3494,243 @@ static void handle_propfind(struct mg_connection *conn, const char* path,
conn
->
num_bytes_sent
+=
mg_printf
(
conn
,
"%s
\n
"
,
"</d:multistatus>"
);
conn
->
num_bytes_sent
+=
mg_printf
(
conn
,
"%s
\n
"
,
"</d:multistatus>"
);
}
}
#if defined(USE_WEBSOCKET)
// START OF SHA-1 code
// Copyright(c) By Steve Reid <steve@edmweb.com>
#define SHA1HANDSOFF
#if defined(__sun)
#include "solarisfixes.h"
#endif
#define rol(value, bits) (((value) << (bits)) | ((value) >> (32 - (bits))))
#if BYTE_ORDER == LITTLE_ENDIAN
#define blk0(i) (block->l[i] = (rol(block->l[i],24)&0xFF00FF00) \
|
(
rol
(
block
->
l
[
i
],
8
)
&
0x00FF00FF
))
#elif BYTE_ORDER == BIG_ENDIAN
#define blk0(i) block->l[i]
#else
#error "Endianness not defined!"
#endif
#define blk(i) (block->l[i&15] = rol(block->l[(i+13)&15]^block->l[(i+8)&15] \
^
block
->
l
[(
i
+
2
)
&
15
]
^
block
->
l
[
i
&
15
],
1
))
#define R0(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk0(i)+0x5A827999+rol(v,5);w=rol(w,30);
#define R1(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk(i)+0x5A827999+rol(v,5);w=rol(w,30);
#define R2(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0x6ED9EBA1+rol(v,5);w=rol(w,30);
#define R3(v,w,x,y,z,i) z+=(((w|x)&y)|(w&x))+blk(i)+0x8F1BBCDC+rol(v,5);w=rol(w,30);
#define R4(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0xCA62C1D6+rol(v,5);w=rol(w,30);
typedef
struct
{
uint32_t
state
[
5
];
uint32_t
count
[
2
];
unsigned
char
buffer
[
64
];
}
SHA1_CTX
;
static
void
SHA1Transform
(
uint32_t
state
[
5
],
const
unsigned
char
buffer
[
64
])
{
uint32_t
a
,
b
,
c
,
d
,
e
;
typedef
union
{
unsigned
char
c
[
64
];
uint32_t
l
[
16
];
}
CHAR64LONG16
;
CHAR64LONG16
block
[
1
];
memcpy
(
block
,
buffer
,
64
);
a
=
state
[
0
];
b
=
state
[
1
];
c
=
state
[
2
];
d
=
state
[
3
];
e
=
state
[
4
];
R0
(
a
,
b
,
c
,
d
,
e
,
0
);
R0
(
e
,
a
,
b
,
c
,
d
,
1
);
R0
(
d
,
e
,
a
,
b
,
c
,
2
);
R0
(
c
,
d
,
e
,
a
,
b
,
3
);
R0
(
b
,
c
,
d
,
e
,
a
,
4
);
R0
(
a
,
b
,
c
,
d
,
e
,
5
);
R0
(
e
,
a
,
b
,
c
,
d
,
6
);
R0
(
d
,
e
,
a
,
b
,
c
,
7
);
R0
(
c
,
d
,
e
,
a
,
b
,
8
);
R0
(
b
,
c
,
d
,
e
,
a
,
9
);
R0
(
a
,
b
,
c
,
d
,
e
,
10
);
R0
(
e
,
a
,
b
,
c
,
d
,
11
);
R0
(
d
,
e
,
a
,
b
,
c
,
12
);
R0
(
c
,
d
,
e
,
a
,
b
,
13
);
R0
(
b
,
c
,
d
,
e
,
a
,
14
);
R0
(
a
,
b
,
c
,
d
,
e
,
15
);
R1
(
e
,
a
,
b
,
c
,
d
,
16
);
R1
(
d
,
e
,
a
,
b
,
c
,
17
);
R1
(
c
,
d
,
e
,
a
,
b
,
18
);
R1
(
b
,
c
,
d
,
e
,
a
,
19
);
R2
(
a
,
b
,
c
,
d
,
e
,
20
);
R2
(
e
,
a
,
b
,
c
,
d
,
21
);
R2
(
d
,
e
,
a
,
b
,
c
,
22
);
R2
(
c
,
d
,
e
,
a
,
b
,
23
);
R2
(
b
,
c
,
d
,
e
,
a
,
24
);
R2
(
a
,
b
,
c
,
d
,
e
,
25
);
R2
(
e
,
a
,
b
,
c
,
d
,
26
);
R2
(
d
,
e
,
a
,
b
,
c
,
27
);
R2
(
c
,
d
,
e
,
a
,
b
,
28
);
R2
(
b
,
c
,
d
,
e
,
a
,
29
);
R2
(
a
,
b
,
c
,
d
,
e
,
30
);
R2
(
e
,
a
,
b
,
c
,
d
,
31
);
R2
(
d
,
e
,
a
,
b
,
c
,
32
);
R2
(
c
,
d
,
e
,
a
,
b
,
33
);
R2
(
b
,
c
,
d
,
e
,
a
,
34
);
R2
(
a
,
b
,
c
,
d
,
e
,
35
);
R2
(
e
,
a
,
b
,
c
,
d
,
36
);
R2
(
d
,
e
,
a
,
b
,
c
,
37
);
R2
(
c
,
d
,
e
,
a
,
b
,
38
);
R2
(
b
,
c
,
d
,
e
,
a
,
39
);
R3
(
a
,
b
,
c
,
d
,
e
,
40
);
R3
(
e
,
a
,
b
,
c
,
d
,
41
);
R3
(
d
,
e
,
a
,
b
,
c
,
42
);
R3
(
c
,
d
,
e
,
a
,
b
,
43
);
R3
(
b
,
c
,
d
,
e
,
a
,
44
);
R3
(
a
,
b
,
c
,
d
,
e
,
45
);
R3
(
e
,
a
,
b
,
c
,
d
,
46
);
R3
(
d
,
e
,
a
,
b
,
c
,
47
);
R3
(
c
,
d
,
e
,
a
,
b
,
48
);
R3
(
b
,
c
,
d
,
e
,
a
,
49
);
R3
(
a
,
b
,
c
,
d
,
e
,
50
);
R3
(
e
,
a
,
b
,
c
,
d
,
51
);
R3
(
d
,
e
,
a
,
b
,
c
,
52
);
R3
(
c
,
d
,
e
,
a
,
b
,
53
);
R3
(
b
,
c
,
d
,
e
,
a
,
54
);
R3
(
a
,
b
,
c
,
d
,
e
,
55
);
R3
(
e
,
a
,
b
,
c
,
d
,
56
);
R3
(
d
,
e
,
a
,
b
,
c
,
57
);
R3
(
c
,
d
,
e
,
a
,
b
,
58
);
R3
(
b
,
c
,
d
,
e
,
a
,
59
);
R4
(
a
,
b
,
c
,
d
,
e
,
60
);
R4
(
e
,
a
,
b
,
c
,
d
,
61
);
R4
(
d
,
e
,
a
,
b
,
c
,
62
);
R4
(
c
,
d
,
e
,
a
,
b
,
63
);
R4
(
b
,
c
,
d
,
e
,
a
,
64
);
R4
(
a
,
b
,
c
,
d
,
e
,
65
);
R4
(
e
,
a
,
b
,
c
,
d
,
66
);
R4
(
d
,
e
,
a
,
b
,
c
,
67
);
R4
(
c
,
d
,
e
,
a
,
b
,
68
);
R4
(
b
,
c
,
d
,
e
,
a
,
69
);
R4
(
a
,
b
,
c
,
d
,
e
,
70
);
R4
(
e
,
a
,
b
,
c
,
d
,
71
);
R4
(
d
,
e
,
a
,
b
,
c
,
72
);
R4
(
c
,
d
,
e
,
a
,
b
,
73
);
R4
(
b
,
c
,
d
,
e
,
a
,
74
);
R4
(
a
,
b
,
c
,
d
,
e
,
75
);
R4
(
e
,
a
,
b
,
c
,
d
,
76
);
R4
(
d
,
e
,
a
,
b
,
c
,
77
);
R4
(
c
,
d
,
e
,
a
,
b
,
78
);
R4
(
b
,
c
,
d
,
e
,
a
,
79
);
state
[
0
]
+=
a
;
state
[
1
]
+=
b
;
state
[
2
]
+=
c
;
state
[
3
]
+=
d
;
state
[
4
]
+=
e
;
a
=
b
=
c
=
d
=
e
=
0
;
memset
(
block
,
'\0'
,
sizeof
(
block
));
}
static
void
SHA1Init
(
SHA1_CTX
*
context
)
{
context
->
state
[
0
]
=
0x67452301
;
context
->
state
[
1
]
=
0xEFCDAB89
;
context
->
state
[
2
]
=
0x98BADCFE
;
context
->
state
[
3
]
=
0x10325476
;
context
->
state
[
4
]
=
0xC3D2E1F0
;
context
->
count
[
0
]
=
context
->
count
[
1
]
=
0
;
}
static
void
SHA1Update
(
SHA1_CTX
*
context
,
const
unsigned
char
*
data
,
uint32_t
len
)
{
uint32_t
i
,
j
;
j
=
context
->
count
[
0
];
if
((
context
->
count
[
0
]
+=
len
<<
3
)
<
j
)
context
->
count
[
1
]
++
;
context
->
count
[
1
]
+=
(
len
>>
29
);
j
=
(
j
>>
3
)
&
63
;
if
((
j
+
len
)
>
63
)
{
memcpy
(
&
context
->
buffer
[
j
],
data
,
(
i
=
64
-
j
));
SHA1Transform
(
context
->
state
,
context
->
buffer
);
for
(
;
i
+
63
<
len
;
i
+=
64
)
{
SHA1Transform
(
context
->
state
,
&
data
[
i
]);
}
j
=
0
;
}
else
i
=
0
;
memcpy
(
&
context
->
buffer
[
j
],
&
data
[
i
],
len
-
i
);
}
static
void
SHA1Final
(
unsigned
char
digest
[
20
],
SHA1_CTX
*
context
)
{
unsigned
i
;
unsigned
char
finalcount
[
8
],
c
;
for
(
i
=
0
;
i
<
8
;
i
++
)
{
finalcount
[
i
]
=
(
unsigned
char
)((
context
->
count
[(
i
>=
4
?
0
:
1
)]
>>
((
3
-
(
i
&
3
))
*
8
)
)
&
255
);
}
c
=
0200
;
SHA1Update
(
context
,
&
c
,
1
);
while
((
context
->
count
[
0
]
&
504
)
!=
448
)
{
c
=
0000
;
SHA1Update
(
context
,
&
c
,
1
);
}
SHA1Update
(
context
,
finalcount
,
8
);
for
(
i
=
0
;
i
<
20
;
i
++
)
{
digest
[
i
]
=
(
unsigned
char
)
((
context
->
state
[
i
>>
2
]
>>
((
3
-
(
i
&
3
))
*
8
)
)
&
255
);
}
memset
(
context
,
'\0'
,
sizeof
(
*
context
));
memset
(
&
finalcount
,
'\0'
,
sizeof
(
finalcount
));
}
// END OF SHA1 CODE
static
void
base64_encode
(
const
unsigned
char
*
src
,
int
src_len
,
char
*
dst
)
{
static
const
char
*
b64
=
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
;
int
i
,
j
,
a
,
b
,
c
;
for
(
i
=
j
=
0
;
i
<
src_len
;
i
+=
3
)
{
a
=
src
[
i
];
b
=
i
+
1
>=
src_len
?
0
:
src
[
i
+
1
];
c
=
i
+
2
>=
src_len
?
0
:
src
[
i
+
2
];
dst
[
j
++
]
=
b64
[
a
>>
2
];
dst
[
j
++
]
=
b64
[((
a
&
3
)
<<
4
)
|
(
b
>>
4
)];
if
(
i
+
1
<
src_len
)
{
dst
[
j
++
]
=
b64
[(
b
&
15
)
<<
2
|
(
c
>>
6
)];
}
if
(
i
+
2
<
src_len
)
{
dst
[
j
++
]
=
b64
[
c
&
63
];
}
}
while
(
j
%
4
!=
0
)
{
dst
[
j
++
]
=
'='
;
}
dst
[
j
++
]
=
'\0'
;
}
static
void
send_websocket_handshake
(
struct
mg_connection
*
conn
)
{
static
const
char
*
magic
=
"258EAFA5-E914-47DA-95CA-C5AB0DC85B11"
;
char
buf
[
100
],
sha
[
20
],
b64_sha
[
sizeof
(
sha
)
*
2
];
SHA1_CTX
sha_ctx
;
mg_snprintf
(
conn
,
buf
,
sizeof
(
buf
),
"%s%s"
,
mg_get_header
(
conn
,
"Sec-WebSocket-Key"
),
magic
);
SHA1Init
(
&
sha_ctx
);
SHA1Update
(
&
sha_ctx
,
(
unsigned
char
*
)
buf
,
strlen
(
buf
));
SHA1Final
((
unsigned
char
*
)
sha
,
&
sha_ctx
);
base64_encode
((
unsigned
char
*
)
sha
,
sizeof
(
sha
),
b64_sha
);
mg_printf
(
conn
,
"%s%s%s"
,
"HTTP/1.1 101 Switching Protocols
\r\n
"
"Upgrade: websocket
\r\n
"
"Connection: Upgrade
\r\n
"
"Sec-WebSocket-Accept: "
,
b64_sha
,
"
\r\n\r\n
"
);
}
static
void
read_websocket
(
struct
mg_connection
*
conn
)
{
unsigned
char
*
mask
,
*
buf
=
(
unsigned
char
*
)
conn
->
body
;
int
n
,
len
,
mask_len
,
body_len
;
for
(;;)
{
if
((
body_len
=
conn
->
data_len
-
conn
->
request_len
)
>=
2
)
{
len
=
buf
[
1
]
&
127
;
mask_len
=
buf
[
1
]
&
128
?
4
:
0
;
if
(
len
<
126
)
{
conn
->
content_len
=
2
+
mask_len
+
len
;
mask
=
buf
+
2
;
}
else
if
(
len
==
126
&&
body_len
>=
4
)
{
conn
->
content_len
=
2
+
mask_len
+
((((
int
)
buf
[
2
])
<<
8
)
+
buf
[
3
]);
mask
=
buf
+
4
;
}
else
if
(
body_len
>=
10
)
{
conn
->
content_len
=
2
+
mask_len
+
((
uint64_t
)
htonl
(
*
(
uint32_t
*
)
&
buf
[
2
]))
<<
32
|
htonl
(
*
(
uint32_t
*
)
&
buf
[
6
]);
mask
=
buf
+
10
;
}
}
if
(
conn
->
content_len
>
0
)
{
conn
->
next_request
=
conn
->
buf
+
conn
->
data_len
;
if
(
call_user
(
conn
,
MG_WEBSOCKET_MESSAGE
)
!=
NULL
)
{
break
;
// Callback signalled to exit
}
}
else
{
if
(
wait_until_socket_is_readable
(
conn
)
==
0
)
{
break
;
}
n
=
pull
(
NULL
,
conn
,
conn
->
buf
+
conn
->
data_len
,
conn
->
buf_size
-
conn
->
data_len
);
if
(
n
<=
0
)
{
break
;
}
conn
->
data_len
+=
n
;
}
}
}
static
void
handle_websocket_request
(
struct
mg_connection
*
conn
)
{
if
(
strcmp
(
mg_get_header
(
conn
,
"Sec-WebSocket-Version"
),
"13"
)
!=
0
)
{
send_http_error
(
conn
,
426
,
"Upgrade Required"
,
"%s"
,
"Upgrade Required"
);
}
else
if
(
call_user
(
conn
,
MG_WEBSOCKET_CONNECT
)
!=
NULL
)
{
// Callback has returned non-NULL, do not proceed with handshake
}
else
{
send_websocket_handshake
(
conn
);
call_user
(
conn
,
MG_WEBSOCKET_READY
);
read_websocket
(
conn
);
}
}
static
int
is_websocket_request
(
const
struct
mg_connection
*
conn
)
{
const
char
*
host
,
*
upgrade
,
*
connection
,
*
version
,
*
key
;
host
=
mg_get_header
(
conn
,
"Host"
);
upgrade
=
mg_get_header
(
conn
,
"Upgrade"
);
connection
=
mg_get_header
(
conn
,
"Connection"
);
key
=
mg_get_header
(
conn
,
"Sec-WebSocket-Key"
);
version
=
mg_get_header
(
conn
,
"Sec-WebSocket-Version"
);
return
host
!=
NULL
&&
upgrade
!=
NULL
&&
connection
!=
NULL
&&
key
!=
NULL
&&
version
!=
NULL
&&
!
mg_strcasecmp
(
upgrade
,
"websocket"
)
&&
!
mg_strcasecmp
(
connection
,
"Upgrade"
);
}
#endif // !USE_WEBSOCKET
// This is the heart of the Mongoose's logic.
// This is the heart of the Mongoose's logic.
// This function is called when the request is read, parsed and validated,
// This function is called when the request is read, parsed and validated,
// and Mongoose must decide what action to take: serve a file, or
// and Mongoose must decide what action to take: serve a file, or
...
@@ -3515,6 +3752,10 @@ static void handle_request(struct mg_connection *conn) {
...
@@ -3515,6 +3752,10 @@ static void handle_request(struct mg_connection *conn) {
DEBUG_TRACE
((
"%s"
,
ri
->
uri
));
DEBUG_TRACE
((
"%s"
,
ri
->
uri
));
if
(
!
check_authorization
(
conn
,
path
))
{
if
(
!
check_authorization
(
conn
,
path
))
{
send_authorization_request
(
conn
);
send_authorization_request
(
conn
);
#if defined(USE_WEBSOCKET)
}
else
if
(
is_websocket_request
(
conn
))
{
handle_websocket_request
(
conn
);
#endif
}
else
if
(
call_user
(
conn
,
MG_NEW_REQUEST
)
!=
NULL
)
{
}
else
if
(
call_user
(
conn
,
MG_NEW_REQUEST
)
!=
NULL
)
{
// Do nothing, callback has served the request
// Do nothing, callback has served the request
}
else
if
(
!
strcmp
(
ri
->
request_method
,
"OPTIONS"
))
{
}
else
if
(
!
strcmp
(
ri
->
request_method
,
"OPTIONS"
))
{
...
...
mongoose.h
View file @
f9873eb8
...
@@ -49,16 +49,23 @@ struct mg_request_info {
...
@@ -49,16 +49,23 @@ struct mg_request_info {
}
http_headers
[
64
];
// Maximum 64 headers
}
http_headers
[
64
];
// Maximum 64 headers
};
};
// Various events on which user-defined function is called by Mongoose.
// Various events on which user-defined function is called by Mongoose.
enum
mg_event
{
enum
mg_event
{
MG_NEW_REQUEST
,
// New HTTP request has arrived from the client
MG_NEW_REQUEST
,
// New HTTP request has arrived from the client
MG_REQUEST_COMPLETE
,
// Mongoose has finished handling the request
MG_REQUEST_COMPLETE
,
// Mongoose has finished handling the request
MG_HTTP_ERROR
,
// HTTP error must be returned to the client
MG_HTTP_ERROR
,
// HTTP error must be returned to the client
MG_EVENT_LOG
,
// Mongoose logs an event, request_info.log_message
MG_EVENT_LOG
,
// Mongoose logs an event, request_info.log_message
MG_INIT_SSL
// Mongoose initializes SSL. Instead of mg_connection *,
MG_INIT_SSL
,
// SSL initialization, sent before certificate setup
// SSL context is passed to the callback function.
MG_WEBSOCKET_CONNECT
,
// Sent on HTTP connect, before websocket handshake.
// If user callback returns NULL, then mongoose proceeds
// with handshake, otherwise it closes the connection.
MG_WEBSOCKET_READY
,
// Handshake has been successfully completed.
MG_WEBSOCKET_MESSAGE
,
// Incoming message from the client
MG_WEBSOCKET_CLOSE
,
// Client has sent FIN frame
};
};
// Prototype for the user-defined function. Mongoose calls this function
// Prototype for the user-defined function. Mongoose calls this function
// on every MG_* event.
// on every MG_* event.
//
//
...
@@ -74,8 +81,7 @@ enum mg_event {
...
@@ -74,8 +81,7 @@ enum mg_event {
// If handler returns NULL, that means that handler has not processed
// If handler returns NULL, that means that handler has not processed
// the request. Handler must not send any data to the client in this case.
// the request. Handler must not send any data to the client in this case.
// Mongoose proceeds with request handling as if nothing happened.
// Mongoose proceeds with request handling as if nothing happened.
typedef
void
*
(
*
mg_callback_t
)(
enum
mg_event
event
,
typedef
void
*
(
*
mg_callback_t
)(
enum
mg_event
event
,
struct
mg_connection
*
conn
);
struct
mg_connection
*
conn
);
// Start web server.
// Start web server.
...
...
test/unit_test.c
View file @
f9873eb8
...
@@ -196,7 +196,21 @@ static void test_mg_fetch(void) {
...
@@ -196,7 +196,21 @@ static void test_mg_fetch(void) {
mg_stop
(
ctx
);
mg_stop
(
ctx
);
}
}
static
void
test_base64_encode
(
void
)
{
const
char
*
in
[]
=
{
"a"
,
"ab"
,
"abc"
,
"abcd"
,
NULL
};
const
char
*
out
[]
=
{
"YQ=="
,
"YWI="
,
"YWJj"
,
"YWJjZA=="
};
char
buf
[
100
];
int
i
;
for
(
i
=
0
;
in
[
i
]
!=
NULL
;
i
++
)
{
base64_encode
((
unsigned
char
*
)
in
[
i
],
strlen
(
in
[
i
]),
buf
);
printf
(
"[%s] [%s]
\n
"
,
out
[
i
],
buf
);
ASSERT
(
!
strcmp
(
buf
,
out
[
i
]));
}
}
int
main
(
void
)
{
int
main
(
void
)
{
test_base64_encode
();
test_match_prefix
();
test_match_prefix
();
test_remove_double_dots
();
test_remove_double_dots
();
test_should_keep_alive
();
test_should_keep_alive
();
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment