Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Merge commit '518b3aa72642145a811310e2fcb2f3c1a3cb2567' + cleaned mg_…

…printf() as our implementation now has changed in a way that printf() errors, if any, are fatal anyhow, so a mg_cry() about such a failure would result in at least one more printf() b0rking then...

Conflicts:
	Makefile
	mongoose.c
  • Loading branch information...
commit 191b7430a00335c5a5da64cbb0da80c45c7e1e32 2 parents e7a0b33 + 518b3aa
Ger Hobbelt authored
6 Makefile
View
@@ -25,7 +25,7 @@ all:
GCC_WARNS = -W -Wall -pedantic
# -Wno-missing-field-initializers -Wno-unused-parameter -Wno-format-zero-length -Wno-missing-braces
-CFLAGS = -W -Wall -std=c99 -O2 $(GCC_WARNS) $(COPT)
+CFLAGS = -std=c99 -O2 $(GCC_WARNS) $(COPT)
MAC_SHARED = -flat_namespace -bundle -undefined suppress
LINFLAGS = -ldl -pthread $(CFLAGS)
LIB = _$(PROG).so
@@ -117,8 +117,8 @@ windows:
# Build for Windows under MinGW
#MINGWDBG= -DDEBUG -O0 -ggdb
MINGWDBG= -DNDEBUG -Os
-MINGWOPT= -std=c99 -mthreads -Wl,--subsystem,console $(MINGWDBG) -DHAVE_STDINT $(GCC_WARNINGS) $(COPT)
-#MINGWOPT= -std=c99 -mthreads -Wl,--subsystem,windows $(MINGWDBG) -DHAVE_STDINT $(GCC_WARNINGS) $(COPT)
+MINGWOPT= -W -Wall -mthreads -Wl,--subsystem,console $(MINGWDBG) -DHAVE_STDINT $(GCC_WARNINGS) $(COPT)
+#MINGWOPT= -W -Wall -mthreads -Wl,--subsystem,windows $(MINGWDBG) -DHAVE_STDINT $(GCC_WARNINGS) $(COPT)
mingw:
windres win32\res.rc win32\res.o
$(CC) $(MINGWOPT) mongoose_ex.c -lws2_32 \
30 README.md
View
@@ -0,0 +1,30 @@
+Overview
+--------
+
+Mongoose is easy to use web server. It also can be used as embedded web server library to provide web interface to applications.
+
+Mongoose executable does not depend on any external library or configuration. If it is copied to any directory and launched from there, it starts to serve that directory on port 8080 (so to access files, go to http://localhost:8080). If some additional config is required - for example, different listening port or IP-based access control, then a mongoose.conf file with respective options can be created in the same directory where executable lives. This makes Mongoose perfect for all sorts of demos, quick tests, file sharing, and Web programming.
+
+
+Features
+--------
+
+- Crossplatform - works on Windows, MacOS and most flavors of UNIX
+- CGI, SSL, SSI, Digest (MD5) authorization, resumed download, aliases
+- IP-based ACL, Windows service, GET, POST, HEAD, PUT, DELETE methods
+- Small footprint: executable size is 40 kB on Linux 2.6 i386 system
+- Embeddable with [simple and clean API](https://github.com/valenok/mongoose/blob/master/mongoose.h). Source is in single .c file to make things easy.
+- Examples: [hello.c](https://github.com/valenok/mongoose/blob/master/examples/hello.c), [post.c](https://github.com/valenok/mongoose/blob/master/examples/post.c), [upload.c](https://github.com/valenok/mongoose/blob/master/examples/upload.c)
+- Python and C# bindings
+
+
+Mailing list
+------------
+
+You can read it online, subscribe to, or send a message at [mongoose-users](http://groups.google.com/group/mongoose-users).
+
+
+Keep Sergey happy
+-----------------
+
+I have a [books wishlist](http://amzn.com/w/1OC2ZCPTQYIEP?sort=priority) on Amazon. If you feel brave, you can buy me a book!
1  examples/Makefile
View
@@ -4,4 +4,5 @@ all:
OS=`uname`; \
test "$$OS" = Linux && LIBS="-ldl" ; \
$(CC) $(CFLAGS) hello.c ../mongoose.c $$LIBS $(ADD) -o hello;
+ $(CC) $(CFLAGS) post.c ../mongoose.c $$LIBS $(ADD) -o post;
$(CC) $(CFLAGS) chat.c ../mongoose.c $$LIBS $(ADD) -o chat
61 examples/post.c
View
@@ -0,0 +1,61 @@
+#include <stdio.h>
+#include <string.h>
+#include "mongoose.h"
+
+static const char *html_form =
+ "<html><body>POST example."
+ "<form method=\"POST\" action=\"/handle_post_request\">"
+ "Input 1: <input type=\"text\" name=\"input_1\" /> <br/>"
+ "Input 2: <input type=\"text\" name=\"input_2\" /> <br/>"
+ "<input type=\"submit\" />"
+ "</form></body></html>";
+
+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")) {
+ // User has submitted a form, show submitted data and a variable value
+ char post_data[1024],
+ input1[sizeof(post_data)], input2[sizeof(post_data)];
+ int post_data_len;
+
+ // Read POST data
+ 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));
+
+ 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);
+ }
+ // Mark as processed
+ return "";
+ } else {
+ return NULL;
+ }
+}
+
+int main(void) {
+ struct mg_context *ctx;
+ const char *options[] = {"listening_ports", "8080", NULL};
+
+ ctx = mg_start(&callback, NULL, options);
+ getchar(); // Wait until user hits "enter"
+ mg_stop(ctx);
+
+ return 0;
+}
130 examples/upload.c
View
@@ -0,0 +1,130 @@
+// Copyright (c) 2004-2012 Sergey Lyubka
+// This file is a part of mongoose project, http://github.com/valenok/mongoose
+
+#include <stdio.h>
+#include <string.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <inttypes.h>
+#include <unistd.h>
+#include "mongoose.h"
+
+// Make sure that form has enctype="multipart/form-data" attribute
+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>";
+
+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;
+ long long int 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 |
+ O_WRONLY | O_EXLOCK | O_CLOEXEC)) < 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 > (long long) 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);
+ } 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);
+ }
+ // Mark as processed
+ return "";
+ } else {
+ return NULL;
+ }
+}
+
+int main(void) {
+ struct mg_context *ctx;
+ const char *options[] = {"listening_ports", "8080", NULL};
+
+ ctx = mg_start(&callback, NULL, options);
+ getchar(); // Wait until user hits "enter"
+ mg_stop(ctx);
+
+ return 0;
+}
27 mongoose.c
View
@@ -3844,7 +3844,8 @@ int mg_vprintf(struct mg_connection *conn, const char *fmt, va_list aa) {
rv = mg_write(conn, fmt, strlen(fmt));
return (rv < 0 ? 0 : rv);
} else if (!strcmp(fmt, "%s")) {
- fmt = va_arg(aa, const char *);
+ // This also takes care of the scenario where mg_printf(conn, "%s", "") was called, so the vsnprintf() further below MUST produce a non-zero length!
+ fmt = va_arg(aa, const char *);
if (!fmt) fmt = "???";
rv = mg_write(conn, fmt, strlen(fmt));
return (rv < 0 ? 0 : rv);
@@ -3860,24 +3861,20 @@ int mg_vprintf(struct mg_connection *conn, const char *fmt, va_list aa) {
mem[sizeof(mem) - 1] = 0;
va_end(ap);
- if (len < 0) {
+ // As we took also care above of the scenario where mg_printf(conn, "%s", "") was called, vsnprintf() MUST produce a non-zero length on success!
+ if (len <= 0 || len >= (int) sizeof(mem) - 1) {
// MSVC produces -1 on printf("%s", str) for very long 'str'!
- len = (int)strlen(mem);
- }
- if (len == 0) {
- // vsnprintf() error, give up
- mg_cry(conn, "%s(%s, ...): vsnprintf() error", __func__, fmt);
- } else if (len >= (int) sizeof(mem) - 1) {
VA_COPY(ap, aa);
len = mg_vasprintf(conn, &buf, 0, fmt, ap);
va_end(ap);
- if (buf) {
+ if (buf && len > 0) {
rv = mg_write(conn, buf, (size_t)len);
free(buf);
return (rv < len ? 0 : rv);
} else {
- // Failed to allocate large enough buffer, give up
+ // Failed to allocate large enough buffer or failed inside mg_vasprintf, give up
+ if (buf) free(buf);
mg_cry(conn, "%s(%s, ...): Can't allocate buffer, not printing anything",
__func__, fmt);
}
@@ -5126,7 +5123,7 @@ static int64_t send_file_data(struct mg_connection *conn, FILE *fp, int64_t len)
// Read from file, exit the loop on error
num_read = (int)fread(buf, 1, (size_t)to_read, fp);
- if (num_read == 0 && ferror(fp)) {
+ if (num_read <= 0 && ferror(fp)) {
send_http_error(conn, 578, NULL, "%s: failed to read from file: %s", __func__, mg_strerror(ERRNO)); // signal internal error in access log file at least
return -2;
}
@@ -7273,8 +7270,7 @@ static unsigned long ssl_id_callback(void) {
}
#if !defined(NO_SSL_DL)
-static int load_dll(struct mg_context *ctx, const char *dll_name,
- struct ssl_func *sw) {
+static int load_dll(const char *dll_name, struct ssl_func *sw) {
union {void *p; void (*fp)(void);} u;
void *dll_handle;
struct ssl_func *fp;
@@ -7316,8 +7312,7 @@ static int set_ssl_option(struct mg_context *ctx) {
}
#if !defined(NO_SSL_DL)
- if (!load_dll(ctx, SSL_LIB, ssl_sw) ||
- !load_dll(ctx, CRYPTO_LIB, crypto_sw)) {
+ if (!load_dll(SSL_LIB, ssl_sw) || !load_dll(CRYPTO_LIB, crypto_sw)) {
return 0;
}
#endif // NO_SSL_DL
@@ -9148,7 +9143,7 @@ struct mg_context *mg_start(const struct mg_user_class_t *user_functions,
// be initialized before listening ports. UID must be set last.
if (!set_gpass_option(ctx) ||
#if !defined(NO_SSL)
- !set_ssl_option(ctx) ||
+ (ctx->config[SSL_CERTIFICATE] != NULL && !set_ssl_option(ctx)) ||
#endif
!set_ports_option(ctx) ||
#if !defined(_WIN32)
14 mongoose_sys_porting.h
View
@@ -23,12 +23,15 @@
#if defined(_WIN32)
#define _CRT_SECURE_NO_WARNINGS // Disable deprecation warning in VS2005
+#ifdef WIN32_LEAN_AND_MEAN
+#undef WIN32_LEAN_AND_MEAN // Disable WIN32_LEAN_AND_MEAN, if necessary
+#endif
#else
-#define _XOPEN_SOURCE 600 // For PATH_MAX and flockfile() on Linux
-#define _LARGEFILE_SOURCE // Enable 64-bit file offsets
+#define _XOPEN_SOURCE /*600*/ // For PATH_MAX and flockfile() on Linux
+#define _LARGEFILE_SOURCE // Enable 64-bit file offsets
#endif
-#define __STDC_FORMAT_MACROS // <inttypes.h> wants this for C++
-#define __STDC_LIMIT_MACROS // C++ wants that for INT64_MAX
+#define __STDC_FORMAT_MACROS // <inttypes.h> wants this for C++
+#define __STDC_LIMIT_MACROS // C++ wants that for INT64_MAX
#if defined(__SYMBIAN32__)
#define NO_SSL // SSL is not supported
@@ -695,7 +698,7 @@ typedef int SOCKET;
#endif
-#if defined(DEBUG) || defined(_DEBUG)
+#if (defined(DEBUG) || defined(_DEBUG)) && !MG_DEBUG_TRACING
#if defined(PTW32_VERSION)
#define MG_PTHREAD_SELF() pthread_self().p
#else
@@ -734,6 +737,7 @@ do { \
} while (0)
#else
#define MG_DEBUG_TRACING 0
+#undef DEBUG_TRACE
#define DEBUG_TRACE(l, x)
#endif // DEBUG
2  test/test.pl
View
@@ -222,8 +222,6 @@ sub kill_spawned_child {
write_file("$root/a+.txt", '');
o("GET /a+.txt HTTP/1.0\n\n", 'HTTP/1.0 200 OK', 'URL-decoding, + in URI');
-o("GET /%5c/a.txt HTTP/1.0\n\n", 'blah', 'GET dir backslash');
-
# Test HTTP version parsing
o("GET / HTTPX/1.0\r\n\r\n", '400 Bad Request', 'Bad HTTP Version', 0);
o("GET / HTTP/x.1\r\n\r\n", '505 HTTP', 'Bad HTTP maj Version');
Please sign in to comment.
Something went wrong with that request. Please try again.