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
0d442058
Commit
0d442058
authored
Dec 07, 2012
by
Sergey Lyubka
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Added mg_upload()
parent
1ef31461
Changes
3
Hide whitespace changes
Inline
Side-by-side
Showing
3 changed files
with
127 additions
and
92 deletions
+127
-92
upload.c
examples/upload.c
+8
-91
mongoose.c
mongoose.c
+108
-1
mongoose.h
mongoose.h
+11
-0
No files found.
examples/upload.c
View file @
0d442058
...
...
@@ -26,97 +26,11 @@ static const char *html_form =
"<input type=
\"
submit
\"
value=
\"
Upload
\"
/>"
"</form></body></html>"
;
static
const
char
*
HTTP_500
=
"HTTP/1.0 500 Server Error
\r\n\r\n
"
;
static
void
handle_file_upload
(
struct
mg_connection
*
conn
)
{
const
char
*
cl_header
;
char
post_data
[
16
*
1024
],
path
[
999
],
file_name
[
1024
],
mime_type
[
100
],
buf
[
BUFSIZ
],
*
eop
,
*
s
,
*
p
;
FILE
*
fp
;
int64_t
cl
,
written
;
int
fd
,
n
,
post_data_len
;
// Figure out total content length. Return if it is not present or invalid.
cl_header
=
mg_get_header
(
conn
,
"Content-Length"
);
if
(
cl_header
==
NULL
||
(
cl
=
strtoll
(
cl_header
,
NULL
,
10
))
<=
0
)
{
mg_printf
(
conn
,
"%s%s"
,
HTTP_500
,
"Invalid Conent-Length"
);
return
;
}
// Read the initial chunk into memory. This should be multipart POST data.
// Parse headers, where we should find file name and content-type.
post_data_len
=
mg_read
(
conn
,
post_data
,
sizeof
(
post_data
));
file_name
[
0
]
=
mime_type
[
0
]
=
'\0'
;
for
(
s
=
p
=
post_data
;
p
<
&
post_data
[
post_data_len
];
p
++
)
{
if
(
p
[
0
]
==
'\r'
&&
p
[
1
]
==
'\n'
)
{
if
(
s
==
p
)
{
p
+=
2
;
break
;
// End of headers
}
p
[
0
]
=
p
[
1
]
=
'\0'
;
sscanf
(
s
,
"Content-Type: %99s"
,
mime_type
);
// TODO(lsm): don't expect filename to be the 3rd field,
// parse the header properly instead.
sscanf
(
s
,
"Content-Disposition: %*s %*s filename=
\"
%1023[^
\"
]"
,
file_name
);
s
=
p
+
2
;
}
}
// Finished parsing headers. Now "p" points to the first byte of data.
// Calculate file size
cl
-=
p
-
post_data
;
// Subtract headers size
cl
-=
strlen
(
post_data
);
// Subtract the boundary marker at the end
cl
-=
6
;
// Subtract "\r\n" before and after boundary
// Construct destination file name. Write to /tmp, do not allow
// paths that contain slashes.
if
((
s
=
strrchr
(
file_name
,
'/'
))
==
NULL
)
{
s
=
file_name
;
}
snprintf
(
path
,
sizeof
(
path
),
"/tmp/%s"
,
s
);
if
(
file_name
[
0
]
==
'\0'
)
{
mg_printf
(
conn
,
"%s%s"
,
HTTP_500
,
"Can't get file name"
);
}
else
if
(
cl
<=
0
)
{
mg_printf
(
conn
,
"%s%s"
,
HTTP_500
,
"Empty file"
);
}
else
if
((
fd
=
open
(
path
,
O_CREAT
|
O_TRUNC
|
#ifdef _WIN32
O_BINARY
|
#else
O_EXLOCK
|
O_CLOEXEC
|
#endif
O_WRONLY
))
<
0
)
{
// We're opening the file with exclusive lock held. This guarantee us that
// there is no other thread can save into the same file simultaneously.
mg_printf
(
conn
,
"%s%s"
,
HTTP_500
,
"Cannot open file"
);
}
else
if
((
fp
=
fdopen
(
fd
,
"w"
))
==
NULL
)
{
mg_printf
(
conn
,
"%s%s"
,
HTTP_500
,
"Cannot reopen file stream"
);
close
(
fd
);
}
else
{
// Success. Write data into the file.
eop
=
post_data
+
post_data_len
;
n
=
p
+
cl
>
eop
?
(
int
)
(
eop
-
p
)
:
(
int
)
cl
;
(
void
)
fwrite
(
p
,
1
,
n
,
fp
);
written
=
n
;
while
(
written
<
cl
&&
(
n
=
mg_read
(
conn
,
buf
,
cl
-
written
>
(
int64_t
)
sizeof
(
buf
)
?
sizeof
(
buf
)
:
cl
-
written
))
>
0
)
{
(
void
)
fwrite
(
buf
,
1
,
n
,
fp
);
written
+=
n
;
}
(
void
)
fclose
(
fp
);
mg_printf
(
conn
,
"HTTP/1.0 200 OK
\r\n\r\n
"
"Saved to [%s], written %llu bytes"
,
path
,
cl
);
}
}
static
void
*
callback
(
enum
mg_event
event
,
struct
mg_connection
*
conn
)
{
const
struct
mg_request_info
*
ri
=
mg_get_request_info
(
conn
);
if
(
event
==
MG_NEW_REQUEST
)
{
if
(
!
strcmp
(
ri
->
uri
,
"/handle_post_request"
))
{
handle_file_upload
(
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.
mg_printf
(
conn
,
"HTTP/1.0 200 OK
\r\n
"
...
...
@@ -126,9 +40,11 @@ static void *callback(enum mg_event event, struct mg_connection *conn) {
}
// Mark as processed
return
""
;
}
else
{
return
NULL
;
}
else
if
(
event
==
MG_UPLOAD
)
{
mg_printf
(
conn
,
"Saved [%s]"
,
mg_get_request_info
(
conn
)
->
ev_data
)
;
}
return
NULL
;
}
int
main
(
void
)
{
...
...
@@ -137,6 +53,7 @@ int main(void) {
ctx
=
mg_start
(
&
callback
,
NULL
,
options
);
getchar
();
// Wait until user hits "enter"
pause
();
mg_stop
(
ctx
);
return
0
;
...
...
mongoose.c
View file @
0d442058
...
...
@@ -747,7 +747,7 @@ static int mg_snprintf(struct mg_connection *conn, char *buf, size_t buflen,
}
// Skip the characters until one of the delimiters characters found.
// 0-terminate resulting word. Skip the delimiter and following whitespaces
if any
.
// 0-terminate resulting word. Skip the delimiter and following whitespaces.
// Advance pointer to buffer to the next word. Return found 0-terminated word.
// Delimiters can be quoted with quotechar.
static
char
*
skip_quoted
(
char
**
buf
,
const
char
*
delimiters
,
...
...
@@ -4035,6 +4035,113 @@ static void handle_lsp_request(struct mg_connection *conn, const char *path,
}
#endif // USE_LUA
int
mg_upload
(
struct
mg_connection
*
conn
,
const
char
*
destination_dir
)
{
const
char
*
content_type_header
,
*
boundary_start
;
char
buf
[
8192
],
path
[
PATH_MAX
],
fname
[
1024
],
boundary
[
100
],
*
s
;
FILE
*
fp
;
int
bl
,
n
,
i
,
j
,
headers_len
,
boundary_len
,
len
=
0
,
num_uploaded_files
=
0
;
// Request looks like this:
//
// POST /upload HTTP/1.1
// Host: 127.0.0.1:8080
// Content-Length: 244894
// Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryRVr
//
// ------WebKitFormBoundaryRVr
// Content-Disposition: form-data; name="file"; filename="accum.png"
// Content-Type: image/png
//
// <89>PNG
// <PNG DATA>
// ------WebKitFormBoundaryRVr
// Extract boundary string from the Content-Type header
if
((
content_type_header
=
mg_get_header
(
conn
,
"Content-Type"
))
==
NULL
||
(
boundary_start
=
strstr
(
content_type_header
,
"boundary="
))
==
NULL
||
(
sscanf
(
boundary_start
,
"boundary=
\"
%99[^
\"
]
\"
"
,
boundary
)
==
0
&&
sscanf
(
boundary_start
,
"boundary=%99s"
,
boundary
)
==
0
)
||
boundary
[
0
]
==
'\0'
)
{
return
num_uploaded_files
;
}
boundary_len
=
strlen
(
boundary
);
bl
=
boundary_len
+
4
;
// \r\n--<boundary>
for
(;;)
{
// Pull in headers
assert
(
len
>=
0
&&
len
<=
(
int
)
sizeof
(
buf
));
while
((
n
=
mg_read
(
conn
,
buf
+
len
,
sizeof
(
buf
)
-
len
))
>
0
)
{
len
+=
n
;
}
if
((
headers_len
=
get_request_len
(
buf
,
len
))
<=
0
)
{
break
;
}
// Fetch file name.
fname
[
0
]
=
'\0'
;
for
(
i
=
j
=
0
;
i
<
headers_len
;
i
++
)
{
if
(
buf
[
i
]
==
'\r'
&&
buf
[
i
+
1
]
==
'\n'
)
{
buf
[
i
]
=
buf
[
i
+
1
]
=
'\0'
;
// TODO(lsm): don't expect filename to be the 3rd field,
// parse the header properly instead.
sscanf
(
&
buf
[
j
],
"Content-Disposition: %*s %*s filename=
\"
%1023[^
\"
]"
,
fname
);
j
=
i
+
2
;
}
}
// Give up if the headers are not what we expect
if
(
fname
[
0
]
==
'\0'
)
{
break
;
}
// Move data to the beginning of the buffer
assert
(
len
>=
headers_len
);
memmove
(
buf
,
&
buf
[
headers_len
],
len
-
headers_len
);
len
-=
headers_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
=
fname
;
}
// Open file in binary mode with exclusive lock set
snprintf
(
path
,
sizeof
(
path
),
"%s/%s"
,
destination_dir
,
s
);
if
((
fp
=
fopen
(
path
,
"wbx"
))
==
NULL
)
{
break
;
}
// Read POST data, write into file until boundary is found.
n
=
0
;
do
{
len
+=
n
;
for
(
i
=
0
;
i
<
len
-
bl
;
i
++
)
{
if
(
!
memcmp
(
&
buf
[
i
],
"
\r\n
--"
,
4
)
&&
!
memcmp
(
&
buf
[
i
+
4
],
boundary
,
boundary_len
))
{
// Found boundary, that's the end of file data.
(
void
)
fwrite
(
buf
,
1
,
i
,
fp
);
num_uploaded_files
++
;
conn
->
request_info
.
ev_data
=
(
void
*
)
path
;
call_user
(
conn
,
MG_UPLOAD
);
memmove
(
buf
,
&
buf
[
i
+
bl
],
len
-
(
i
+
bl
));
len
-=
i
+
bl
;
break
;
}
}
if
(
len
>
bl
)
{
fwrite
(
buf
,
1
,
len
-
bl
,
fp
);
memmove
(
buf
,
&
buf
[
len
-
bl
],
len
-
bl
);
len
=
bl
;
}
}
while
((
n
=
mg_read
(
conn
,
buf
+
len
,
sizeof
(
buf
)
-
len
))
>
0
);
fclose
(
fp
);
}
return
num_uploaded_files
;
}
// This is the heart of the Mongoose's logic.
// This function is called when the request is read, parsed and validated,
// and Mongoose must decide what action to take: serve a file, or
...
...
mongoose.h
View file @
0d442058
...
...
@@ -127,6 +127,11 @@ enum mg_event {
// Callback's return value is ignored.
// ev_data contains lua_State pointer.
MG_INIT_LUA
,
// Mongoose has uploaded file to a temporary directory.
// Callback's return value is ignored.
// ev_data contains NUL-terminated file name.
MG_UPLOAD
,
};
...
...
@@ -336,6 +341,12 @@ 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 and MG_UPLOAD event is sent.
// Return number of uploaded files.
int
mg_upload
(
struct
mg_connection
*
conn
,
const
char
*
destination_dir
);
// Convenience function -- create detached thread.
// Return: 0 on success, non-0 on error.
typedef
void
*
(
*
mg_thread_func_t
)(
void
*
);
...
...
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