23 #if defined(__GNUC__) || defined(__MINGW32__)
25 (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__)
26 #if GCC_VERSION >= 40500
28 #define GCC_DIAGNOSTIC
32 #if defined(GCC_DIAGNOSTIC)
35 #pragma GCC diagnostic ignored "-Wunused-macros"
37 #pragma GCC diagnostic ignored "-Wpadded"
40 #if defined(__clang__)
44 #pragma GCC diagnostic push
45 #pragma GCC diagnostic ignored "-Wreserved-id-macro"
49 #if !defined(_CRT_SECURE_NO_WARNINGS)
50 #define _CRT_SECURE_NO_WARNINGS
52 #if !defined(_WIN32_WINNT)
53 #define _WIN32_WINNT 0x0501
56 #if !defined(_GNU_SOURCE)
59 #if defined(__linux__) && !defined(_XOPEN_SOURCE)
60 #define _XOPEN_SOURCE 600
62 #if !defined(_LARGEFILE_SOURCE)
63 #define _LARGEFILE_SOURCE
65 #if !defined(_FILE_OFFSET_BITS)
66 #define _FILE_OFFSET_BITS 64
68 #if !defined(__STDC_FORMAT_MACROS)
69 #define __STDC_FORMAT_MACROS
71 #if !defined(__STDC_LIMIT_MACROS)
72 #define __STDC_LIMIT_MACROS
74 #if !defined(_DARWIN_UNLIMITED_SELECT)
75 #define _DARWIN_UNLIMITED_SELECT
78 #define __EXTENSIONS__
79 #define __inline inline
83 #if defined(__clang__)
85 #pragma GCC diagnostic pop
95 #pragma warning(disable : 4306)
97 #pragma warning(disable : 4127)
99 #pragma warning(disable : 4204)
101 #pragma warning(disable : 4820)
103 #pragma warning(disable : 4668)
105 #pragma warning(disable : 4255)
107 #pragma warning(disable : 4711)
114 #if defined(__STDC_VERSION__) && __STDC_VERSION__ > 201100L
115 #define mg_static_assert _Static_assert
116 #elif defined(__cplusplus) && __cplusplus >= 201103L
117 #define mg_static_assert static_assert
119 char static_assert_replacement[1];
120 #define mg_static_assert(cond, txt) \
121 extern char static_assert_replacement[(cond) ? 1 : -1]
124 mg_static_assert(
sizeof(
int) == 4 ||
sizeof(
int) == 8,
125 "int data type size check");
126 mg_static_assert(
sizeof(
void *) == 4 ||
sizeof(
void *) == 8,
127 "pointer data type size check");
128 mg_static_assert(
sizeof(
void *) >=
sizeof(
int),
"data type size check");
132 #if defined(NO_ALTERNATIVE_QUEUE)
133 #if defined(ALTERNATIVE_QUEUE)
134 #error "Define ALTERNATIVE_QUEUE or NO_ALTERNATIVE_QUEUE or none, but not both"
137 #define ALTERNATIVE_QUEUE
142 #if !defined(WIN32_LEAN_AND_MEAN)
143 #define WIN32_LEAN_AND_MEAN
146 #if defined(__SYMBIAN32__)
152 #error "Symbian is no longer maintained. CivetWeb no longer supports Symbian."
155 #define PATH_MAX FILENAME_MAX
159 #if !defined(CIVETWEB_HEADER_INCLUDED)
165 #if !defined(DEBUG_TRACE)
167 static void DEBUG_TRACE_FUNC(
const char *func,
169 PRINTF_FORMAT_STRING(
const char *fmt),
170 ...) PRINTF_ARGS(3, 4);
172 #define DEBUG_TRACE(fmt, ...) \
173 DEBUG_TRACE_FUNC(__func__, __LINE__, fmt, __VA_ARGS__)
175 #define NEED_DEBUG_TRACE_FUNC
178 #define DEBUG_TRACE(fmt, ...) \
185 #if !defined(DEBUG_ASSERT)
187 #define DEBUG_ASSERT(cond) \
190 DEBUG_TRACE("ASSERTION FAILED: %s", #cond); \
195 #define DEBUG_ASSERT(cond)
200 #if defined(__GNUC__) && defined(GCC_INSTRUMENTATION)
201 void __cyg_profile_func_enter(
void *this_fn,
void *call_site)
202 __attribute__((no_instrument_function));
204 void __cyg_profile_func_exit(
void *this_fn,
void *call_site)
205 __attribute__((no_instrument_function));
208 __cyg_profile_func_enter(
void *this_fn,
void *call_site)
210 if ((
void *)this_fn != (
void *)printf) {
211 printf(
"E %p %p\n", this_fn, call_site);
216 __cyg_profile_func_exit(
void *this_fn,
void *call_site)
218 if ((
void *)this_fn != (
void *)printf) {
219 printf(
"X %p %p\n", this_fn, call_site);
225 #if !defined(IGNORE_UNUSED_RESULT)
226 #define IGNORE_UNUSED_RESULT(a) ((void)((a) && 1))
230 #if defined(__GNUC__) || defined(__MINGW32__)
246 #pragma GCC diagnostic ignored "-Wunused-function"
248 #define FUNCTION_MAY_BE_UNUSED
251 #define FUNCTION_MAY_BE_UNUSED
256 #if !defined(_WIN32_WCE)
260 #include <sys/stat.h>
261 #include <sys/types.h>
265 #if defined(__clang__)
269 #pragma clang diagnostic ignored "-Wdisabled-macro-expansion"
272 #if defined(__GNUC__) || defined(__MINGW32__)
289 #if defined(__MACH__)
291 #if defined(__clang__)
292 #if (__clang_major__ == 3) && ((__clang_minor__ == 7) || (__clang_minor__ == 8))
294 #pragma clang diagnostic ignored "-Wno-reserved-id-macro"
295 #pragma clang diagnostic ignored "-Wno-keyword-macro"
299 #define CLOCK_MONOTONIC (1)
300 #define CLOCK_REALTIME (2)
302 #include <mach/clock.h>
303 #include <mach/mach.h>
304 #include <mach/mach_time.h>
305 #include <sys/errno.h>
306 #include <sys/time.h>
310 _civet_clock_gettime(
int clk_id,
struct timespec *t)
312 memset(t, 0,
sizeof(*t));
313 if (clk_id == CLOCK_REALTIME) {
315 int rv = gettimeofday(&now, NULL);
319 t->tv_sec = now.tv_sec;
320 t->tv_nsec = now.tv_usec * 1000;
323 }
else if (clk_id == CLOCK_MONOTONIC) {
324 static uint64_t clock_start_time = 0;
325 static mach_timebase_info_data_t timebase_ifo = {0, 0};
327 uint64_t now = mach_absolute_time();
329 if (clock_start_time == 0) {
330 kern_return_t mach_status = mach_timebase_info(&timebase_ifo);
331 DEBUG_ASSERT(mach_status == KERN_SUCCESS);
336 clock_start_time = now;
339 now = (uint64_t)((
double)(now - clock_start_time)
340 * (
double)timebase_ifo.numer
341 / (double)timebase_ifo.denom);
343 t->tv_sec = now / 1000000000;
344 t->tv_nsec = now % 1000000000;
351 #if defined(__CLOCK_AVAILABILITY)
356 _civet_safe_clock_gettime(
int clk_id,
struct timespec *t)
359 return clock_gettime(clk_id, t);
361 return _civet_clock_gettime(clk_id, t);
363 #define clock_gettime _civet_safe_clock_gettime
365 #define clock_gettime _civet_clock_gettime
388 #if !defined(MAX_WORKER_THREADS)
389 #define MAX_WORKER_THREADS (1024 * 64)
396 #if !defined(SOCKET_TIMEOUT_QUANTUM)
397 #define SOCKET_TIMEOUT_QUANTUM (2000)
401 #if !defined(MG_FILE_COMPRESSION_SIZE_LIMIT)
402 #define MG_FILE_COMPRESSION_SIZE_LIMIT (1024)
405 #if !defined(PASSWORDS_FILE_NAME)
406 #define PASSWORDS_FILE_NAME ".htpasswd"
411 #if !defined(CGI_ENVIRONMENT_SIZE)
412 #define CGI_ENVIRONMENT_SIZE (4096)
416 #if !defined(MAX_CGI_ENVIR_VARS)
417 #define MAX_CGI_ENVIR_VARS (256)
421 #if !defined(MG_BUF_LEN)
422 #define MG_BUF_LEN (1024 * 8)
427 #if !defined(MGSQLEN)
435 #define ARRAY_SIZE(array) (sizeof(array) / sizeof(array[0]))
438 #if !defined(INT64_MAX)
439 #define INT64_MAX (9223372036854775807)
442 #define SHUTDOWN_RD (0)
443 #define SHUTDOWN_WR (1)
444 #define SHUTDOWN_BOTH (2)
446 mg_static_assert(MAX_WORKER_THREADS >= 1,
447 "worker threads must be a positive number");
449 mg_static_assert(
sizeof(
size_t) == 4 ||
sizeof(
size_t) == 8,
450 "size_t data type size check");
454 #include <winsock2.h>
455 #include <ws2tcpip.h>
457 typedef const char *SOCK_OPT_TYPE;
459 #if !defined(PATH_MAX)
460 #define W_PATH_MAX (MAX_PATH)
462 #define PATH_MAX (W_PATH_MAX * 3)
464 #define W_PATH_MAX ((PATH_MAX + 2) / 3)
467 mg_static_assert(PATH_MAX >= 1,
"path length must be a positive number");
469 #if !defined(_IN_PORT_T)
470 #if !defined(in_port_t)
471 #define in_port_t u_short
475 #if !defined(_WIN32_WCE)
485 #define errno ((int)(GetLastError()))
486 #define strerror(x) (_ultoa(x, (char *)_alloca(sizeof(x) * 3), 10))
489 #define MAKEUQUAD(lo, hi) \
490 ((uint64_t)(((uint32_t)(lo)) | ((uint64_t)((uint32_t)(hi))) << 32))
491 #define RATE_DIFF (10000000)
492 #define EPOCH_DIFF (MAKEUQUAD(0xd53e8000, 0x019db1de))
493 #define SYS2UNIX_TIME(lo, hi) \
494 ((time_t)((MAKEUQUAD((lo), (hi)) - EPOCH_DIFF) / RATE_DIFF))
499 #if defined(_MSC_VER)
500 #if (_MSC_VER < 1300)
502 #define STR(x) STRX(x)
503 #define __func__ __FILE__ ":" STR(__LINE__)
504 #define strtoull(x, y, z) ((unsigned __int64)_atoi64(x))
505 #define strtoll(x, y, z) (_atoi64(x))
507 #define __func__ __FUNCTION__
508 #define strtoull(x, y, z) (_strtoui64(x, y, z))
509 #define strtoll(x, y, z) (_strtoi64(x, y, z))
513 #define ERRNO ((int)(GetLastError()))
516 #if defined(_WIN64) || defined(__MINGW64__)
517 #if !defined(SSL_LIB)
518 #define SSL_LIB "ssleay64.dll"
520 #if !defined(CRYPTO_LIB)
521 #define CRYPTO_LIB "libeay64.dll"
524 #if !defined(SSL_LIB)
525 #define SSL_LIB "ssleay32.dll"
527 #if !defined(CRYPTO_LIB)
528 #define CRYPTO_LIB "libeay32.dll"
532 #define O_NONBLOCK (0)
536 #if !defined(EWOULDBLOCK)
537 #define EWOULDBLOCK WSAEWOULDBLOCK
540 #define INT64_FMT "I64d"
541 #define UINT64_FMT "I64u"
543 #define WINCDECL __cdecl
544 #define vsnprintf_impl _vsnprintf
545 #define access _access
546 #define mg_sleep(x) (Sleep(x))
548 #define pipe(x) _pipe(x, MG_BUF_LEN, _O_BINARY)
550 #define popen(x, y) (_popen(x, y))
553 #define pclose(x) (_pclose(x))
555 #define close(x) (_close(x))
556 #define dlsym(x, y) (GetProcAddress((HINSTANCE)(x), (y)))
557 #define RTLD_LAZY (0)
558 #define fseeko(x, y, z) ((_lseeki64(_fileno(x), (y), (z)) == -1) ? -1 : 0)
559 #define fdopen(x, y) (_fdopen((x), (y)))
560 #define write(x, y, z) (_write((x), (y), (unsigned)z))
561 #define read(x, y, z) (_read((x), (y), (unsigned)z))
562 #define flockfile(x) (EnterCriticalSection(&global_log_file_lock))
563 #define funlockfile(x) (LeaveCriticalSection(&global_log_file_lock))
564 #define sleep(x) (Sleep((x)*1000))
565 #define rmdir(x) (_rmdir(x))
566 #if defined(_WIN64) || !defined(__MINGW32__)
568 #define timegm(x) (_mkgmtime(x))
570 time_t timegm(
struct tm *tm);
576 #define fileno(x) (_fileno(x))
579 typedef HANDLE pthread_mutex_t;
580 typedef DWORD pthread_key_t;
581 typedef HANDLE pthread_t;
583 CRITICAL_SECTION threadIdSec;
584 struct mg_workerTLS *waiting_thread;
587 #if !defined(__clockid_t_defined)
588 typedef DWORD clockid_t;
590 #if !defined(CLOCK_MONOTONIC)
591 #define CLOCK_MONOTONIC (1)
593 #if !defined(CLOCK_REALTIME)
594 #define CLOCK_REALTIME (2)
596 #if !defined(CLOCK_THREAD)
597 #define CLOCK_THREAD (3)
599 #if !defined(CLOCK_PROCESS)
600 #define CLOCK_PROCESS (4)
604 #if defined(_MSC_VER) && (_MSC_VER >= 1900)
605 #define _TIMESPEC_DEFINED
607 #if !defined(_TIMESPEC_DEFINED)
614 #if !defined(WIN_PTHREADS_TIME_H)
615 #define MUST_IMPLEMENT_CLOCK_GETTIME
618 #if defined(MUST_IMPLEMENT_CLOCK_GETTIME)
619 #define clock_gettime mg_clock_gettime
621 clock_gettime(clockid_t clk_id,
struct timespec *tp)
624 ULARGE_INTEGER li, li2;
627 static double perfcnt_per_sec = 0.0;
628 static BOOL initialized = FALSE;
631 QueryPerformanceFrequency((LARGE_INTEGER *)&li);
632 perfcnt_per_sec = 1.0 / li.QuadPart;
637 memset(tp, 0,
sizeof(*tp));
639 if (clk_id == CLOCK_REALTIME) {
642 GetSystemTimeAsFileTime(&ft);
643 li.LowPart = ft.dwLowDateTime;
644 li.HighPart = ft.dwHighDateTime;
645 li.QuadPart -= 116444736000000000;
646 tp->tv_sec = (time_t)(li.QuadPart / 10000000);
647 tp->tv_nsec = (long)(li.QuadPart % 10000000) * 100;
651 }
else if (clk_id == CLOCK_MONOTONIC) {
654 QueryPerformanceCounter((LARGE_INTEGER *)&li);
655 d = li.QuadPart * perfcnt_per_sec;
656 tp->tv_sec = (time_t)d;
657 d -= (double)tp->tv_sec;
658 tp->tv_nsec = (
long)(d * 1.0E9);
662 }
else if (clk_id == CLOCK_THREAD) {
665 FILETIME t_create, t_exit, t_kernel, t_user;
666 if (GetThreadTimes(GetCurrentThread(),
671 li.LowPart = t_user.dwLowDateTime;
672 li.HighPart = t_user.dwHighDateTime;
673 li2.LowPart = t_kernel.dwLowDateTime;
674 li2.HighPart = t_kernel.dwHighDateTime;
675 li.QuadPart += li2.QuadPart;
676 tp->tv_sec = (time_t)(li.QuadPart / 10000000);
677 tp->tv_nsec = (long)(li.QuadPart % 10000000) * 100;
682 }
else if (clk_id == CLOCK_PROCESS) {
685 FILETIME t_create, t_exit, t_kernel, t_user;
686 if (GetProcessTimes(GetCurrentProcess(),
691 li.LowPart = t_user.dwLowDateTime;
692 li.HighPart = t_user.dwHighDateTime;
693 li2.LowPart = t_kernel.dwLowDateTime;
694 li2.HighPart = t_kernel.dwHighDateTime;
695 li.QuadPart += li2.QuadPart;
696 tp->tv_sec = (time_t)(li.QuadPart / 10000000);
697 tp->tv_nsec = (long)(li.QuadPart % 10000000) * 100;
717 static int pthread_mutex_lock(pthread_mutex_t *);
718 static int pthread_mutex_unlock(pthread_mutex_t *);
719 static void path_to_unicode(
const struct mg_connection *conn,
729 mg_fgets(
char *buf,
size_t size,
struct mg_file *filep,
char **p);
734 char d_name[PATH_MAX];
739 WIN32_FIND_DATAW info;
740 struct dirent result;
744 #if !defined(HAVE_POLL)
754 #if defined(_MSC_VER)
755 #pragma comment(lib, "Ws2_32.lib")
760 #include <arpa/inet.h>
761 #include <inttypes.h>
763 #include <netinet/in.h>
764 #include <netinet/tcp.h>
766 #include <sys/poll.h>
767 #include <sys/socket.h>
768 #include <sys/time.h>
769 #include <sys/utsname.h>
770 #include <sys/wait.h>
771 typedef const void *SOCK_OPT_TYPE;
774 typedef unsigned short int in_port_t;
781 #define vsnprintf_impl vsnprintf
783 #if !defined(NO_SSL_DL) && !defined(NO_SSL)
787 #if defined(__MACH__)
788 #define SSL_LIB "libssl.dylib"
789 #define CRYPTO_LIB "libcrypto.dylib"
791 #if !defined(SSL_LIB)
792 #define SSL_LIB "libssl.so"
794 #if !defined(CRYPTO_LIB)
795 #define CRYPTO_LIB "libcrypto.so"
798 #if !defined(O_BINARY)
801 #define closesocket(a) (close(a))
802 #define mg_mkdir(conn, path, mode) (mkdir(path, mode))
803 #define mg_remove(conn, x) (remove(x))
804 #define mg_sleep(x) (usleep((x)*1000))
805 #define mg_opendir(conn, x) (opendir(x))
806 #define mg_closedir(x) (closedir(x))
807 #define mg_readdir(x) (readdir(x))
808 #define ERRNO (errno)
809 #define INVALID_SOCKET (-1)
810 #define INT64_FMT PRId64
811 #define UINT64_FMT PRIu64
817 #if !defined(CLOCK_MONOTONIC)
818 #define CLOCK_MONOTONIC CLOCK_REALTIME
829 #define socklen_t int
836 #if !defined(SOMAXCONN)
838 #define SOMAXCONN (100)
842 #if defined(NEED_TIMEGM)
846 return (y % 4 == 0 && y % 100 != 0) || y % 400 == 0;
852 return (y - 1969) / 4 - (y - 1901) / 100 + (y - 1601) / 400;
856 timegm(
struct tm *tm)
858 static const unsigned short ydays[] = {
859 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365};
860 int year = tm->tm_year + 1900;
861 int mon = tm->tm_mon;
862 int mday = tm->tm_mday - 1;
863 int hour = tm->tm_hour;
864 int min = tm->tm_min;
865 int sec = tm->tm_sec;
867 if (year < 1970 || mon < 0 || mon > 11 || mday < 0
868 || (mday >= ydays[mon + 1] - ydays[mon]
869 + (mon == 1 && is_leap(year) ? 1 : 0))
870 || hour < 0 || hour > 23 || min < 0 || min > 59 || sec < 0 || sec > 60)
873 time_t res = year - 1970;
876 res += ydays[mon] + (mon > 1 && is_leap(year) ? 1 : 0);
877 res += count_leap(year);
891 #if !defined(va_copy)
892 #define va_copy(x, y) ((x) = (y))
899 #if defined(GCC_DIAGNOSTIC)
901 #pragma GCC diagnostic push
902 #pragma GCC diagnostic ignored "-Wunused-function"
906 static CRITICAL_SECTION global_log_file_lock;
908 FUNCTION_MAY_BE_UNUSED
912 return GetCurrentThreadId();
916 FUNCTION_MAY_BE_UNUSED
920 void (*_ignored)(
void *)
927 return (*key != TLS_OUT_OF_INDEXES) ? 0 : -1;
933 FUNCTION_MAY_BE_UNUSED
935 pthread_key_delete(pthread_key_t key)
937 return TlsFree(key) ? 0 : 1;
941 FUNCTION_MAY_BE_UNUSED
943 pthread_setspecific(pthread_key_t key,
void *value)
945 return TlsSetValue(key, value) ? 0 : 1;
949 FUNCTION_MAY_BE_UNUSED
951 pthread_getspecific(pthread_key_t key)
953 return TlsGetValue(key);
956 #if defined(GCC_DIAGNOSTIC)
958 #pragma GCC diagnostic pop
961 static struct pthread_mutex_undefined_struct *pthread_mutex_attr = NULL;
963 static pthread_mutexattr_t pthread_mutex_attr;
967 #if defined(_WIN32_WCE)
970 #if defined(GCC_DIAGNOSTIC)
972 #pragma GCC diagnostic push
973 #pragma GCC diagnostic ignored "-Wunused-function"
977 FUNCTION_MAY_BE_UNUSED
986 SystemTimeToFileTime(&st, &ft);
987 t = SYS2UNIX_TIME(ft.dwLowDateTime, ft.dwHighDateTime);
997 FUNCTION_MAY_BE_UNUSED
999 localtime_s(
const time_t *ptime,
struct tm *ptm)
1001 int64_t t = ((int64_t)*ptime) * RATE_DIFF + EPOCH_DIFF;
1004 TIME_ZONE_INFORMATION tzinfo;
1010 *(int64_t *)&ft = t;
1011 FileTimeToLocalFileTime(&ft, &lft);
1012 FileTimeToSystemTime(&lft, &st);
1013 ptm->tm_year = st.wYear - 1900;
1014 ptm->tm_mon = st.wMonth - 1;
1015 ptm->tm_wday = st.wDayOfWeek;
1016 ptm->tm_mday = st.wDay;
1017 ptm->tm_hour = st.wHour;
1018 ptm->tm_min = st.wMinute;
1019 ptm->tm_sec = st.wSecond;
1022 (GetTimeZoneInformation(&tzinfo) == TIME_ZONE_ID_DAYLIGHT) ? 1 : 0;
1028 FUNCTION_MAY_BE_UNUSED
1030 gmtime_s(
const time_t *ptime,
struct tm *ptm)
1033 return localtime_s(ptime, ptm);
1037 static int mg_atomic_inc(
volatile int *addr);
1038 static struct tm tm_array[MAX_WORKER_THREADS];
1039 static int tm_index = 0;
1042 FUNCTION_MAY_BE_UNUSED
1044 localtime(
const time_t *ptime)
1046 int i = mg_atomic_inc(&tm_index) % (
sizeof(tm_array) /
sizeof(tm_array[0]));
1047 return localtime_s(ptime, tm_array + i);
1051 FUNCTION_MAY_BE_UNUSED
1053 gmtime(
const time_t *ptime)
1055 int i = mg_atomic_inc(&tm_index) % ARRAY_SIZE(tm_array);
1056 return gmtime_s(ptime, tm_array + i);
1060 FUNCTION_MAY_BE_UNUSED
1062 strftime(
char *dst,
size_t dst_size,
const char *fmt,
const struct tm *tm)
1069 #define _beginthreadex(psec, stack, func, prm, flags, ptid) \
1070 (uintptr_t) CreateThread(psec, stack, func, prm, flags, ptid)
1072 #define remove(f) mg_remove(NULL, f)
1075 FUNCTION_MAY_BE_UNUSED
1077 rename(
const char *a,
const char *b)
1079 wchar_t wa[W_PATH_MAX];
1080 wchar_t wb[W_PATH_MAX];
1081 path_to_unicode(NULL, a, wa, ARRAY_SIZE(wa));
1082 path_to_unicode(NULL, b, wb, ARRAY_SIZE(wb));
1084 return MoveFileW(wa, wb) ? 0 : -1;
1094 FUNCTION_MAY_BE_UNUSED
1096 stat(
const char *name,
struct stat *st)
1098 wchar_t wbuf[W_PATH_MAX];
1099 WIN32_FILE_ATTRIBUTE_DATA attr;
1100 time_t creation_time, write_time;
1102 path_to_unicode(NULL, name, wbuf, ARRAY_SIZE(wbuf));
1103 memset(&attr, 0,
sizeof(attr));
1105 GetFileAttributesExW(wbuf, GetFileExInfoStandard, &attr);
1107 (((int64_t)attr.nFileSizeHigh) << 32) + (int64_t)attr.nFileSizeLow;
1109 write_time = SYS2UNIX_TIME(attr.ftLastWriteTime.dwLowDateTime,
1110 attr.ftLastWriteTime.dwHighDateTime);
1111 creation_time = SYS2UNIX_TIME(attr.ftCreationTime.dwLowDateTime,
1112 attr.ftCreationTime.dwHighDateTime);
1114 if (creation_time > write_time) {
1115 st->st_mtime = creation_time;
1117 st->st_mtime = write_time;
1122 #define access(x, a) 1
1130 #if defined(GCC_DIAGNOSTIC)
1132 #pragma GCC diagnostic pop
1138 #if defined(GCC_DIAGNOSTIC)
1140 #pragma GCC diagnostic push
1141 #pragma GCC diagnostic ignored "-Wunused-function"
1143 #if defined(__clang__)
1145 #pragma clang diagnostic push
1146 #pragma clang diagnostic ignored "-Wunused-function"
1149 static pthread_mutex_t global_lock_mutex;
1154 FUNCTION_MAY_BE_UNUSED
1155 static int pthread_mutex_lock(pthread_mutex_t *mutex);
1157 FUNCTION_MAY_BE_UNUSED
1158 static int pthread_mutex_unlock(pthread_mutex_t *mutex);
1162 FUNCTION_MAY_BE_UNUSED
1164 mg_global_lock(
void)
1166 (void)pthread_mutex_lock(&global_lock_mutex);
1170 FUNCTION_MAY_BE_UNUSED
1172 mg_global_unlock(
void)
1174 (void)pthread_mutex_unlock(&global_lock_mutex);
1178 FUNCTION_MAY_BE_UNUSED
1180 mg_atomic_inc(
volatile int *addr)
1183 #if defined(_WIN32) && !defined(NO_ATOMICS)
1187 ret = InterlockedIncrement((
volatile long *)addr);
1188 #elif defined(__GNUC__) \
1189 && ((__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ > 0))) \
1190 && !defined(NO_ATOMICS)
1191 ret = __sync_add_and_fetch(addr, 1);
1201 FUNCTION_MAY_BE_UNUSED
1203 mg_atomic_dec(
volatile int *addr)
1206 #if defined(_WIN32) && !defined(NO_ATOMICS)
1210 ret = InterlockedDecrement((
volatile long *)addr);
1211 #elif defined(__GNUC__) \
1212 && ((__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ > 0))) \
1213 && !defined(NO_ATOMICS)
1214 ret = __sync_sub_and_fetch(addr, 1);
1224 #if defined(USE_SERVER_STATS)
1226 mg_atomic_add(
volatile int64_t *addr, int64_t value)
1229 #if defined(_WIN64) && !defined(NO_ATOMICS)
1230 ret = InterlockedAdd64(addr, value);
1231 #elif defined(__GNUC__) \
1232 && ((__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ > 0))) \
1233 && !defined(NO_ATOMICS)
1234 ret = __sync_add_and_fetch(addr, value);
1246 #if defined(GCC_DIAGNOSTIC)
1248 #pragma GCC diagnostic pop
1250 #if defined(__clang__)
1252 #pragma clang diagnostic pop
1256 #if defined(USE_SERVER_STATS)
1258 struct mg_memory_stat {
1259 volatile int64_t totalMemUsed;
1260 volatile int64_t maxMemUsed;
1261 volatile int blockCount;
1265 static struct mg_memory_stat *get_memory_stat(
struct mg_context *ctx);
1269 mg_malloc_ex(
size_t size,
1270 struct mg_context *ctx,
1274 void *data = malloc(size + 2 *
sizeof(uintptr_t));
1276 struct mg_memory_stat *mstat = get_memory_stat(ctx);
1278 #if defined(MEMORY_DEBUGGING)
1279 char mallocStr[256];
1286 int64_t mmem = mg_atomic_add(&mstat->totalMemUsed, (int64_t)size);
1287 if (mmem > mstat->maxMemUsed) {
1290 mstat->maxMemUsed = mmem;
1293 mg_atomic_inc(&mstat->blockCount);
1294 ((uintptr_t *)data)[0] = size;
1295 ((uintptr_t *)data)[1] = (uintptr_t)mstat;
1296 memory = (
void *)(((
char *)data) + 2 *
sizeof(uintptr_t));
1299 #if defined(MEMORY_DEBUGGING)
1301 "MEM: %p %5lu alloc %7lu %4lu --- %s:%u\n",
1303 (
unsigned long)size,
1304 (
unsigned long)mstat->totalMemUsed,
1305 (
unsigned long)mstat->blockCount,
1309 OutputDebugStringA(mallocStr);
1311 DEBUG_TRACE(
"%s", mallocStr);
1320 mg_calloc_ex(
size_t count,
1322 struct mg_context *ctx,
1326 void *data = mg_malloc_ex(size * count, ctx, file, line);
1329 memset(data, 0, size * count);
1336 mg_free_ex(
void *memory,
const char *file,
unsigned line)
1338 void *data = (
void *)(((
char *)memory) - 2 *
sizeof(uintptr_t));
1341 #if defined(MEMORY_DEBUGGING)
1342 char mallocStr[256];
1349 uintptr_t size = ((uintptr_t *)data)[0];
1350 struct mg_memory_stat *mstat =
1351 (
struct mg_memory_stat *)(((uintptr_t *)data)[1]);
1352 mg_atomic_add(&mstat->totalMemUsed, -(int64_t)size);
1353 mg_atomic_dec(&mstat->blockCount);
1354 #if defined(MEMORY_DEBUGGING)
1356 "MEM: %p %5lu free %7lu %4lu --- %s:%u\n",
1358 (
unsigned long)size,
1359 (
unsigned long)mstat->totalMemUsed,
1360 (
unsigned long)mstat->blockCount,
1364 OutputDebugStringA(mallocStr);
1366 DEBUG_TRACE(
"%s", mallocStr);
1375 mg_realloc_ex(
void *memory,
1377 struct mg_context *ctx,
1385 #if defined(MEMORY_DEBUGGING)
1386 char mallocStr[256];
1395 struct mg_memory_stat *mstat;
1396 data = (
void *)(((
char *)memory) - 2 *
sizeof(uintptr_t));
1397 oldsize = ((uintptr_t *)data)[0];
1398 mstat = (
struct mg_memory_stat *)((uintptr_t *)data)[1];
1399 _realloc = realloc(data, newsize + 2 *
sizeof(uintptr_t));
1402 mg_atomic_add(&mstat->totalMemUsed, -(int64_t)oldsize);
1403 #if defined(MEMORY_DEBUGGING)
1405 "MEM: %p %5lu r-free %7lu %4lu --- %s:%u\n",
1407 (
unsigned long)oldsize,
1408 (
unsigned long)mstat->totalMemUsed,
1409 (
unsigned long)mstat->blockCount,
1413 OutputDebugStringA(mallocStr);
1415 DEBUG_TRACE(
"%s", mallocStr);
1418 mg_atomic_add(&mstat->totalMemUsed, (int64_t)newsize);
1419 #if defined(MEMORY_DEBUGGING)
1421 "MEM: %p %5lu r-alloc %7lu %4lu --- %s:%u\n",
1423 (
unsigned long)newsize,
1424 (
unsigned long)mstat->totalMemUsed,
1425 (
unsigned long)mstat->blockCount,
1429 OutputDebugStringA(mallocStr);
1431 DEBUG_TRACE(
"%s", mallocStr);
1434 *(uintptr_t *)data = newsize;
1435 data = (
void *)(((
char *)data) + 2 *
sizeof(uintptr_t));
1437 #if defined(MEMORY_DEBUGGING)
1439 OutputDebugStringA(
"MEM: realloc failed\n");
1441 DEBUG_TRACE(
"%s",
"MEM: realloc failed\n");
1448 data = mg_malloc_ex(newsize, ctx, file, line);
1453 mg_free_ex(memory, file, line);
1459 #define mg_malloc(a) mg_malloc_ex(a, NULL, __FILE__, __LINE__)
1460 #define mg_calloc(a, b) mg_calloc_ex(a, b, NULL, __FILE__, __LINE__)
1461 #define mg_realloc(a, b) mg_realloc_ex(a, b, NULL, __FILE__, __LINE__)
1462 #define mg_free(a) mg_free_ex(a, __FILE__, __LINE__)
1464 #define mg_malloc_ctx(a, c) mg_malloc_ex(a, c, __FILE__, __LINE__)
1465 #define mg_calloc_ctx(a, b, c) mg_calloc_ex(a, b, c, __FILE__, __LINE__)
1466 #define mg_realloc_ctx(a, b, c) mg_realloc_ex(a, b, c, __FILE__, __LINE__)
1470 static __inline
void *
1476 static __inline
void *
1477 mg_calloc(
size_t a,
size_t b)
1479 return calloc(a, b);
1482 static __inline
void *
1483 mg_realloc(
void *a,
size_t b)
1485 return realloc(a, b);
1488 static __inline
void
1494 #define mg_malloc_ctx(a, c) mg_malloc(a)
1495 #define mg_calloc_ctx(a, b, c) mg_calloc(a, b)
1496 #define mg_realloc_ctx(a, b, c) mg_realloc(a, b)
1497 #define mg_free_ctx(a, c) mg_free(a)
1502 static void mg_vsnprintf(
const struct mg_connection *conn,
1509 static void mg_snprintf(
const struct mg_connection *conn,
1513 PRINTF_FORMAT_STRING(
const char *fmt),
1514 ...) PRINTF_ARGS(5, 6);
1524 #if defined(realloc)
1530 #if defined(snprintf)
1533 #if defined(vsnprintf)
1536 #define malloc DO_NOT_USE_THIS_FUNCTION__USE_mg_malloc
1537 #define calloc DO_NOT_USE_THIS_FUNCTION__USE_mg_calloc
1538 #define realloc DO_NOT_USE_THIS_FUNCTION__USE_mg_realloc
1539 #define free DO_NOT_USE_THIS_FUNCTION__USE_mg_free
1540 #define snprintf DO_NOT_USE_THIS_FUNCTION__USE_mg_snprintf
1544 #define vsnprintf DO_NOT_USE_THIS_FUNCTION__USE_mg_vsnprintf
1549 static int mg_init_library_called = 0;
1551 #if !defined(NO_SSL)
1552 static int mg_ssl_initialized = 0;
1555 static pthread_key_t sTlsKey;
1556 static int thread_idx_max = 0;
1558 #if defined(MG_LEGACY_INTERFACE)
1559 #define MG_ALLOW_USING_GET_REQUEST_INFO_FOR_RESPONSE
1562 struct mg_workerTLS {
1564 unsigned long thread_idx;
1566 HANDLE pthread_cond_helper_mutex;
1567 struct mg_workerTLS *next_waiting_thread;
1569 #if defined(MG_ALLOW_USING_GET_REQUEST_INFO_FOR_RESPONSE)
1575 #if defined(GCC_DIAGNOSTIC)
1577 #pragma GCC diagnostic push
1578 #pragma GCC diagnostic ignored "-Wunused-function"
1580 #if defined(__clang__)
1582 #pragma clang diagnostic push
1583 #pragma clang diagnostic ignored "-Wunused-function"
1597 FUNCTION_MAY_BE_UNUSED
1598 static unsigned long
1599 mg_current_thread_id(
void)
1602 return GetCurrentThreadId();
1605 #if defined(__clang__)
1606 #pragma clang diagnostic push
1607 #pragma clang diagnostic ignored "-Wunreachable-code"
1615 if (
sizeof(pthread_t) >
sizeof(
unsigned long)) {
1618 struct mg_workerTLS *tls =
1619 (
struct mg_workerTLS *)pthread_getspecific(sTlsKey);
1623 tls = (
struct mg_workerTLS *)mg_malloc(
sizeof(
struct mg_workerTLS));
1624 tls->is_master = -2;
1625 tls->thread_idx = (unsigned)mg_atomic_inc(&thread_idx_max);
1626 pthread_setspecific(sTlsKey, tls);
1628 return tls->thread_idx;
1633 unsigned long ret = 0;
1634 pthread_t t = pthread_self();
1635 memcpy(&ret, &t,
sizeof(pthread_t));
1639 #if defined(__clang__)
1640 #pragma clang diagnostic pop
1647 FUNCTION_MAY_BE_UNUSED
1649 mg_get_current_time_ns(
void)
1651 struct timespec tsnow;
1652 clock_gettime(CLOCK_REALTIME, &tsnow);
1653 return (((uint64_t)tsnow.tv_sec) * 1000000000) + (uint64_t)tsnow.tv_nsec;
1657 #if defined(GCC_DIAGNOSTIC)
1659 #pragma GCC diagnostic pop
1661 #if defined(__clang__)
1663 #pragma clang diagnostic pop
1667 #if defined(NEED_DEBUG_TRACE_FUNC)
1669 DEBUG_TRACE_FUNC(
const char *func,
unsigned line,
const char *fmt, ...)
1673 static uint64_t nslast;
1674 struct timespec tsnow;
1677 unsigned long thread_id = mg_current_thread_id();
1679 clock_gettime(CLOCK_REALTIME, &tsnow);
1680 nsnow = ((uint64_t)tsnow.tv_sec) * ((uint64_t)1000000000)
1681 + ((uint64_t)tsnow.tv_nsec);
1688 printf(
"*** %lu.%09lu %12" INT64_FMT
" %lu %s:%u: ",
1689 (
unsigned long)tsnow.tv_sec,
1690 (
unsigned long)tsnow.tv_nsec,
1695 va_start(args, fmt);
1700 funlockfile(stdout);
1706 #define MD5_STATIC static
1710 #if defined(NO_SOCKLEN_T)
1711 typedef int socklen_t;
1714 #define IP_ADDR_STR_LEN (50)
1716 #if !defined(MSG_NOSIGNAL)
1717 #define MSG_NOSIGNAL (0)
1722 typedef struct SSL SSL;
1723 typedef struct SSL_CTX SSL_CTX;
1725 #if defined(NO_SSL_DL)
1726 #include <openssl/bn.h>
1727 #include <openssl/conf.h>
1728 #include <openssl/crypto.h>
1729 #include <openssl/dh.h>
1730 #include <openssl/engine.h>
1731 #include <openssl/err.h>
1732 #include <openssl/opensslv.h>
1733 #include <openssl/pem.h>
1734 #include <openssl/ssl.h>
1735 #include <openssl/tls1.h>
1736 #include <openssl/x509.h>
1738 #if defined(WOLFSSL_VERSION)
1741 #include "wolfssl_extras.inl"
1744 #if (OPENSSL_VERSION_NUMBER >= 0x10100000L)
1746 #if !defined(OPENSSL_API_1_1)
1747 #define OPENSSL_API_1_1
1749 #define OPENSSL_REMOVE_THREAD_STATE()
1751 #define OPENSSL_REMOVE_THREAD_STATE() ERR_remove_thread_state(NULL)
1760 typedef struct ssl_st SSL;
1761 typedef struct ssl_method_st SSL_METHOD;
1762 typedef struct ssl_ctx_st SSL_CTX;
1763 typedef struct x509_store_ctx_st X509_STORE_CTX;
1764 typedef struct x509_name X509_NAME;
1765 typedef struct asn1_integer ASN1_INTEGER;
1766 typedef struct bignum BIGNUM;
1767 typedef struct ossl_init_settings_st OPENSSL_INIT_SETTINGS;
1768 typedef struct evp_md EVP_MD;
1769 typedef struct x509 X509;
1772 #define SSL_CTRL_OPTIONS (32)
1773 #define SSL_CTRL_CLEAR_OPTIONS (77)
1774 #define SSL_CTRL_SET_ECDH_AUTO (94)
1776 #define OPENSSL_INIT_NO_LOAD_SSL_STRINGS 0x00100000L
1777 #define OPENSSL_INIT_LOAD_SSL_STRINGS 0x00200000L
1778 #define OPENSSL_INIT_LOAD_CRYPTO_STRINGS 0x00000002L
1780 #define SSL_VERIFY_NONE (0)
1781 #define SSL_VERIFY_PEER (1)
1782 #define SSL_VERIFY_FAIL_IF_NO_PEER_CERT (2)
1783 #define SSL_VERIFY_CLIENT_ONCE (4)
1784 #define SSL_OP_ALL ((long)(0x80000BFFUL))
1785 #define SSL_OP_NO_SSLv2 (0x01000000L)
1786 #define SSL_OP_NO_SSLv3 (0x02000000L)
1787 #define SSL_OP_NO_TLSv1 (0x04000000L)
1788 #define SSL_OP_NO_TLSv1_2 (0x08000000L)
1789 #define SSL_OP_NO_TLSv1_1 (0x10000000L)
1790 #define SSL_OP_SINGLE_DH_USE (0x00100000L)
1791 #define SSL_OP_CIPHER_SERVER_PREFERENCE (0x00400000L)
1792 #define SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION (0x00010000L)
1793 #define SSL_OP_NO_COMPRESSION (0x00020000L)
1795 #define SSL_CB_HANDSHAKE_START (0x10)
1796 #define SSL_CB_HANDSHAKE_DONE (0x20)
1798 #define SSL_ERROR_NONE (0)
1799 #define SSL_ERROR_SSL (1)
1800 #define SSL_ERROR_WANT_READ (2)
1801 #define SSL_ERROR_WANT_WRITE (3)
1802 #define SSL_ERROR_WANT_X509_LOOKUP (4)
1803 #define SSL_ERROR_SYSCALL (5)
1804 #define SSL_ERROR_ZERO_RETURN (6)
1805 #define SSL_ERROR_WANT_CONNECT (7)
1806 #define SSL_ERROR_WANT_ACCEPT (8)
1808 #define TLSEXT_TYPE_server_name (0)
1809 #define TLSEXT_NAMETYPE_host_name (0)
1810 #define SSL_TLSEXT_ERR_OK (0)
1811 #define SSL_TLSEXT_ERR_ALERT_WARNING (1)
1812 #define SSL_TLSEXT_ERR_ALERT_FATAL (2)
1813 #define SSL_TLSEXT_ERR_NOACK (3)
1821 #if defined(OPENSSL_API_1_1)
1823 #define SSL_free (*(void (*)(SSL *))ssl_sw[0].ptr)
1824 #define SSL_accept (*(int (*)(SSL *))ssl_sw[1].ptr)
1825 #define SSL_connect (*(int (*)(SSL *))ssl_sw[2].ptr)
1826 #define SSL_read (*(int (*)(SSL *, void *, int))ssl_sw[3].ptr)
1827 #define SSL_write (*(int (*)(SSL *, const void *, int))ssl_sw[4].ptr)
1828 #define SSL_get_error (*(int (*)(SSL *, int))ssl_sw[5].ptr)
1829 #define SSL_set_fd (*(int (*)(SSL *, SOCKET))ssl_sw[6].ptr)
1830 #define SSL_new (*(SSL * (*)(SSL_CTX *)) ssl_sw[7].ptr)
1831 #define SSL_CTX_new (*(SSL_CTX * (*)(SSL_METHOD *)) ssl_sw[8].ptr)
1832 #define TLS_server_method (*(SSL_METHOD * (*)(void)) ssl_sw[9].ptr)
1833 #define OPENSSL_init_ssl \
1834 (*(int (*)(uint64_t opts, \
1835 const OPENSSL_INIT_SETTINGS *settings))ssl_sw[10] \
1837 #define SSL_CTX_use_PrivateKey_file \
1838 (*(int (*)(SSL_CTX *, const char *, int))ssl_sw[11].ptr)
1839 #define SSL_CTX_use_certificate_file \
1840 (*(int (*)(SSL_CTX *, const char *, int))ssl_sw[12].ptr)
1841 #define SSL_CTX_set_default_passwd_cb \
1842 (*(void (*)(SSL_CTX *, mg_callback_t))ssl_sw[13].ptr)
1843 #define SSL_CTX_free (*(void (*)(SSL_CTX *))ssl_sw[14].ptr)
1844 #define SSL_CTX_use_certificate_chain_file \
1845 (*(int (*)(SSL_CTX *, const char *))ssl_sw[15].ptr)
1846 #define TLS_client_method (*(SSL_METHOD * (*)(void)) ssl_sw[16].ptr)
1847 #define SSL_pending (*(int (*)(SSL *))ssl_sw[17].ptr)
1848 #define SSL_CTX_set_verify \
1849 (*(void (*)(SSL_CTX *, \
1851 int (*verify_callback)(int, X509_STORE_CTX *)))ssl_sw[18] \
1853 #define SSL_shutdown (*(int (*)(SSL *))ssl_sw[19].ptr)
1854 #define SSL_CTX_load_verify_locations \
1855 (*(int (*)(SSL_CTX *, const char *, const char *))ssl_sw[20].ptr)
1856 #define SSL_CTX_set_default_verify_paths (*(int (*)(SSL_CTX *))ssl_sw[21].ptr)
1857 #define SSL_CTX_set_verify_depth (*(void (*)(SSL_CTX *, int))ssl_sw[22].ptr)
1858 #define SSL_get_peer_certificate (*(X509 * (*)(SSL *)) ssl_sw[23].ptr)
1859 #define SSL_get_version (*(const char *(*)(SSL *))ssl_sw[24].ptr)
1860 #define SSL_get_current_cipher (*(SSL_CIPHER * (*)(SSL *)) ssl_sw[25].ptr)
1861 #define SSL_CIPHER_get_name \
1862 (*(const char *(*)(const SSL_CIPHER *))ssl_sw[26].ptr)
1863 #define SSL_CTX_check_private_key (*(int (*)(SSL_CTX *))ssl_sw[27].ptr)
1864 #define SSL_CTX_set_session_id_context \
1865 (*(int (*)(SSL_CTX *, const unsigned char *, unsigned int))ssl_sw[28].ptr)
1866 #define SSL_CTX_ctrl (*(long (*)(SSL_CTX *, int, long, void *))ssl_sw[29].ptr)
1867 #define SSL_CTX_set_cipher_list \
1868 (*(int (*)(SSL_CTX *, const char *))ssl_sw[30].ptr)
1869 #define SSL_CTX_set_options \
1870 (*(unsigned long (*)(SSL_CTX *, unsigned long))ssl_sw[31].ptr)
1871 #define SSL_CTX_set_info_callback \
1872 (*(void (*)(SSL_CTX * ctx, void (*callback)(const SSL *, int, int))) \
1875 #define SSL_get_ex_data (*(char *(*)(const SSL *, int))ssl_sw[33].ptr)
1876 #define SSL_set_ex_data (*(void (*)(SSL *, int, char *))ssl_sw[34].ptr)
1877 #define SSL_CTX_callback_ctrl \
1878 (*(long (*)(SSL_CTX *, int, void (*)(void)))ssl_sw[35].ptr)
1879 #define SSL_get_servername \
1880 (*(const char *(*)(const SSL *, int type))ssl_sw[36].ptr)
1881 #define SSL_set_SSL_CTX (*(SSL_CTX * (*)(SSL *, SSL_CTX *)) ssl_sw[37].ptr)
1882 #define SSL_ctrl (*(long (*)(SSL *, int, long, void *))ssl_sw[38].ptr)
1884 #define SSL_CTX_clear_options(ctx, op) \
1885 SSL_CTX_ctrl((ctx), SSL_CTRL_CLEAR_OPTIONS, (op), NULL)
1886 #define SSL_CTX_set_ecdh_auto(ctx, onoff) \
1887 SSL_CTX_ctrl(ctx, SSL_CTRL_SET_ECDH_AUTO, onoff, NULL)
1889 #define SSL_CTRL_SET_TLSEXT_SERVERNAME_CB 53
1890 #define SSL_CTRL_SET_TLSEXT_SERVERNAME_ARG 54
1891 #define SSL_CTRL_SET_TLSEXT_HOSTNAME 55
1892 #define SSL_CTX_set_tlsext_servername_callback(ctx, cb) \
1893 SSL_CTX_callback_ctrl(ctx, \
1894 SSL_CTRL_SET_TLSEXT_SERVERNAME_CB, \
1896 #define SSL_CTX_set_tlsext_servername_arg(ctx, arg) \
1897 SSL_CTX_ctrl(ctx, SSL_CTRL_SET_TLSEXT_SERVERNAME_ARG, 0, (void *)arg)
1898 #define SSL_set_tlsext_host_name(ctx, arg) \
1899 SSL_ctrl(ctx, SSL_CTRL_SET_TLSEXT_HOSTNAME, 0, (void *)arg)
1901 #define X509_get_notBefore(x) ((x)->cert_info->validity->notBefore)
1902 #define X509_get_notAfter(x) ((x)->cert_info->validity->notAfter)
1904 #define SSL_set_app_data(s, arg) (SSL_set_ex_data(s, 0, (char *)arg))
1905 #define SSL_get_app_data(s) (SSL_get_ex_data(s, 0))
1907 #define ERR_get_error (*(unsigned long (*)(void))crypto_sw[0].ptr)
1908 #define ERR_error_string (*(char *(*)(unsigned long, char *))crypto_sw[1].ptr)
1909 #define CONF_modules_unload (*(void (*)(int))crypto_sw[2].ptr)
1910 #define X509_free (*(void (*)(X509 *))crypto_sw[3].ptr)
1911 #define X509_get_subject_name (*(X509_NAME * (*)(X509 *)) crypto_sw[4].ptr)
1912 #define X509_get_issuer_name (*(X509_NAME * (*)(X509 *)) crypto_sw[5].ptr)
1913 #define X509_NAME_oneline \
1914 (*(char *(*)(X509_NAME *, char *, int))crypto_sw[6].ptr)
1915 #define X509_get_serialNumber (*(ASN1_INTEGER * (*)(X509 *)) crypto_sw[7].ptr)
1916 #define EVP_get_digestbyname \
1917 (*(const EVP_MD *(*)(const char *))crypto_sw[8].ptr)
1918 #define EVP_Digest \
1920 const void *, size_t, void *, unsigned int *, const EVP_MD *, void *)) \
1923 #define i2d_X509 (*(int (*)(X509 *, unsigned char **))crypto_sw[10].ptr)
1924 #define BN_bn2hex (*(char *(*)(const BIGNUM *a))crypto_sw[11].ptr)
1925 #define ASN1_INTEGER_to_BN \
1926 (*(BIGNUM * (*)(const ASN1_INTEGER *ai, BIGNUM *bn)) crypto_sw[12].ptr)
1927 #define BN_free (*(void (*)(const BIGNUM *a))crypto_sw[13].ptr)
1928 #define CRYPTO_free (*(void (*)(void *addr))crypto_sw[14].ptr)
1930 #define OPENSSL_free(a) CRYPTO_free(a)
1932 #define OPENSSL_REMOVE_THREAD_STATE()
1938 static struct ssl_func ssl_sw[] = {{
"SSL_free", NULL},
1939 {
"SSL_accept", NULL},
1940 {
"SSL_connect", NULL},
1942 {
"SSL_write", NULL},
1943 {
"SSL_get_error", NULL},
1944 {
"SSL_set_fd", NULL},
1946 {
"SSL_CTX_new", NULL},
1947 {
"TLS_server_method", NULL},
1948 {
"OPENSSL_init_ssl", NULL},
1949 {
"SSL_CTX_use_PrivateKey_file", NULL},
1950 {
"SSL_CTX_use_certificate_file", NULL},
1951 {
"SSL_CTX_set_default_passwd_cb", NULL},
1952 {
"SSL_CTX_free", NULL},
1953 {
"SSL_CTX_use_certificate_chain_file", NULL},
1954 {
"TLS_client_method", NULL},
1955 {
"SSL_pending", NULL},
1956 {
"SSL_CTX_set_verify", NULL},
1957 {
"SSL_shutdown", NULL},
1958 {
"SSL_CTX_load_verify_locations", NULL},
1959 {
"SSL_CTX_set_default_verify_paths", NULL},
1960 {
"SSL_CTX_set_verify_depth", NULL},
1961 {
"SSL_get_peer_certificate", NULL},
1962 {
"SSL_get_version", NULL},
1963 {
"SSL_get_current_cipher", NULL},
1964 {
"SSL_CIPHER_get_name", NULL},
1965 {
"SSL_CTX_check_private_key", NULL},
1966 {
"SSL_CTX_set_session_id_context", NULL},
1967 {
"SSL_CTX_ctrl", NULL},
1968 {
"SSL_CTX_set_cipher_list", NULL},
1969 {
"SSL_CTX_set_options", NULL},
1970 {
"SSL_CTX_set_info_callback", NULL},
1971 {
"SSL_get_ex_data", NULL},
1972 {
"SSL_set_ex_data", NULL},
1973 {
"SSL_CTX_callback_ctrl", NULL},
1974 {
"SSL_get_servername", NULL},
1975 {
"SSL_set_SSL_CTX", NULL},
1982 static struct ssl_func crypto_sw[] = {{
"ERR_get_error", NULL},
1983 {
"ERR_error_string", NULL},
1984 {
"CONF_modules_unload", NULL},
1985 {
"X509_free", NULL},
1986 {
"X509_get_subject_name", NULL},
1987 {
"X509_get_issuer_name", NULL},
1988 {
"X509_NAME_oneline", NULL},
1989 {
"X509_get_serialNumber", NULL},
1990 {
"EVP_get_digestbyname", NULL},
1991 {
"EVP_Digest", NULL},
1993 {
"BN_bn2hex", NULL},
1994 {
"ASN1_INTEGER_to_BN", NULL},
1996 {
"CRYPTO_free", NULL},
2000 #define SSL_free (*(void (*)(SSL *))ssl_sw[0].ptr)
2001 #define SSL_accept (*(int (*)(SSL *))ssl_sw[1].ptr)
2002 #define SSL_connect (*(int (*)(SSL *))ssl_sw[2].ptr)
2003 #define SSL_read (*(int (*)(SSL *, void *, int))ssl_sw[3].ptr)
2004 #define SSL_write (*(int (*)(SSL *, const void *, int))ssl_sw[4].ptr)
2005 #define SSL_get_error (*(int (*)(SSL *, int))ssl_sw[5].ptr)
2006 #define SSL_set_fd (*(int (*)(SSL *, SOCKET))ssl_sw[6].ptr)
2007 #define SSL_new (*(SSL * (*)(SSL_CTX *)) ssl_sw[7].ptr)
2008 #define SSL_CTX_new (*(SSL_CTX * (*)(SSL_METHOD *)) ssl_sw[8].ptr)
2009 #define SSLv23_server_method (*(SSL_METHOD * (*)(void)) ssl_sw[9].ptr)
2010 #define SSL_library_init (*(int (*)(void))ssl_sw[10].ptr)
2011 #define SSL_CTX_use_PrivateKey_file \
2012 (*(int (*)(SSL_CTX *, const char *, int))ssl_sw[11].ptr)
2013 #define SSL_CTX_use_certificate_file \
2014 (*(int (*)(SSL_CTX *, const char *, int))ssl_sw[12].ptr)
2015 #define SSL_CTX_set_default_passwd_cb \
2016 (*(void (*)(SSL_CTX *, mg_callback_t))ssl_sw[13].ptr)
2017 #define SSL_CTX_free (*(void (*)(SSL_CTX *))ssl_sw[14].ptr)
2018 #define SSL_load_error_strings (*(void (*)(void))ssl_sw[15].ptr)
2019 #define SSL_CTX_use_certificate_chain_file \
2020 (*(int (*)(SSL_CTX *, const char *))ssl_sw[16].ptr)
2021 #define SSLv23_client_method (*(SSL_METHOD * (*)(void)) ssl_sw[17].ptr)
2022 #define SSL_pending (*(int (*)(SSL *))ssl_sw[18].ptr)
2023 #define SSL_CTX_set_verify \
2024 (*(void (*)(SSL_CTX *, \
2026 int (*verify_callback)(int, X509_STORE_CTX *)))ssl_sw[19] \
2028 #define SSL_shutdown (*(int (*)(SSL *))ssl_sw[20].ptr)
2029 #define SSL_CTX_load_verify_locations \
2030 (*(int (*)(SSL_CTX *, const char *, const char *))ssl_sw[21].ptr)
2031 #define SSL_CTX_set_default_verify_paths (*(int (*)(SSL_CTX *))ssl_sw[22].ptr)
2032 #define SSL_CTX_set_verify_depth (*(void (*)(SSL_CTX *, int))ssl_sw[23].ptr)
2033 #define SSL_get_peer_certificate (*(X509 * (*)(SSL *)) ssl_sw[24].ptr)
2034 #define SSL_get_version (*(const char *(*)(SSL *))ssl_sw[25].ptr)
2035 #define SSL_get_current_cipher (*(SSL_CIPHER * (*)(SSL *)) ssl_sw[26].ptr)
2036 #define SSL_CIPHER_get_name \
2037 (*(const char *(*)(const SSL_CIPHER *))ssl_sw[27].ptr)
2038 #define SSL_CTX_check_private_key (*(int (*)(SSL_CTX *))ssl_sw[28].ptr)
2039 #define SSL_CTX_set_session_id_context \
2040 (*(int (*)(SSL_CTX *, const unsigned char *, unsigned int))ssl_sw[29].ptr)
2041 #define SSL_CTX_ctrl (*(long (*)(SSL_CTX *, int, long, void *))ssl_sw[30].ptr)
2042 #define SSL_CTX_set_cipher_list \
2043 (*(int (*)(SSL_CTX *, const char *))ssl_sw[31].ptr)
2044 #define SSL_CTX_set_info_callback \
2045 (*(void (*)(SSL_CTX *, void (*callback)(const SSL *, int, int)))ssl_sw[32] \
2047 #define SSL_get_ex_data (*(char *(*)(const SSL *, int))ssl_sw[33].ptr)
2048 #define SSL_set_ex_data (*(void (*)(SSL *, int, char *))ssl_sw[34].ptr)
2049 #define SSL_CTX_callback_ctrl \
2050 (*(long (*)(SSL_CTX *, int, void (*)(void)))ssl_sw[35].ptr)
2051 #define SSL_get_servername \
2052 (*(const char *(*)(const SSL *, int type))ssl_sw[36].ptr)
2053 #define SSL_set_SSL_CTX (*(SSL_CTX * (*)(SSL *, SSL_CTX *)) ssl_sw[37].ptr)
2054 #define SSL_ctrl (*(long (*)(SSL *, int, long, void *))ssl_sw[38].ptr)
2056 #define SSL_CTX_set_options(ctx, op) \
2057 SSL_CTX_ctrl((ctx), SSL_CTRL_OPTIONS, (op), NULL)
2058 #define SSL_CTX_clear_options(ctx, op) \
2059 SSL_CTX_ctrl((ctx), SSL_CTRL_CLEAR_OPTIONS, (op), NULL)
2060 #define SSL_CTX_set_ecdh_auto(ctx, onoff) \
2061 SSL_CTX_ctrl(ctx, SSL_CTRL_SET_ECDH_AUTO, onoff, NULL)
2063 #define SSL_CTRL_SET_TLSEXT_SERVERNAME_CB 53
2064 #define SSL_CTRL_SET_TLSEXT_SERVERNAME_ARG 54
2065 #define SSL_CTRL_SET_TLSEXT_HOSTNAME 55
2066 #define SSL_CTX_set_tlsext_servername_callback(ctx, cb) \
2067 SSL_CTX_callback_ctrl(ctx, \
2068 SSL_CTRL_SET_TLSEXT_SERVERNAME_CB, \
2070 #define SSL_CTX_set_tlsext_servername_arg(ctx, arg) \
2071 SSL_CTX_ctrl(ctx, SSL_CTRL_SET_TLSEXT_SERVERNAME_ARG, 0, (void *)arg)
2072 #define SSL_set_tlsext_host_name(ctx, arg) \
2073 SSL_ctrl(ctx, SSL_CTRL_SET_TLSEXT_HOSTNAME, 0, (void *)arg)
2075 #define X509_get_notBefore(x) ((x)->cert_info->validity->notBefore)
2076 #define X509_get_notAfter(x) ((x)->cert_info->validity->notAfter)
2078 #define SSL_set_app_data(s, arg) (SSL_set_ex_data(s, 0, (char *)arg))
2079 #define SSL_get_app_data(s) (SSL_get_ex_data(s, 0))
2081 #define CRYPTO_num_locks (*(int (*)(void))crypto_sw[0].ptr)
2082 #define CRYPTO_set_locking_callback \
2083 (*(void (*)(void (*)(int, int, const char *, int)))crypto_sw[1].ptr)
2084 #define CRYPTO_set_id_callback \
2085 (*(void (*)(unsigned long (*)(void)))crypto_sw[2].ptr)
2086 #define ERR_get_error (*(unsigned long (*)(void))crypto_sw[3].ptr)
2087 #define ERR_error_string (*(char *(*)(unsigned long, char *))crypto_sw[4].ptr)
2088 #define ERR_remove_state (*(void (*)(unsigned long))crypto_sw[5].ptr)
2089 #define ERR_free_strings (*(void (*)(void))crypto_sw[6].ptr)
2090 #define ENGINE_cleanup (*(void (*)(void))crypto_sw[7].ptr)
2091 #define CONF_modules_unload (*(void (*)(int))crypto_sw[8].ptr)
2092 #define CRYPTO_cleanup_all_ex_data (*(void (*)(void))crypto_sw[9].ptr)
2093 #define EVP_cleanup (*(void (*)(void))crypto_sw[10].ptr)
2094 #define X509_free (*(void (*)(X509 *))crypto_sw[11].ptr)
2095 #define X509_get_subject_name (*(X509_NAME * (*)(X509 *)) crypto_sw[12].ptr)
2096 #define X509_get_issuer_name (*(X509_NAME * (*)(X509 *)) crypto_sw[13].ptr)
2097 #define X509_NAME_oneline \
2098 (*(char *(*)(X509_NAME *, char *, int))crypto_sw[14].ptr)
2099 #define X509_get_serialNumber (*(ASN1_INTEGER * (*)(X509 *)) crypto_sw[15].ptr)
2100 #define i2c_ASN1_INTEGER \
2101 (*(int (*)(ASN1_INTEGER *, unsigned char **))crypto_sw[16].ptr)
2102 #define EVP_get_digestbyname \
2103 (*(const EVP_MD *(*)(const char *))crypto_sw[17].ptr)
2104 #define EVP_Digest \
2106 const void *, size_t, void *, unsigned int *, const EVP_MD *, void *)) \
2109 #define i2d_X509 (*(int (*)(X509 *, unsigned char **))crypto_sw[19].ptr)
2110 #define BN_bn2hex (*(char *(*)(const BIGNUM *a))crypto_sw[20].ptr)
2111 #define ASN1_INTEGER_to_BN \
2112 (*(BIGNUM * (*)(const ASN1_INTEGER *ai, BIGNUM *bn)) crypto_sw[21].ptr)
2113 #define BN_free (*(void (*)(const BIGNUM *a))crypto_sw[22].ptr)
2114 #define CRYPTO_free (*(void (*)(void *addr))crypto_sw[23].ptr)
2116 #define OPENSSL_free(a) CRYPTO_free(a)
2121 #define OPENSSL_REMOVE_THREAD_STATE() ERR_remove_state(0)
2127 static struct ssl_func ssl_sw[] = {{
"SSL_free", NULL},
2128 {
"SSL_accept", NULL},
2129 {
"SSL_connect", NULL},
2131 {
"SSL_write", NULL},
2132 {
"SSL_get_error", NULL},
2133 {
"SSL_set_fd", NULL},
2135 {
"SSL_CTX_new", NULL},
2136 {
"SSLv23_server_method", NULL},
2137 {
"SSL_library_init", NULL},
2138 {
"SSL_CTX_use_PrivateKey_file", NULL},
2139 {
"SSL_CTX_use_certificate_file", NULL},
2140 {
"SSL_CTX_set_default_passwd_cb", NULL},
2141 {
"SSL_CTX_free", NULL},
2142 {
"SSL_load_error_strings", NULL},
2143 {
"SSL_CTX_use_certificate_chain_file", NULL},
2144 {
"SSLv23_client_method", NULL},
2145 {
"SSL_pending", NULL},
2146 {
"SSL_CTX_set_verify", NULL},
2147 {
"SSL_shutdown", NULL},
2148 {
"SSL_CTX_load_verify_locations", NULL},
2149 {
"SSL_CTX_set_default_verify_paths", NULL},
2150 {
"SSL_CTX_set_verify_depth", NULL},
2151 {
"SSL_get_peer_certificate", NULL},
2152 {
"SSL_get_version", NULL},
2153 {
"SSL_get_current_cipher", NULL},
2154 {
"SSL_CIPHER_get_name", NULL},
2155 {
"SSL_CTX_check_private_key", NULL},
2156 {
"SSL_CTX_set_session_id_context", NULL},
2157 {
"SSL_CTX_ctrl", NULL},
2158 {
"SSL_CTX_set_cipher_list", NULL},
2159 {
"SSL_CTX_set_info_callback", NULL},
2160 {
"SSL_get_ex_data", NULL},
2161 {
"SSL_set_ex_data", NULL},
2162 {
"SSL_CTX_callback_ctrl", NULL},
2163 {
"SSL_get_servername", NULL},
2164 {
"SSL_set_SSL_CTX", NULL},
2171 static struct ssl_func crypto_sw[] = {{
"CRYPTO_num_locks", NULL},
2172 {
"CRYPTO_set_locking_callback", NULL},
2173 {
"CRYPTO_set_id_callback", NULL},
2174 {
"ERR_get_error", NULL},
2175 {
"ERR_error_string", NULL},
2176 {
"ERR_remove_state", NULL},
2177 {
"ERR_free_strings", NULL},
2178 {
"ENGINE_cleanup", NULL},
2179 {
"CONF_modules_unload", NULL},
2180 {
"CRYPTO_cleanup_all_ex_data", NULL},
2181 {
"EVP_cleanup", NULL},
2182 {
"X509_free", NULL},
2183 {
"X509_get_subject_name", NULL},
2184 {
"X509_get_issuer_name", NULL},
2185 {
"X509_NAME_oneline", NULL},
2186 {
"X509_get_serialNumber", NULL},
2187 {
"i2c_ASN1_INTEGER", NULL},
2188 {
"EVP_get_digestbyname", NULL},
2189 {
"EVP_Digest", NULL},
2191 {
"BN_bn2hex", NULL},
2192 {
"ASN1_INTEGER_to_BN", NULL},
2194 {
"CRYPTO_free", NULL},
2201 #if !defined(NO_CACHING)
2202 static const char *month_names[] = {
"Jan",
2221 struct sockaddr_in sin;
2222 #if defined(USE_IPV6)
2223 struct sockaddr_in6 sin6;
2233 struct mg_file_stat {
2236 time_t last_modified;
2243 struct mg_file_in_memory {
2249 struct mg_file_access {
2252 #if defined(MG_USE_OPEN_FILE)
2263 struct mg_file_stat stat;
2264 struct mg_file_access access;
2267 #if defined(MG_USE_OPEN_FILE)
2269 #define STRUCT_FILE_INITIALIZER \
2271 {(uint64_t)0, (time_t)0, 0, 0, 0}, \
2273 (FILE *)NULL, (const char *)NULL \
2279 #define STRUCT_FILE_INITIALIZER \
2281 {(uint64_t)0, (time_t)0, 0, 0, 0}, \
2296 unsigned char is_ssl;
2297 unsigned char ssl_redir;
2299 unsigned char in_use;
2317 #if defined(__linux__)
2318 ALLOW_SENDFILE_CALL,
2321 CASE_SENSITIVE_FILES,
2329 #if defined(USE_WEBSOCKET)
2331 ENABLE_WEBSOCKET_PING_PONG,
2334 #if defined(USE_LUA)
2335 LUA_BACKGROUND_SCRIPT,
2336 LUA_BACKGROUND_SCRIPT_PARAMS,
2338 #if defined(USE_TIMERS)
2346 PUT_DELETE_PASSWORDS_FILE,
2349 AUTHENTICATION_DOMAIN,
2350 ENABLE_AUTH_DOMAIN_CHECK,
2352 ENABLE_DIRECTORY_LISTING,
2353 GLOBAL_PASSWORDS_FILE,
2355 ACCESS_CONTROL_LIST,
2358 SSL_CERTIFICATE_CHAIN,
2359 URL_REWRITE_PATTERN,
2365 SSL_DEFAULT_VERIFY_PATHS,
2367 SSL_PROTOCOL_VERSION,
2370 #if defined(USE_LUA)
2372 LUA_SCRIPT_EXTENSIONS,
2373 LUA_SERVER_PAGE_EXTENSIONS,
2374 #if defined(MG_EXPERIMENTAL_INTERFACES)
2378 #if defined(USE_DUKTAPE)
2379 DUKTAPE_SCRIPT_EXTENSIONS,
2382 #if defined(USE_WEBSOCKET)
2385 #if defined(USE_LUA) && defined(USE_WEBSOCKET)
2386 LUA_WEBSOCKET_EXTENSIONS,
2389 ACCESS_CONTROL_ALLOW_ORIGIN,
2390 ACCESS_CONTROL_ALLOW_METHODS,
2391 ACCESS_CONTROL_ALLOW_HEADERS,
2393 #if !defined(NO_CACHING)
2394 STATIC_FILE_MAX_AGE,
2396 #if !defined(NO_SSL)
2397 STRICT_HTTPS_MAX_AGE,
2400 ALLOW_INDEX_SCRIPT_SUB_RES,
2409 static const struct mg_option config_options[] = {
2412 {
"listening_ports", MG_CONFIG_TYPE_STRING_LIST,
"8080"},
2413 {
"num_threads", MG_CONFIG_TYPE_NUMBER,
"50"},
2414 {
"run_as_user", MG_CONFIG_TYPE_STRING, NULL},
2415 {
"tcp_nodelay", MG_CONFIG_TYPE_NUMBER,
"0"},
2416 {
"max_request_size", MG_CONFIG_TYPE_NUMBER,
"16384"},
2417 {
"linger_timeout_ms", MG_CONFIG_TYPE_NUMBER, NULL},
2418 #if defined(__linux__)
2419 {
"allow_sendfile_call", MG_CONFIG_TYPE_BOOLEAN,
"yes"},
2422 {
"case_sensitive", MG_CONFIG_TYPE_BOOLEAN,
"no"},
2424 {
"throttle", MG_CONFIG_TYPE_STRING_LIST, NULL},
2425 {
"access_log_file", MG_CONFIG_TYPE_FILE, NULL},
2426 {
"error_log_file", MG_CONFIG_TYPE_FILE, NULL},
2427 {
"enable_keep_alive", MG_CONFIG_TYPE_BOOLEAN,
"no"},
2428 {
"request_timeout_ms", MG_CONFIG_TYPE_NUMBER,
"30000"},
2429 {
"keep_alive_timeout_ms", MG_CONFIG_TYPE_NUMBER,
"500"},
2430 #if defined(USE_WEBSOCKET)
2431 {
"websocket_timeout_ms", MG_CONFIG_TYPE_NUMBER, NULL},
2432 {
"enable_websocket_ping_pong", MG_CONFIG_TYPE_BOOLEAN,
"no"},
2434 {
"decode_url", MG_CONFIG_TYPE_BOOLEAN,
"yes"},
2435 #if defined(USE_LUA)
2436 {
"lua_background_script", MG_CONFIG_TYPE_FILE, NULL},
2437 {
"lua_background_script_params", MG_CONFIG_TYPE_STRING_LIST, NULL},
2439 #if defined(USE_TIMERS)
2440 {
"cgi_timeout_ms", MG_CONFIG_TYPE_NUMBER, NULL},
2444 {
"document_root", MG_CONFIG_TYPE_DIRECTORY, NULL},
2445 {
"cgi_pattern", MG_CONFIG_TYPE_EXT_PATTERN,
"**.cgi$|**.pl$|**.php$"},
2446 {
"cgi_environment", MG_CONFIG_TYPE_STRING_LIST, NULL},
2447 {
"put_delete_auth_file", MG_CONFIG_TYPE_FILE, NULL},
2448 {
"cgi_interpreter", MG_CONFIG_TYPE_FILE, NULL},
2449 {
"protect_uri", MG_CONFIG_TYPE_STRING_LIST, NULL},
2450 {
"authentication_domain", MG_CONFIG_TYPE_STRING,
"mydomain.com"},
2451 {
"enable_auth_domain_check", MG_CONFIG_TYPE_BOOLEAN,
"yes"},
2452 {
"ssi_pattern", MG_CONFIG_TYPE_EXT_PATTERN,
"**.shtml$|**.shtm$"},
2453 {
"enable_directory_listing", MG_CONFIG_TYPE_BOOLEAN,
"yes"},
2454 {
"global_auth_file", MG_CONFIG_TYPE_FILE, NULL},
2456 MG_CONFIG_TYPE_STRING_LIST,
2457 #if defined(USE_LUA)
2458 "index.xhtml,index.html,index.htm,"
2459 "index.lp,index.lsp,index.lua,index.cgi,"
2460 "index.shtml,index.php"},
2462 "index.xhtml,index.html,index.htm,index.cgi,index.shtml,index.php"},
2464 {
"access_control_list", MG_CONFIG_TYPE_STRING_LIST, NULL},
2465 {
"extra_mime_types", MG_CONFIG_TYPE_STRING_LIST, NULL},
2466 {
"ssl_certificate", MG_CONFIG_TYPE_FILE, NULL},
2467 {
"ssl_certificate_chain", MG_CONFIG_TYPE_FILE, NULL},
2468 {
"url_rewrite_patterns", MG_CONFIG_TYPE_STRING_LIST, NULL},
2469 {
"hide_files_patterns", MG_CONFIG_TYPE_EXT_PATTERN, NULL},
2471 {
"ssl_verify_peer", MG_CONFIG_TYPE_YES_NO_OPTIONAL,
"no"},
2473 {
"ssl_ca_path", MG_CONFIG_TYPE_DIRECTORY, NULL},
2474 {
"ssl_ca_file", MG_CONFIG_TYPE_FILE, NULL},
2475 {
"ssl_verify_depth", MG_CONFIG_TYPE_NUMBER,
"9"},
2476 {
"ssl_default_verify_paths", MG_CONFIG_TYPE_BOOLEAN,
"yes"},
2477 {
"ssl_cipher_list", MG_CONFIG_TYPE_STRING, NULL},
2478 {
"ssl_protocol_version", MG_CONFIG_TYPE_NUMBER,
"0"},
2479 {
"ssl_short_trust", MG_CONFIG_TYPE_BOOLEAN,
"no"},
2481 #if defined(USE_LUA)
2482 {
"lua_preload_file", MG_CONFIG_TYPE_FILE, NULL},
2483 {
"lua_script_pattern", MG_CONFIG_TYPE_EXT_PATTERN,
"**.lua$"},
2484 {
"lua_server_page_pattern", MG_CONFIG_TYPE_EXT_PATTERN,
"**.lp$|**.lsp$"},
2485 #if defined(MG_EXPERIMENTAL_INTERFACES)
2486 {
"lua_debug", MG_CONFIG_TYPE_STRING, NULL},
2489 #if defined(USE_DUKTAPE)
2492 {
"duktape_script_pattern", MG_CONFIG_TYPE_EXT_PATTERN,
"**.ssjs$"},
2495 #if defined(USE_WEBSOCKET)
2496 {
"websocket_root", MG_CONFIG_TYPE_DIRECTORY, NULL},
2498 #if defined(USE_LUA) && defined(USE_WEBSOCKET)
2499 {
"lua_websocket_pattern", MG_CONFIG_TYPE_EXT_PATTERN,
"**.lua$"},
2501 {
"access_control_allow_origin", MG_CONFIG_TYPE_STRING,
"*"},
2502 {
"access_control_allow_methods", MG_CONFIG_TYPE_STRING,
"*"},
2503 {
"access_control_allow_headers", MG_CONFIG_TYPE_STRING,
"*"},
2504 {
"error_pages", MG_CONFIG_TYPE_DIRECTORY, NULL},
2505 #if !defined(NO_CACHING)
2506 {
"static_file_max_age", MG_CONFIG_TYPE_NUMBER,
"3600"},
2508 #if !defined(NO_SSL)
2509 {
"strict_transport_security_max_age", MG_CONFIG_TYPE_NUMBER, NULL},
2511 {
"additional_header", MG_CONFIG_TYPE_STRING_MULTILINE, NULL},
2512 {
"allow_index_script_resource", MG_CONFIG_TYPE_BOOLEAN,
"no"},
2514 {NULL, MG_CONFIG_TYPE_UNKNOWN, NULL}};
2519 mg_static_assert((
sizeof(config_options) /
sizeof(config_options[0]))
2520 == (NUM_OPTIONS + 1),
2521 "config_options and enum not sync");
2524 enum { REQUEST_HANDLER, WEBSOCKET_HANDLER, AUTH_HANDLER };
2527 struct mg_handler_info {
2536 mg_request_handler handler;
2537 unsigned int refcount;
2538 pthread_mutex_t refcount_mutex;
2543 mg_websocket_connect_handler connect_handler;
2544 mg_websocket_ready_handler ready_handler;
2545 mg_websocket_data_handler data_handler;
2546 mg_websocket_close_handler close_handler;
2549 struct mg_websocket_subprotocols *subprotocols;
2552 mg_authorization_handler auth_handler;
2558 struct mg_handler_info *next;
2565 CONTEXT_HTTP_CLIENT,
2570 struct mg_domain_context {
2572 char *config[NUM_OPTIONS];
2573 struct mg_handler_info *handlers;
2576 uint64_t auth_nonce_mask;
2577 unsigned long nonce_count;
2579 #if defined(USE_LUA) && defined(USE_WEBSOCKET)
2581 struct mg_shared_lua_websocket_list *shared_lua_websockets;
2585 struct mg_domain_context *next;
2600 struct socket *listening_sockets;
2601 struct pollfd *listening_socket_fds;
2602 unsigned int num_listening_sockets;
2604 struct mg_connection *worker_connections;
2607 #if defined(USE_SERVER_STATS)
2608 int active_connections;
2609 int max_connections;
2610 int64_t total_connections;
2611 int64_t total_requests;
2612 int64_t total_data_read;
2613 int64_t total_data_written;
2617 volatile int stop_flag;
2618 pthread_mutex_t thread_mutex;
2620 pthread_t masterthreadid;
2623 pthread_t *worker_threadids;
2626 #if defined(ALTERNATIVE_QUEUE)
2627 struct socket *client_socks;
2628 void **client_wait_events;
2630 struct socket queue[MGSQLEN];
2631 volatile int sq_head;
2632 volatile int sq_tail;
2633 pthread_cond_t sq_full;
2634 pthread_cond_t sq_empty;
2638 unsigned int max_request_size;
2640 #if defined(USE_SERVER_STATS)
2641 struct mg_memory_stat ctx_memory;
2649 #if defined(USE_TIMERS)
2650 struct ttimers *timers;
2654 #if defined(USE_LUA)
2655 void *lua_background_state;
2659 pthread_mutex_t nonce_mutex;
2662 struct mg_callbacks callbacks;
2672 struct mg_domain_context dd;
2676 #if defined(USE_SERVER_STATS)
2677 static struct mg_memory_stat mg_common_memory = {0, 0, 0};
2679 static struct mg_memory_stat *
2680 get_memory_stat(
struct mg_context *ctx)
2683 return &(ctx->ctx_memory);
2685 return &mg_common_memory;
2690 CONNECTION_TYPE_INVALID,
2691 CONNECTION_TYPE_REQUEST,
2692 CONNECTION_TYPE_RESPONSE
2695 struct mg_connection {
2696 int connection_type;
2698 struct mg_request_info request_info;
2699 struct mg_response_info response_info;
2701 struct mg_context *phys_ctx;
2702 struct mg_domain_context *dom_ctx;
2704 #if defined(USE_SERVER_STATS)
2712 SSL_CTX *client_ssl_ctx;
2713 struct socket client;
2714 time_t conn_birth_time;
2716 struct timespec req_time;
2718 int64_t num_bytes_sent;
2719 int64_t content_len;
2720 int64_t consumed_content;
2727 size_t chunk_remainder;
2733 int in_error_handler;
2735 #if defined(USE_WEBSOCKET)
2736 int in_websocket_handling;
2738 int handled_requests;
2747 time_t last_throttle_time;
2748 int64_t last_throttle_bytes;
2749 pthread_mutex_t mutex;
2751 #if defined(USE_LUA) && defined(USE_WEBSOCKET)
2752 void *lua_websocket_state;
2761 struct mg_connection *conn;
2763 struct mg_file_stat file;
2767 #if defined(USE_WEBSOCKET)
2768 static int is_websocket_protocol(
const struct mg_connection *conn);
2770 #define is_websocket_protocol(conn) (0)
2774 #define mg_cry_internal(conn, fmt, ...) \
2775 mg_cry_internal_wrap(conn, __func__, __LINE__, fmt, __VA_ARGS__)
2777 static void mg_cry_internal_wrap(
const struct mg_connection *conn,
2781 ...) PRINTF_ARGS(4, 5);
2784 #if !defined(NO_THREAD_NAME)
2785 #if defined(_WIN32) && defined(_MSC_VER)
2789 #pragma pack(push, 8)
2790 typedef struct tagTHREADNAME_INFO {
2798 #elif defined(__linux__)
2800 #include <sys/prctl.h>
2801 #include <sys/sendfile.h>
2802 #if defined(ALTERNATIVE_QUEUE)
2803 #include <sys/eventfd.h>
2807 #if defined(ALTERNATIVE_QUEUE)
2812 int evhdl = eventfd(0, EFD_CLOEXEC);
2821 ret = (
int *)mg_malloc(
sizeof(
int));
2833 event_wait(
void *eventhdl)
2842 evhdl = *(
int *)eventhdl;
2844 s = (int)read(evhdl, &u,
sizeof(u));
2845 if (s !=
sizeof(u)) {
2855 event_signal(
void *eventhdl)
2864 evhdl = *(
int *)eventhdl;
2866 s = (int)write(evhdl, &u,
sizeof(u));
2867 if (s !=
sizeof(u)) {
2876 event_destroy(
void *eventhdl)
2884 evhdl = *(
int *)eventhdl;
2896 #if !defined(__linux__) && !defined(_WIN32) && defined(ALTERNATIVE_QUEUE)
2898 struct posix_event {
2899 pthread_mutex_t mutex;
2900 pthread_cond_t cond;
2907 struct posix_event *ret = mg_malloc(
sizeof(
struct posix_event));
2912 if (0 != pthread_mutex_init(&(ret->mutex), NULL)) {
2917 if (0 != pthread_cond_init(&(ret->cond), NULL)) {
2919 pthread_mutex_destroy(&(ret->mutex));
2928 event_wait(
void *eventhdl)
2930 struct posix_event *ev = (
struct posix_event *)eventhdl;
2931 pthread_mutex_lock(&(ev->mutex));
2932 pthread_cond_wait(&(ev->cond), &(ev->mutex));
2933 pthread_mutex_unlock(&(ev->mutex));
2939 event_signal(
void *eventhdl)
2941 struct posix_event *ev = (
struct posix_event *)eventhdl;
2942 pthread_mutex_lock(&(ev->mutex));
2943 pthread_cond_signal(&(ev->cond));
2944 pthread_mutex_unlock(&(ev->mutex));
2950 event_destroy(
void *eventhdl)
2952 struct posix_event *ev = (
struct posix_event *)eventhdl;
2953 pthread_cond_destroy(&(ev->cond));
2954 pthread_mutex_destroy(&(ev->mutex));
2961 mg_set_thread_name(
const char *name)
2963 char threadName[16 + 1];
2966 NULL, NULL, threadName,
sizeof(threadName),
"civetweb-%s", name);
2969 #if defined(_MSC_VER)
2972 THREADNAME_INFO info;
2973 info.dwType = 0x1000;
2974 info.szName = threadName;
2975 info.dwThreadID = ~0U;
2978 RaiseException(0x406D1388,
2980 sizeof(info) /
sizeof(ULONG_PTR),
2981 (ULONG_PTR *)&info);
2982 } __except (EXCEPTION_EXECUTE_HANDLER) {
2984 #elif defined(__MINGW32__)
2987 #elif defined(_GNU_SOURCE) && defined(__GLIBC__) \
2988 && ((__GLIBC__ > 2) || ((__GLIBC__ == 2) && (__GLIBC_MINOR__ >= 12)))
2990 #if defined(__MACH__)
2992 (void)pthread_setname_np(threadName);
2994 (void)pthread_setname_np(pthread_self(), threadName);
2996 #elif defined(__linux__)
2998 (void)prctl(PR_SET_NAME, threadName, 0, 0, 0);
3003 mg_set_thread_name(
const char *threadName)
3009 #if defined(MG_LEGACY_INTERFACE)
3011 mg_get_valid_option_names(
void)
3015 *data[2 *
sizeof(config_options) /
sizeof(config_options[0])] = {0};
3018 for (i = 0; config_options[i].name != NULL; i++) {
3019 data[i * 2] = config_options[i].name;
3020 data[i * 2 + 1] = config_options[i].default_value;
3028 const struct mg_option *
3029 mg_get_valid_options(
void)
3031 return config_options;
3036 #define MG_FOPEN_MODE_NONE (0)
3039 #define MG_FOPEN_MODE_READ (1)
3042 #define MG_FOPEN_MODE_WRITE (2)
3045 #define MG_FOPEN_MODE_APPEND (4)
3052 open_file_in_memory(
const struct mg_connection *conn,
3054 struct mg_file *filep,
3057 #if defined(MG_USE_OPEN_FILE)
3060 const char *buf = NULL;
3065 if ((mode != MG_FOPEN_MODE_NONE) && (mode != MG_FOPEN_MODE_READ)) {
3069 if (conn->phys_ctx->callbacks.open_file) {
3070 buf = conn->phys_ctx->callbacks.open_file(conn, path, &size);
3072 if (filep == NULL) {
3083 filep->access.membuf = buf;
3084 filep->access.fp = NULL;
3087 filep->stat.size = size;
3091 filep->stat.last_modified = time(NULL);
3093 filep->stat.is_directory = 0;
3094 filep->stat.is_gzipped = 0;
3098 return (buf != NULL);
3113 is_file_in_memory(
const struct mg_connection *conn,
const char *path)
3115 return open_file_in_memory(conn, path, NULL, MG_FOPEN_MODE_NONE);
3120 is_file_opened(
const struct mg_file_access *fileacc)
3126 #if defined(MG_USE_OPEN_FILE)
3127 return (fileacc->membuf != NULL) || (fileacc->fp != NULL);
3129 return (fileacc->fp != NULL);
3134 static int mg_stat(
const struct mg_connection *conn,
3136 struct mg_file_stat *filep);
3146 mg_fopen(
const struct mg_connection *conn,
3149 struct mg_file *filep)
3156 filep->access.fp = NULL;
3157 #if defined(MG_USE_OPEN_FILE)
3158 filep->access.membuf = NULL;
3161 if (!is_file_in_memory(conn, path)) {
3165 found = mg_stat(conn, path, &(filep->stat));
3167 if ((mode == MG_FOPEN_MODE_READ) && (!found)) {
3174 wchar_t wbuf[W_PATH_MAX];
3175 path_to_unicode(conn, path, wbuf, ARRAY_SIZE(wbuf));
3177 case MG_FOPEN_MODE_READ:
3178 filep->access.fp = _wfopen(wbuf, L
"rb");
3180 case MG_FOPEN_MODE_WRITE:
3181 filep->access.fp = _wfopen(wbuf, L
"wb");
3183 case MG_FOPEN_MODE_APPEND:
3184 filep->access.fp = _wfopen(wbuf, L
"ab");
3191 case MG_FOPEN_MODE_READ:
3192 filep->access.fp = fopen(path,
"r");
3194 case MG_FOPEN_MODE_WRITE:
3195 filep->access.fp = fopen(path,
"w");
3197 case MG_FOPEN_MODE_APPEND:
3198 filep->access.fp = fopen(path,
"a");
3207 found = mg_stat(conn, path, &(filep->stat));
3212 return (filep->access.fp != NULL);
3215 #if defined(MG_USE_OPEN_FILE)
3217 if (open_file_in_memory(conn, path, filep, mode)) {
3219 return (filep->access.membuf != NULL);
3231 mg_fclose(
struct mg_file_access *fileacc)
3234 if (fileacc != NULL) {
3235 if (fileacc->fp != NULL) {
3236 ret = fclose(fileacc->fp);
3237 #if defined(MG_USE_OPEN_FILE)
3238 }
else if (fileacc->membuf != NULL) {
3243 memset(fileacc, 0,
sizeof(*fileacc));
3250 mg_strlcpy(
register char *dst,
register const char *src,
size_t n)
3252 for (; *src !=
'\0' && n > 1; n--) {
3260 lowercase(
const char *s)
3262 return tolower(*(
const unsigned char *)s);
3267 mg_strncasecmp(
const char *s1,
const char *s2,
size_t len)
3273 diff = lowercase(s1++) - lowercase(s2++);
3274 }
while (diff == 0 && s1[-1] !=
'\0' && --len > 0);
3282 mg_strcasecmp(
const char *s1,
const char *s2)
3287 diff = lowercase(s1++) - lowercase(s2++);
3288 }
while (diff == 0 && s1[-1] !=
'\0');
3295 mg_strndup_ctx(
const char *ptr,
size_t len,
struct mg_context *ctx)
3301 if ((p = (
char *)mg_malloc_ctx(len + 1, ctx)) != NULL) {
3302 mg_strlcpy(p, ptr, len + 1);
3310 mg_strdup_ctx(
const char *str,
struct mg_context *ctx)
3312 return mg_strndup_ctx(str, strlen(str), ctx);
3316 mg_strdup(
const char *str)
3318 return mg_strndup_ctx(str, strlen(str), NULL);
3323 mg_strcasestr(
const char *big_str,
const char *small_str)
3325 size_t i, big_len = strlen(big_str), small_len = strlen(small_str);
3327 if (big_len >= small_len) {
3328 for (i = 0; i <= (big_len - small_len); i++) {
3329 if (mg_strncasecmp(big_str + i, small_str, small_len) == 0) {
3342 mg_vsnprintf(
const struct mg_connection *conn,
3358 #if defined(__clang__)
3359 #pragma clang diagnostic push
3360 #pragma clang diagnostic ignored "-Wformat-nonliteral"
3365 n = (int)vsnprintf_impl(buf, buflen, fmt, ap);
3366 ok = (n >= 0) && ((
size_t)n < buflen);
3368 #if defined(__clang__)
3369 #pragma clang diagnostic pop
3380 mg_cry_internal(conn,
3381 "truncating vsnprintf buffer: [%.*s]",
3382 (
int)((buflen > 200) ? 200 : (buflen - 1)),
3384 n = (int)buflen - 1;
3391 mg_snprintf(
const struct mg_connection *conn,
3401 mg_vsnprintf(conn, truncated, buf, buflen, fmt, ap);
3407 get_option_index(
const char *name)
3411 for (i = 0; config_options[i].name != NULL; i++) {
3412 if (strcmp(config_options[i].name, name) == 0) {
3421 mg_get_option(
const struct mg_context *ctx,
const char *name)
3424 if ((i = get_option_index(name)) == -1) {
3426 }
else if (!ctx || ctx->dd.config[i] == NULL) {
3429 return ctx->dd.config[i];
3433 #define mg_get_option DO_NOT_USE_THIS_FUNCTION_INTERNALLY__access_directly
3436 mg_get_context(
const struct mg_connection *conn)
3438 return (conn == NULL) ? (
struct mg_context *)NULL : (conn->phys_ctx);
3443 mg_get_user_data(
const struct mg_context *ctx)
3445 return (ctx == NULL) ? NULL : ctx->user_data;
3450 mg_set_user_connection_data(
struct mg_connection *conn,
void *data)
3453 conn->request_info.conn_data = data;
3459 mg_get_user_connection_data(
const struct mg_connection *conn)
3462 return conn->request_info.conn_data;
3468 #if defined(MG_LEGACY_INTERFACE)
3471 mg_get_ports(
const struct mg_context *ctx,
size_t size,
int *ports,
int *ssl)
3477 for (i = 0; i < size && i < ctx->num_listening_sockets; i++) {
3478 ssl[i] = ctx->listening_sockets[i].is_ssl;
3480 #if defined(USE_IPV6)
3481 (ctx->listening_sockets[i].lsa.sa.sa_family == AF_INET6)
3482 ? ntohs(ctx->listening_sockets[i].lsa.sin6.sin6_port)
3485 ntohs(ctx->listening_sockets[i].lsa.sin.sin_port);
3493 mg_get_server_ports(
const struct mg_context *ctx,
3495 struct mg_server_ports *ports)
3502 memset(ports, 0,
sizeof(*ports) * (
size_t)size);
3506 if (!ctx->listening_sockets) {
3510 for (i = 0; (i < size) && (i < (
int)ctx->num_listening_sockets); i++) {
3513 #if defined(USE_IPV6)
3514 (ctx->listening_sockets[i].lsa.sa.sa_family == AF_INET6)
3515 ? ntohs(ctx->listening_sockets[i].lsa.sin6.sin6_port)
3518 ntohs(ctx->listening_sockets[i].lsa.sin.sin_port);
3519 ports[cnt].is_ssl = ctx->listening_sockets[i].is_ssl;
3520 ports[cnt].is_redirect = ctx->listening_sockets[i].ssl_redir;
3522 if (ctx->listening_sockets[i].lsa.sa.sa_family == AF_INET) {
3524 ports[cnt].protocol = 1;
3526 }
else if (ctx->listening_sockets[i].lsa.sa.sa_family == AF_INET6) {
3528 ports[cnt].protocol = 3;
3538 sockaddr_to_string(
char *buf,
size_t len,
const union usa *usa)
3546 if (usa->sa.sa_family == AF_INET) {
3547 getnameinfo(&usa->sa,
3555 #if defined(USE_IPV6)
3556 else if (usa->sa.sa_family == AF_INET6) {
3557 getnameinfo(&usa->sa,
3572 gmt_time_string(
char *buf,
size_t buf_len, time_t *t)
3574 #if !defined(REENTRANT_TIME)
3577 tm = ((t != NULL) ? gmtime(t) : NULL);
3581 struct tm *tm = &_tm;
3586 strftime(buf, buf_len,
"%a, %d %b %Y %H:%M:%S GMT", tm);
3588 mg_strlcpy(buf,
"Thu, 01 Jan 1970 00:00:00 GMT", buf_len);
3589 buf[buf_len - 1] =
'\0';
3596 mg_difftimespec(
const struct timespec *ts_now,
const struct timespec *ts_before)
3598 return (
double)(ts_now->tv_nsec - ts_before->tv_nsec) * 1.0E-9
3599 + (
double)(ts_now->tv_sec - ts_before->tv_sec);
3603 #if defined(MG_EXTERNAL_FUNCTION_mg_cry_internal_impl)
3604 static void mg_cry_internal_impl(
const struct mg_connection *conn,
3609 #include "external_mg_cry_internal_impl.inl"
3614 mg_cry_internal_impl(
const struct mg_connection *conn,
3620 char buf[MG_BUF_LEN], src_addr[IP_ADDR_STR_LEN];
3628 #if defined(GCC_DIAGNOSTIC)
3629 #pragma GCC diagnostic push
3630 #pragma GCC diagnostic ignored "-Wformat-nonliteral"
3633 IGNORE_UNUSED_RESULT(vsnprintf_impl(buf,
sizeof(buf), fmt, ap));
3635 #if defined(GCC_DIAGNOSTIC)
3636 #pragma GCC diagnostic pop
3639 buf[
sizeof(buf) - 1] = 0;
3641 DEBUG_TRACE(
"mg_cry called from %s:%u: %s", func, line, buf);
3651 if ((conn->phys_ctx->callbacks.log_message == NULL)
3652 || (conn->phys_ctx->callbacks.log_message(conn, buf) == 0)) {
3654 if (conn->dom_ctx->config[ERROR_LOG_FILE] != NULL) {
3656 conn->dom_ctx->config[ERROR_LOG_FILE],
3657 MG_FOPEN_MODE_APPEND,
3660 fi.access.fp = NULL;
3663 fi.access.fp = NULL;
3666 if (fi.access.fp != NULL) {
3667 flockfile(fi.access.fp);
3668 timestamp = time(NULL);
3670 sockaddr_to_string(src_addr,
sizeof(src_addr), &conn->client.rsa);
3671 fprintf(fi.access.fp,
3672 "[%010lu] [error] [client %s] ",
3673 (
unsigned long)timestamp,
3676 if (conn->request_info.request_method != NULL) {
3677 fprintf(fi.access.fp,
3679 conn->request_info.request_method,
3680 conn->request_info.request_uri
3681 ? conn->request_info.request_uri
3685 fprintf(fi.access.fp,
"%s", buf);
3686 fputc(
'\n', fi.access.fp);
3687 fflush(fi.access.fp);
3688 funlockfile(fi.access.fp);
3689 (void)mg_fclose(&fi.access);
3699 mg_cry_internal_wrap(
const struct mg_connection *conn,
3707 mg_cry_internal_impl(conn, func, line, fmt, ap);
3713 mg_cry(
const struct mg_connection *conn,
const char *fmt, ...)
3717 mg_cry_internal_impl(conn,
"user", 0, fmt, ap);
3722 #define mg_cry DO_NOT_USE_THIS_FUNCTION__USE_mg_cry_internal
3727 static struct mg_connection *
3728 fc(
struct mg_context *ctx)
3730 static struct mg_connection fake_connection;
3731 fake_connection.phys_ctx = ctx;
3732 fake_connection.dom_ctx = &(ctx->dd);
3733 return &fake_connection;
3740 return CIVETWEB_VERSION;
3744 const struct mg_request_info *
3745 mg_get_request_info(
const struct mg_connection *conn)
3750 #if defined(MG_ALLOW_USING_GET_REQUEST_INFO_FOR_RESPONSE)
3751 if (conn->connection_type == CONNECTION_TYPE_RESPONSE) {
3753 struct mg_workerTLS *tls =
3754 (
struct mg_workerTLS *)pthread_getspecific(sTlsKey);
3756 sprintf(txt,
"%03i", conn->response_info.status_code);
3757 if (strlen(txt) == 3) {
3758 memcpy(tls->txtbuf, txt, 4);
3760 strcpy(tls->txtbuf,
"ERR");
3763 ((
struct mg_connection *)conn)->request_info.local_uri =
3764 ((
struct mg_connection *)conn)->request_info.request_uri =
3767 ((
struct mg_connection *)conn)->request_info.num_headers =
3768 conn->response_info.num_headers;
3769 memcpy(((
struct mg_connection *)conn)->request_info.http_headers,
3770 conn->response_info.http_headers,
3771 sizeof(conn->response_info.http_headers));
3774 if (conn->connection_type != CONNECTION_TYPE_REQUEST) {
3777 return &conn->request_info;
3781 const struct mg_response_info *
3782 mg_get_response_info(
const struct mg_connection *conn)
3787 if (conn->connection_type != CONNECTION_TYPE_RESPONSE) {
3790 return &conn->response_info;
3795 get_proto_name(
const struct mg_connection *conn)
3797 #if defined(__clang__)
3798 #pragma clang diagnostic push
3799 #pragma clang diagnostic ignored "-Wunreachable-code"
3807 const struct mg_request_info *ri = &conn->request_info;
3810 (is_websocket_protocol(conn) ? (ri->is_ssl ?
"wss" :
"ws")
3811 : (ri->is_ssl ?
"https" :
"http"));
3815 #if defined(__clang__)
3816 #pragma clang diagnostic pop
3822 mg_get_request_link(
const struct mg_connection *conn,
char *buf,
size_t buflen)
3824 if ((buflen < 1) || (buf == 0) || (conn == 0)) {
3829 const struct mg_request_info *ri = &conn->request_info;
3831 const char *proto = get_proto_name(conn);
3833 if (ri->local_uri == NULL) {
3837 if ((ri->request_uri != NULL)
3838 && (0 != strcmp(ri->local_uri, ri->request_uri))) {
3859 #if defined(USE_IPV6)
3860 int is_ipv6 = (conn->client.lsa.sa.sa_family == AF_INET6);
3861 int port = is_ipv6 ? htons(conn->client.lsa.sin6.sin6_port)
3862 : htons(conn->client.lsa.sin.sin_port);
3864 int port = htons(conn->client.lsa.sin.sin_port);
3866 int def_port = ri->is_ssl ? 443 : 80;
3867 int auth_domain_check_enabled =
3868 conn->dom_ctx->config[ENABLE_AUTH_DOMAIN_CHECK]
3870 conn->dom_ctx->config[ENABLE_AUTH_DOMAIN_CHECK],
"yes"));
3871 const char *server_domain =
3872 conn->dom_ctx->config[AUTHENTICATION_DOMAIN];
3877 if (port != def_port) {
3878 sprintf(portstr,
":%u", (
unsigned)port);
3883 if (!auth_domain_check_enabled || !server_domain) {
3885 sockaddr_to_string(server_ip,
3889 server_domain = server_ip;
3915 skip_quoted(
char **buf,
3916 const char *delimiters,
3917 const char *whitespace,
3920 char *p, *begin_word, *end_word, *end_whitespace;
3923 end_word = begin_word + strcspn(begin_word, delimiters);
3926 if (end_word > begin_word) {
3928 while (*p == quotechar) {
3935 if (*end_word !=
'\0') {
3936 size_t end_off = strcspn(end_word + 1, delimiters);
3937 memmove(p, end_word, end_off + 1);
3939 end_word += end_off + 1;
3945 for (p++; p < end_word; p++) {
3950 if (*end_word ==
'\0') {
3954 #if defined(GCC_DIAGNOSTIC)
3956 #pragma GCC diagnostic push
3957 #pragma GCC diagnostic ignored "-Wsign-conversion"
3960 end_whitespace = end_word + strspn(&end_word[1], whitespace) + 1;
3962 #if defined(GCC_DIAGNOSTIC)
3963 #pragma GCC diagnostic pop
3966 for (p = end_word; p < end_whitespace; p++) {
3970 *buf = end_whitespace;
3979 get_header(
const struct mg_header *hdr,
int num_hdr,
const char *name)
3982 for (i = 0; i < num_hdr; i++) {
3983 if (!mg_strcasecmp(name, hdr[i].name)) {
3984 return hdr[i].value;
3992 #if defined(USE_WEBSOCKET)
3996 get_req_headers(
const struct mg_request_info *ri,
3998 const char **output,
3999 int output_max_size)
4004 for (i = 0; i < ri->num_headers && cnt < output_max_size; i++) {
4005 if (!mg_strcasecmp(name, ri->http_headers[i].name)) {
4006 output[cnt++] = ri->http_headers[i].value;
4016 mg_get_header(
const struct mg_connection *conn,
const char *name)
4022 if (conn->connection_type == CONNECTION_TYPE_REQUEST) {
4023 return get_header(conn->request_info.http_headers,
4024 conn->request_info.num_headers,
4027 if (conn->connection_type == CONNECTION_TYPE_RESPONSE) {
4028 return get_header(conn->response_info.http_headers,
4029 conn->response_info.num_headers,
4037 get_http_version(
const struct mg_connection *conn)
4043 if (conn->connection_type == CONNECTION_TYPE_REQUEST) {
4044 return conn->request_info.http_version;
4046 if (conn->connection_type == CONNECTION_TYPE_RESPONSE) {
4047 return conn->response_info.http_version;
4060 next_option(
const char *list,
struct vec *val,
struct vec *eq_val)
4065 if (val == NULL || list == NULL || *list ==
'\0') {
4071 while (*list ==
' ' || *list ==
'\t')
4075 if ((list = strchr(val->ptr,
',')) != NULL) {
4077 val->len = ((size_t)(list - val->ptr));
4081 list = val->ptr + strlen(val->ptr);
4082 val->len = ((size_t)(list - val->ptr));
4086 end = (int)val->len - 1;
4087 while (end >= 0 && ((val->ptr[end] ==
' ') || (val->ptr[end] ==
'\t')))
4089 val->len = (size_t)(end + 1);
4091 if (val->len == 0) {
4096 if (eq_val != NULL) {
4100 eq_val->ptr = (
const char *)memchr(val->ptr,
'=', val->len);
4101 if (eq_val->ptr != NULL) {
4103 eq_val->len = ((size_t)(val->ptr - eq_val->ptr)) + val->len;
4104 val->len = ((
size_t)(eq_val->ptr - val->ptr)) - 1;
4117 header_has_option(
const char *header,
const char *option)
4122 DEBUG_ASSERT(option != NULL);
4123 DEBUG_ASSERT(option[0] !=
'\0');
4125 while ((header = next_option(header, &opt_vec, &eq_vec)) != NULL) {
4126 if (mg_strncasecmp(option, opt_vec.ptr, opt_vec.len) == 0)
4136 match_prefix(
const char *pattern,
size_t pattern_len,
const char *str)
4139 ptrdiff_t i, j, len, res;
4141 if ((or_str = (
const char *)memchr(pattern,
'|', pattern_len)) != NULL) {
4142 res = match_prefix(pattern, (
size_t)(or_str - pattern), str);
4143 return (res > 0) ? res
4144 : match_prefix(or_str + 1,
4145 (
size_t)((pattern + pattern_len)
4150 for (i = 0, j = 0; (i < (ptrdiff_t)pattern_len); i++, j++) {
4151 if ((pattern[i] ==
'?') && (str[j] !=
'\0')) {
4153 }
else if (pattern[i] ==
'$') {
4154 return (str[j] ==
'\0') ? j : -1;
4155 }
else if (pattern[i] ==
'*') {
4157 if (pattern[i] ==
'*') {
4159 len = strlen(str + j);
4161 len = strcspn(str + j,
"/");
4163 if (i == (ptrdiff_t)pattern_len) {
4167 res = match_prefix(pattern + i, pattern_len - i, str + j + len);
4168 }
while (res == -1 && len-- > 0);
4169 return (res == -1) ? -1 : j + res + len;
4170 }
else if (lowercase(&pattern[i]) != lowercase(&str[j])) {
4174 return (ptrdiff_t)j;
4182 should_keep_alive(
const struct mg_connection *conn)
4184 const char *http_version;
4188 if ((conn == NULL) || conn->must_close) {
4193 if (mg_strcasecmp(conn->dom_ctx->config[ENABLE_KEEP_ALIVE],
"yes") != 0) {
4199 header = mg_get_header(conn,
"Connection");
4202 if (header_has_option(header,
"keep-alive")) {
4209 http_version = get_http_version(conn);
4210 if (http_version && (0 == strcmp(http_version,
"1.1"))) {
4221 should_decode_url(
const struct mg_connection *conn)
4223 if (!conn || !conn->dom_ctx) {
4227 return (mg_strcasecmp(conn->dom_ctx->config[DECODE_URL],
"yes") == 0);
4232 suggest_connection_header(
const struct mg_connection *conn)
4234 return should_keep_alive(conn) ?
"keep-alive" :
"close";
4239 send_no_cache_header(
struct mg_connection *conn)
4242 return mg_printf(conn,
4243 "Cache-Control: no-cache, no-store, "
4244 "must-revalidate, private, max-age=0\r\n"
4245 "Pragma: no-cache\r\n"
4251 send_static_cache_header(
struct mg_connection *conn)
4253 #if !defined(NO_CACHING)
4256 int max_age = atoi(conn->dom_ctx->config[STATIC_FILE_MAX_AGE]);
4262 return send_no_cache_header(conn);
4274 return mg_printf(conn,
"Cache-Control: max-age=%u\r\n", (
unsigned)max_age);
4276 return send_no_cache_header(conn);
4282 send_additional_header(
struct mg_connection *conn)
4285 const char *header = conn->dom_ctx->config[ADDITIONAL_HEADER];
4287 #if !defined(NO_SSL)
4288 if (conn->dom_ctx->config[STRICT_HTTPS_MAX_AGE]) {
4289 int max_age = atoi(conn->dom_ctx->config[STRICT_HTTPS_MAX_AGE]);
4291 i += mg_printf(conn,
4292 "Strict-Transport-Security: max-age=%u\r\n",
4298 if (header && header[0]) {
4299 i += mg_printf(conn,
"%s\r\n", header);
4306 static void handle_file_based_request(
struct mg_connection *conn,
4308 struct mg_file *filep);
4312 mg_get_response_code_text(
const struct mg_connection *conn,
int response_code)
4318 switch (response_code) {
4323 return "Switching Protocols";
4325 return "Processing";
4335 return "Non-Authoritative Information";
4337 return "No Content";
4339 return "Reset Content";
4341 return "Partial Content";
4343 return "Multi-Status";
4346 return "Already Reported";
4353 return "Multiple Choices";
4355 return "Moved Permanently";
4361 return "Not Modified";
4365 return "Temporary Redirect";
4367 return "Permanent Redirect";
4371 return "Bad Request";
4373 return "Unauthorized";
4375 return "Payment Required";
4381 return "Method Not Allowed";
4383 return "Not Acceptable";
4385 return "Proxy Authentication Required";
4387 return "Request Time-out";
4393 return "Length Required";
4395 return "Precondition Failed";
4397 return "Request Entity Too Large";
4399 return "Request-URI Too Large";
4401 return "Unsupported Media Type";
4403 return "Requested range not satisfiable";
4406 return "Expectation Failed";
4409 return "Misdirected Request";
4411 return "Unproccessable entity";
4416 return "Failed Dependency";
4420 return "Upgrade Required";
4423 return "Precondition Required";
4425 return "Too Many Requests";
4428 return "Request Header Fields Too Large";
4431 return "Unavailable For Legal Reasons";
4436 return "Internal Server Error";
4438 return "Not Implemented";
4440 return "Bad Gateway";
4442 return "Service Unavailable";
4444 return "Gateway Time-out";
4446 return "HTTP Version not supported";
4448 return "Variant Also Negotiates";
4450 return "Insufficient Storage";
4453 return "Loop Detected";
4456 return "Not Extended";
4458 return "Network Authentication Required";
4464 return "I am a teapot";
4466 return "Authentication Timeout";
4468 return "Enhance Your Calm";
4470 return "Login Timeout";
4472 return "Bandwidth Limit Exceeded";
4477 mg_cry_internal(conn,
4478 "Unknown HTTP response code: %u",
4483 if (response_code >= 100 && response_code < 200) {
4485 return "Information";
4487 if (response_code >= 200 && response_code < 300) {
4491 if (response_code >= 300 && response_code < 400) {
4493 return "Redirection";
4495 if (response_code >= 400 && response_code < 500) {
4497 return "Client Error";
4499 if (response_code >= 500 && response_code < 600) {
4501 return "Server Error";
4511 mg_send_http_error_impl(
struct mg_connection *conn,
4516 char errmsg_buf[MG_BUF_LEN];
4517 char path_buf[PATH_MAX];
4519 int len, i, page_handler_found, scope, truncated, has_body;
4521 time_t curtime = time(NULL);
4522 const char *error_handler = NULL;
4523 struct mg_file error_page_file = STRUCT_FILE_INITIALIZER;
4524 const char *error_page_file_ext, *tstr;
4525 int handled_by_callback = 0;
4527 const char *status_text = mg_get_response_code_text(conn, status);
4529 if ((conn == NULL) || (fmt == NULL)) {
4534 conn->status_code = status;
4537 has_body = ((status > 199) && (status != 204) && (status != 304));
4541 || (!conn->in_error_handler
4542 && (conn->phys_ctx->callbacks.http_error != NULL))) {
4545 mg_vsnprintf(conn, NULL, errmsg_buf,
sizeof(errmsg_buf), fmt, ap);
4548 DEBUG_TRACE(
"Error %i - [%s]", status, errmsg_buf);
4554 if (!conn->in_error_handler
4555 && (conn->phys_ctx->callbacks.http_error != NULL)) {
4557 conn->in_error_handler = 1;
4558 handled_by_callback =
4559 (conn->phys_ctx->callbacks.http_error(conn, status, errmsg_buf)
4561 conn->in_error_handler = 0;
4564 if (!handled_by_callback) {
4566 if (conn->in_error_handler) {
4568 "Recursion when handling error %u - fall back to default",
4572 error_handler = conn->dom_ctx->config[ERROR_PAGES];
4573 error_page_file_ext = conn->dom_ctx->config[INDEX_FILES];
4574 page_handler_found = 0;
4576 if (error_handler != NULL) {
4577 for (scope = 1; (scope <= 3) && !page_handler_found; scope++) {
4583 sizeof(path_buf) - 32,
4594 sizeof(path_buf) - 32,
4603 sizeof(path_buf) - 32,
4614 len = (int)strlen(path_buf);
4616 tstr = strchr(error_page_file_ext,
'.');
4620 (i < 32) && (tstr[i] != 0) && (tstr[i] !=
',');
4625 path_buf[len + i - 1] = tstr[i];
4630 path_buf[len + i - 1] = 0;
4632 if (mg_stat(conn, path_buf, &error_page_file.stat)) {
4633 DEBUG_TRACE(
"Check error page %s - found",
4635 page_handler_found = 1;
4638 DEBUG_TRACE(
"Check error page %s - not found",
4641 tstr = strchr(tstr + i,
'.');
4646 if (page_handler_found) {
4647 conn->in_error_handler = 1;
4648 handle_file_based_request(conn, path_buf, &error_page_file);
4649 conn->in_error_handler = 0;
4655 gmt_time_string(date,
sizeof(date), &curtime);
4657 conn->must_close = 1;
4658 mg_printf(conn,
"HTTP/1.1 %d %s\r\n", status, status_text);
4659 send_no_cache_header(conn);
4660 send_additional_header(conn);
4664 "Content-Type: text/plain; charset=utf-8\r\n");
4668 "Connection: close\r\n\r\n",
4674 mg_printf(conn,
"Error %d: %s\n", status, status_text);
4675 mg_write(conn, errmsg_buf, strlen(errmsg_buf));
4679 DEBUG_TRACE(
"Error %i", status);
4687 mg_send_http_error(
struct mg_connection *conn,
int status,
const char *fmt, ...)
4693 ret = mg_send_http_error_impl(conn, status, fmt, ap);
4701 mg_send_http_ok(
struct mg_connection *conn,
4702 const char *mime_type,
4703 long long content_length)
4706 time_t curtime = time(NULL);
4708 if ((mime_type == NULL) || (*mime_type == 0)) {
4713 gmt_time_string(date,
sizeof(date), &curtime);
4716 "HTTP/1.1 200 OK\r\n"
4717 "Content-Type: %s\r\n"
4719 "Connection: %s\r\n",
4722 suggest_connection_header(conn));
4724 send_no_cache_header(conn);
4725 send_additional_header(conn);
4726 if (content_length < 0) {
4727 mg_printf(conn,
"Transfer-Encoding: chunked\r\n\r\n");
4730 "Content-Length: %" UINT64_FMT
"\r\n\r\n",
4731 (uint64_t)content_length);
4739 mg_send_http_redirect(
struct mg_connection *conn,
4740 const char *target_url,
4754 const char *redirect_text;
4756 size_t content_len = 0;
4757 char reply[MG_BUF_LEN];
4760 if (redirect_code == 0) {
4761 redirect_code = 307;
4765 if ((redirect_code != 301) && (redirect_code != 302)
4766 && (redirect_code != 303) && (redirect_code != 307)
4767 && (redirect_code != 308)) {
4773 redirect_text = mg_get_response_code_text(conn, redirect_code);
4776 if ((target_url == NULL) || (*target_url == 0)) {
4780 #if defined(MG_SEND_REDIRECT_BODY)
4808 "<html><head>%s</head><body><a href=\"%s\">%s</a></body></html>",
4812 content_len = strlen(reply);
4819 ret = mg_printf(conn,
4820 "HTTP/1.1 %i %s\r\n"
4822 "Content-Length: %u\r\n"
4823 "Connection: %s\r\n\r\n",
4827 (
unsigned int)content_len,
4828 suggest_connection_header(conn));
4833 if (0 != strcmp(conn->request_info.request_method,
"HEAD")) {
4834 ret = mg_write(conn, reply, content_len);
4838 return (ret > 0) ? ret : -1;
4845 #if defined(GCC_DIAGNOSTIC)
4847 #pragma GCC diagnostic push
4848 #pragma GCC diagnostic ignored "-Wunused-function"
4852 FUNCTION_MAY_BE_UNUSED
4854 pthread_mutex_init(pthread_mutex_t *mutex,
void *unused)
4857 *mutex = CreateMutex(NULL, FALSE, NULL);
4858 return (*mutex == NULL) ? -1 : 0;
4861 FUNCTION_MAY_BE_UNUSED
4863 pthread_mutex_destroy(pthread_mutex_t *mutex)
4865 return (CloseHandle(*mutex) == 0) ? -1 : 0;
4869 FUNCTION_MAY_BE_UNUSED
4871 pthread_mutex_lock(pthread_mutex_t *mutex)
4873 return (WaitForSingleObject(*mutex, (DWORD)INFINITE) == WAIT_OBJECT_0) ? 0
4878 #if defined(ENABLE_UNUSED_PTHREAD_FUNCTIONS)
4879 FUNCTION_MAY_BE_UNUSED
4881 pthread_mutex_trylock(pthread_mutex_t *mutex)
4883 switch (WaitForSingleObject(*mutex, 0)) {
4894 FUNCTION_MAY_BE_UNUSED
4896 pthread_mutex_unlock(pthread_mutex_t *mutex)
4898 return (ReleaseMutex(*mutex) == 0) ? -1 : 0;
4902 FUNCTION_MAY_BE_UNUSED
4904 pthread_cond_init(pthread_cond_t *cv,
const void *unused)
4907 InitializeCriticalSection(&cv->threadIdSec);
4908 cv->waiting_thread = NULL;
4913 FUNCTION_MAY_BE_UNUSED
4915 pthread_cond_timedwait(pthread_cond_t *cv,
4916 pthread_mutex_t *mutex,
4917 FUNCTION_MAY_BE_UNUSED
const struct timespec *abstime)
4919 struct mg_workerTLS **ptls,
4920 *tls = (
struct mg_workerTLS *)pthread_getspecific(sTlsKey);
4922 int64_t nsnow, nswaitabs, nswaitrel;
4925 EnterCriticalSection(&cv->threadIdSec);
4927 ptls = &cv->waiting_thread;
4928 for (; *ptls != NULL; ptls = &(*ptls)->next_waiting_thread)
4930 tls->next_waiting_thread = NULL;
4932 LeaveCriticalSection(&cv->threadIdSec);
4935 nsnow = mg_get_current_time_ns();
4937 (((int64_t)abstime->tv_sec) * 1000000000) + abstime->tv_nsec;
4938 nswaitrel = nswaitabs - nsnow;
4939 if (nswaitrel < 0) {
4942 mswaitrel = (DWORD)(nswaitrel / 1000000);
4944 mswaitrel = (DWORD)INFINITE;
4947 pthread_mutex_unlock(mutex);
4949 == WaitForSingleObject(tls->pthread_cond_helper_mutex, mswaitrel));
4952 EnterCriticalSection(&cv->threadIdSec);
4953 ptls = &cv->waiting_thread;
4954 for (; *ptls != NULL; ptls = &(*ptls)->next_waiting_thread) {
4956 *ptls = tls->next_waiting_thread;
4961 LeaveCriticalSection(&cv->threadIdSec);
4963 WaitForSingleObject(tls->pthread_cond_helper_mutex,
4968 pthread_mutex_lock(mutex);
4974 FUNCTION_MAY_BE_UNUSED
4976 pthread_cond_wait(pthread_cond_t *cv, pthread_mutex_t *mutex)
4978 return pthread_cond_timedwait(cv, mutex, NULL);
4982 FUNCTION_MAY_BE_UNUSED
4984 pthread_cond_signal(pthread_cond_t *cv)
4989 EnterCriticalSection(&cv->threadIdSec);
4990 if (cv->waiting_thread) {
4991 wkup = cv->waiting_thread->pthread_cond_helper_mutex;
4992 cv->waiting_thread = cv->waiting_thread->next_waiting_thread;
4994 ok = SetEvent(wkup);
4997 LeaveCriticalSection(&cv->threadIdSec);
5003 FUNCTION_MAY_BE_UNUSED
5005 pthread_cond_broadcast(pthread_cond_t *cv)
5007 EnterCriticalSection(&cv->threadIdSec);
5008 while (cv->waiting_thread) {
5009 pthread_cond_signal(cv);
5011 LeaveCriticalSection(&cv->threadIdSec);
5017 FUNCTION_MAY_BE_UNUSED
5019 pthread_cond_destroy(pthread_cond_t *cv)
5021 EnterCriticalSection(&cv->threadIdSec);
5022 DEBUG_ASSERT(cv->waiting_thread == NULL);
5023 LeaveCriticalSection(&cv->threadIdSec);
5024 DeleteCriticalSection(&cv->threadIdSec);
5030 #if defined(ALTERNATIVE_QUEUE)
5031 FUNCTION_MAY_BE_UNUSED
5035 return (
void *)CreateEvent(NULL, FALSE, FALSE, NULL);
5039 FUNCTION_MAY_BE_UNUSED
5041 event_wait(
void *eventhdl)
5043 int res = WaitForSingleObject((HANDLE)eventhdl, (DWORD)INFINITE);
5044 return (res == WAIT_OBJECT_0);
5048 FUNCTION_MAY_BE_UNUSED
5050 event_signal(
void *eventhdl)
5052 return (
int)SetEvent((HANDLE)eventhdl);
5056 FUNCTION_MAY_BE_UNUSED
5058 event_destroy(
void *eventhdl)
5060 CloseHandle((HANDLE)eventhdl);
5065 #if defined(GCC_DIAGNOSTIC)
5067 #pragma GCC diagnostic pop
5073 change_slashes_to_backslashes(
char *path)
5077 for (i = 0; path[i] !=
'\0'; i++) {
5078 if (path[i] ==
'/') {
5084 if ((path[i] ==
'\\') && (i > 0)) {
5085 while ((path[i + 1] ==
'\\') || (path[i + 1] ==
'/')) {
5086 (void)memmove(path + i + 1, path + i + 2, strlen(path + i + 1));
5094 mg_wcscasecmp(
const wchar_t *s1,
const wchar_t *s2)
5099 diff = tolower(*s1) - tolower(*s2);
5102 }
while ((diff == 0) && (s1[-1] !=
'\0'));
5111 path_to_unicode(
const struct mg_connection *conn,
5116 char buf[PATH_MAX], buf2[PATH_MAX];
5117 wchar_t wbuf2[W_PATH_MAX + 1];
5118 DWORD long_len, err;
5119 int (*fcompare)(
const wchar_t *,
const wchar_t *) = mg_wcscasecmp;
5121 mg_strlcpy(buf, path,
sizeof(buf));
5122 change_slashes_to_backslashes(buf);
5126 memset(wbuf, 0, wbuf_len *
sizeof(
wchar_t));
5127 MultiByteToWideChar(CP_UTF8, 0, buf, -1, wbuf, (
int)wbuf_len);
5128 WideCharToMultiByte(
5129 CP_UTF8, 0, wbuf, (
int)wbuf_len, buf2,
sizeof(buf2), NULL, NULL);
5130 if (strcmp(buf, buf2) != 0) {
5144 if (conn->dom_ctx->config[CASE_SENSITIVE_FILES]
5145 && !mg_strcasecmp(conn->dom_ctx->config[CASE_SENSITIVE_FILES],
5153 #if !defined(_WIN32_WCE)
5155 memset(wbuf2, 0, ARRAY_SIZE(wbuf2) *
sizeof(
wchar_t));
5156 long_len = GetLongPathNameW(wbuf, wbuf2, ARRAY_SIZE(wbuf2) - 1);
5157 if (long_len == 0) {
5158 err = GetLastError();
5159 if (err == ERROR_FILE_NOT_FOUND) {
5164 if ((long_len >= ARRAY_SIZE(wbuf2)) || (fcompare(wbuf, wbuf2) != 0)) {
5173 if (strchr(path,
'~')) {
5185 path_cannot_disclose_cgi(
const char *path)
5187 static const char *allowed_last_characters =
"_-";
5188 int last = path[strlen(path) - 1];
5189 return isalnum(last) || strchr(allowed_last_characters, last) != NULL;
5194 mg_stat(
const struct mg_connection *conn,
5196 struct mg_file_stat *filep)
5198 wchar_t wbuf[W_PATH_MAX];
5199 WIN32_FILE_ATTRIBUTE_DATA info;
5200 time_t creation_time;
5205 memset(filep, 0,
sizeof(*filep));
5207 if (conn && is_file_in_memory(conn, path)) {
5213 struct mg_file tmp_file = STRUCT_FILE_INITIALIZER;
5214 open_file_in_memory(conn, path, &tmp_file, MG_FOPEN_MODE_NONE);
5215 filep->size = tmp_file.stat.size;
5216 filep->location = 2;
5224 filep->last_modified = time(NULL);
5236 path_to_unicode(conn, path, wbuf, ARRAY_SIZE(wbuf));
5237 if (GetFileAttributesExW(wbuf, GetFileExInfoStandard, &info) != 0) {
5238 filep->size = MAKEUQUAD(info.nFileSizeLow, info.nFileSizeHigh);
5239 filep->last_modified =
5240 SYS2UNIX_TIME(info.ftLastWriteTime.dwLowDateTime,
5241 info.ftLastWriteTime.dwHighDateTime);
5247 creation_time = SYS2UNIX_TIME(info.ftCreationTime.dwLowDateTime,
5248 info.ftCreationTime.dwHighDateTime);
5249 if (creation_time > filep->last_modified) {
5250 filep->last_modified = creation_time;
5253 filep->is_directory = info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY;
5258 if (!filep->is_directory && !path_cannot_disclose_cgi(path)) {
5259 memset(filep, 0,
sizeof(*filep));
5271 mg_remove(
const struct mg_connection *conn,
const char *path)
5273 wchar_t wbuf[W_PATH_MAX];
5274 path_to_unicode(conn, path, wbuf, ARRAY_SIZE(wbuf));
5275 return DeleteFileW(wbuf) ? 0 : -1;
5280 mg_mkdir(
const struct mg_connection *conn,
const char *path,
int mode)
5282 wchar_t wbuf[W_PATH_MAX];
5284 path_to_unicode(conn, path, wbuf, ARRAY_SIZE(wbuf));
5285 return CreateDirectoryW(wbuf, NULL) ? 0 : -1;
5291 #if defined(GCC_DIAGNOSTIC)
5293 #pragma GCC diagnostic push
5294 #pragma GCC diagnostic ignored "-Wunused-function"
5299 FUNCTION_MAY_BE_UNUSED
5301 mg_opendir(
const struct mg_connection *conn,
const char *name)
5304 wchar_t wpath[W_PATH_MAX];
5308 SetLastError(ERROR_BAD_ARGUMENTS);
5309 }
else if ((dir = (DIR *)mg_malloc(
sizeof(*dir))) == NULL) {
5310 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
5312 path_to_unicode(conn, name, wpath, ARRAY_SIZE(wpath));
5313 attrs = GetFileAttributesW(wpath);
5314 if ((wcslen(wpath) + 2 < ARRAY_SIZE(wpath)) && (attrs != 0xFFFFFFFF)
5315 && ((attrs & FILE_ATTRIBUTE_DIRECTORY) != 0)) {
5316 (void)wcscat(wpath, L
"\\*");
5317 dir->handle = FindFirstFileW(wpath, &dir->info);
5318 dir->result.d_name[0] =
'\0';
5329 FUNCTION_MAY_BE_UNUSED
5331 mg_closedir(DIR *dir)
5336 if (dir->handle != INVALID_HANDLE_VALUE)
5337 result = FindClose(dir->handle) ? 0 : -1;
5342 SetLastError(ERROR_BAD_ARGUMENTS);
5349 FUNCTION_MAY_BE_UNUSED
5350 static struct dirent *
5351 mg_readdir(DIR *dir)
5353 struct dirent *result = 0;
5356 if (dir->handle != INVALID_HANDLE_VALUE) {
5357 result = &dir->result;
5358 (void)WideCharToMultiByte(CP_UTF8,
5360 dir->info.cFileName,
5363 sizeof(result->d_name),
5367 if (!FindNextFileW(dir->handle, &dir->info)) {
5368 (void)FindClose(dir->handle);
5369 dir->handle = INVALID_HANDLE_VALUE;
5373 SetLastError(ERROR_FILE_NOT_FOUND);
5376 SetLastError(ERROR_BAD_ARGUMENTS);
5383 #if !defined(HAVE_POLL)
5388 FUNCTION_MAY_BE_UNUSED
5390 poll(
struct pollfd *pfd,
unsigned int n,
int milliseconds)
5399 memset(&tv, 0,
sizeof(tv));
5400 tv.tv_sec = milliseconds / 1000;
5401 tv.tv_usec = (milliseconds % 1000) * 1000;
5405 for (i = 0; i < n; i++) {
5406 if (pfd[i].events & POLLIN) {
5407 FD_SET((SOCKET)pfd[i].fd, &rset);
5408 }
else if (pfd[i].events & POLLOUT) {
5409 FD_SET((SOCKET)pfd[i].fd, &wset);
5413 if (pfd[i].fd > maxfd) {
5418 if ((result = select((
int)maxfd + 1, &rset, &wset, NULL, &tv)) > 0) {
5419 for (i = 0; i < n; i++) {
5420 if (FD_ISSET(pfd[i].fd, &rset)) {
5421 pfd[i].revents |= POLLIN;
5423 if (FD_ISSET(pfd[i].fd, &wset)) {
5424 pfd[i].revents |= POLLOUT;
5441 #if defined(GCC_DIAGNOSTIC)
5443 #pragma GCC diagnostic pop
5448 set_close_on_exec(SOCKET sock,
struct mg_connection *conn )
5451 #if defined(_WIN32_WCE)
5454 (void)SetHandleInformation((HANDLE)(intptr_t)sock, HANDLE_FLAG_INHERIT, 0);
5460 mg_start_thread(mg_thread_func_t f,
void *p)
5462 #if defined(USE_STACK_SIZE) && (USE_STACK_SIZE > 1)
5466 return ((_beginthread((
void(__cdecl *)(
void *))f, USE_STACK_SIZE, p)
5467 == ((uintptr_t)(-1L)))
5472 (_beginthread((
void(__cdecl *)(
void *))f, 0, p) == ((uintptr_t)(-1L)))
5481 mg_start_thread_with_id(
unsigned(__stdcall *f)(
void *),
5483 pthread_t *threadidptr)
5486 HANDLE threadhandle;
5489 uip = _beginthreadex(NULL, 0, (
unsigned(__stdcall *)(
void *))f, p, 0, NULL);
5490 threadhandle = (HANDLE)uip;
5491 if ((uip != (uintptr_t)(-1L)) && (threadidptr != NULL)) {
5492 *threadidptr = threadhandle;
5502 mg_join_thread(pthread_t threadid)
5508 dwevent = WaitForSingleObject(threadid, (DWORD)INFINITE);
5509 if (dwevent == WAIT_FAILED) {
5510 DEBUG_TRACE(
"WaitForSingleObject() failed, error %d", ERRNO);
5512 if (dwevent == WAIT_OBJECT_0) {
5513 CloseHandle(threadid);
5521 #if !defined(NO_SSL_DL) && !defined(NO_SSL)
5525 #if defined(GCC_DIAGNOSTIC)
5527 #pragma GCC diagnostic push
5528 #pragma GCC diagnostic ignored "-Wunused-function"
5532 FUNCTION_MAY_BE_UNUSED
5534 dlopen(
const char *dll_name,
int flags)
5536 wchar_t wbuf[W_PATH_MAX];
5538 path_to_unicode(NULL, dll_name, wbuf, ARRAY_SIZE(wbuf));
5539 return LoadLibraryW(wbuf);
5543 FUNCTION_MAY_BE_UNUSED
5545 dlclose(
void *handle)
5549 if (FreeLibrary((HMODULE)handle) != 0) {
5559 #if defined(GCC_DIAGNOSTIC)
5561 #pragma GCC diagnostic pop
5567 #if !defined(NO_CGI)
5572 kill(pid_t pid,
int sig_num)
5574 (void)TerminateProcess((HANDLE)pid, (UINT)sig_num);
5575 (void)CloseHandle((HANDLE)pid);
5580 #if !defined(WNOHANG)
5586 waitpid(pid_t pid,
int *status,
int flags)
5588 DWORD timeout = INFINITE;
5593 if ((flags | WNOHANG) == WNOHANG) {
5597 waitres = WaitForSingleObject((HANDLE)pid, timeout);
5598 if (waitres == WAIT_OBJECT_0) {
5601 if (waitres == WAIT_TIMEOUT) {
5609 trim_trailing_whitespaces(
char *s)
5611 char *e = s + strlen(s) - 1;
5612 while ((e > s) && isspace(*(
unsigned char *)e)) {
5619 spawn_process(
struct mg_connection *conn,
5629 char *p, *interp, full_interp[PATH_MAX], full_dir[PATH_MAX],
5630 cmdline[PATH_MAX], buf[PATH_MAX];
5632 struct mg_file file = STRUCT_FILE_INITIALIZER;
5634 PROCESS_INFORMATION pi = {0};
5638 memset(&si, 0,
sizeof(si));
5641 si.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
5642 si.wShowWindow = SW_HIDE;
5644 me = GetCurrentProcess();
5646 (HANDLE)_get_osfhandle(fdin[0]),
5651 DUPLICATE_SAME_ACCESS);
5653 (HANDLE)_get_osfhandle(fdout[1]),
5658 DUPLICATE_SAME_ACCESS);
5660 (HANDLE)_get_osfhandle(fderr[1]),
5665 DUPLICATE_SAME_ACCESS);
5670 SetHandleInformation((HANDLE)_get_osfhandle(fdin[1]),
5671 HANDLE_FLAG_INHERIT,
5673 SetHandleInformation((HANDLE)_get_osfhandle(fdout[0]),
5674 HANDLE_FLAG_INHERIT,
5676 SetHandleInformation((HANDLE)_get_osfhandle(fderr[0]),
5677 HANDLE_FLAG_INHERIT,
5681 interp = conn->dom_ctx->config[CGI_INTERPRETER];
5682 if (interp == NULL) {
5683 buf[0] = buf[1] =
'\0';
5687 conn, &truncated, cmdline,
sizeof(cmdline),
"%s/%s", dir, prog);
5690 pi.hProcess = (pid_t)-1;
5694 if (mg_fopen(conn, cmdline, MG_FOPEN_MODE_READ, &file)) {
5695 #if defined(MG_USE_OPEN_FILE)
5696 p = (
char *)file.access.membuf;
5700 mg_fgets(buf,
sizeof(buf), &file, &p);
5701 (void)mg_fclose(&file.access);
5702 buf[
sizeof(buf) - 1] =
'\0';
5705 if ((buf[0] ==
'#') && (buf[1] ==
'!')) {
5706 trim_trailing_whitespaces(buf + 2);
5713 if (interp[0] !=
'\0') {
5714 GetFullPathNameA(interp,
sizeof(full_interp), full_interp, NULL);
5715 interp = full_interp;
5717 GetFullPathNameA(dir,
sizeof(full_dir), full_dir, NULL);
5719 if (interp[0] !=
'\0') {
5724 "\"%s\" \"%s\\%s\"",
5739 pi.hProcess = (pid_t)-1;
5743 DEBUG_TRACE(
"Running [%s]", cmdline);
5744 if (CreateProcessA(NULL,
5749 CREATE_NEW_PROCESS_GROUP,
5756 conn,
"%s: CreateProcess(%s): %ld", __func__, cmdline, (
long)ERRNO);
5757 pi.hProcess = (pid_t)-1;
5762 (void)CloseHandle(si.hStdOutput);
5763 (void)CloseHandle(si.hStdError);
5764 (void)CloseHandle(si.hStdInput);
5765 if (pi.hThread != NULL) {
5766 (void)CloseHandle(pi.hThread);
5769 return (pid_t)pi.hProcess;
5775 set_blocking_mode(SOCKET sock)
5777 unsigned long non_blocking = 0;
5778 return ioctlsocket(sock, (
long)FIONBIO, &non_blocking);
5782 set_non_blocking_mode(SOCKET sock)
5784 unsigned long non_blocking = 1;
5785 return ioctlsocket(sock, (
long)FIONBIO, &non_blocking);
5791 mg_stat(
const struct mg_connection *conn,
5793 struct mg_file_stat *filep)
5799 memset(filep, 0,
sizeof(*filep));
5801 if (conn && is_file_in_memory(conn, path)) {
5805 struct mg_file tmp_file = STRUCT_FILE_INITIALIZER;
5806 open_file_in_memory(conn, path, &tmp_file, MG_FOPEN_MODE_NONE);
5807 filep->size = tmp_file.stat.size;
5808 filep->last_modified = time(NULL);
5809 filep->location = 2;
5815 if (0 == stat(path, &st)) {
5816 filep->size = (uint64_t)(st.st_size);
5817 filep->last_modified = st.st_mtime;
5818 filep->is_directory = S_ISDIR(st.st_mode);
5827 set_close_on_exec(SOCKET fd,
struct mg_connection *conn )
5829 if (fcntl(fd, F_SETFD, FD_CLOEXEC) != 0) {
5831 mg_cry_internal(conn,
5832 "%s: fcntl(F_SETFD FD_CLOEXEC) failed: %s",
5841 mg_start_thread(mg_thread_func_t func,
void *param)
5843 pthread_t thread_id;
5844 pthread_attr_t attr;
5847 (void)pthread_attr_init(&attr);
5848 (void)pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
5850 #if defined(USE_STACK_SIZE) && (USE_STACK_SIZE > 1)
5853 (void)pthread_attr_setstacksize(&attr, USE_STACK_SIZE);
5856 result = pthread_create(&thread_id, &attr, func, param);
5857 pthread_attr_destroy(&attr);
5865 mg_start_thread_with_id(mg_thread_func_t func,
5867 pthread_t *threadidptr)
5869 pthread_t thread_id;
5870 pthread_attr_t attr;
5873 (void)pthread_attr_init(&attr);
5875 #if defined(USE_STACK_SIZE) && (USE_STACK_SIZE > 1)
5878 (void)pthread_attr_setstacksize(&attr, USE_STACK_SIZE);
5881 result = pthread_create(&thread_id, &attr, func, param);
5882 pthread_attr_destroy(&attr);
5883 if ((result == 0) && (threadidptr != NULL)) {
5884 *threadidptr = thread_id;
5892 mg_join_thread(pthread_t threadid)
5896 result = pthread_join(threadid, NULL);
5901 #if !defined(NO_CGI)
5903 spawn_process(
struct mg_connection *conn,
5921 if ((pid = fork()) == -1) {
5923 mg_send_http_error(conn,
5925 "Error: Creating CGI process\nfork(): %s",
5927 }
else if (pid == 0) {
5929 if (chdir(dir) != 0) {
5931 conn,
"%s: chdir(%s): %s", __func__, dir, strerror(ERRNO));
5932 }
else if (dup2(fdin[0], 0) == -1) {
5933 mg_cry_internal(conn,
5934 "%s: dup2(%d, 0): %s",
5938 }
else if (dup2(fdout[1], 1) == -1) {
5939 mg_cry_internal(conn,
5940 "%s: dup2(%d, 1): %s",
5944 }
else if (dup2(fderr[1], 2) == -1) {
5945 mg_cry_internal(conn,
5946 "%s: dup2(%d, 2): %s",
5954 (void)close(fdin[0]);
5955 (void)close(fdout[1]);
5956 (void)close(fderr[1]);
5959 (void)close(fdin[1]);
5960 (void)close(fdout[0]);
5961 (void)close(fderr[0]);
5968 signal(SIGCHLD, SIG_DFL);
5970 interp = conn->dom_ctx->config[CGI_INTERPRETER];
5971 if (interp == NULL) {
5972 (void)execle(prog, prog, NULL, envp);
5973 mg_cry_internal(conn,
5974 "%s: execle(%s): %s",
5979 (void)execle(interp, interp, prog, NULL, envp);
5980 mg_cry_internal(conn,
5981 "%s: execle(%s %s): %s",
5997 set_non_blocking_mode(SOCKET sock)
5999 int flags = fcntl(sock, F_GETFL, 0);
6004 if (fcntl(sock, F_SETFL, (flags | O_NONBLOCK)) < 0) {
6011 set_blocking_mode(SOCKET sock)
6013 int flags = fcntl(sock, F_GETFL, 0);
6018 if (fcntl(sock, F_SETFL, flags & (~(
int)(O_NONBLOCK))) < 0) {
6032 static uint64_t lfsr = 0;
6033 static uint64_t lcg = 0;
6034 uint64_t now = mg_get_current_time_ns();
6039 lfsr = mg_get_current_time_ns();
6040 lcg = mg_get_current_time_ns();
6044 | ((((lfsr >> 0) ^ (lfsr >> 1) ^ (lfsr >> 3) ^ (lfsr >> 4)) & 1)
6046 lcg = lcg * 6364136223846793005LL + 1442695040888963407LL;
6054 return (lfsr ^ lcg ^ now);
6059 mg_poll(
struct pollfd *pfd,
6062 volatile int *stop_server)
6067 int ms_now = SOCKET_TIMEOUT_QUANTUM;
6077 if ((milliseconds >= 0) && (milliseconds < ms_now)) {
6078 ms_now = milliseconds;
6081 result = poll(pfd, n, ms_now);
6089 if (milliseconds > 0) {
6090 milliseconds -= ms_now;
6093 }
while (milliseconds != 0);
6108 push_inner(
struct mg_context *ctx,
6116 uint64_t start = 0, now = 0, timeout_ns = 0;
6118 unsigned ms_wait = SOCKET_TIMEOUT_QUANTUM;
6123 typedef size_t len_t;
6127 now = mg_get_current_time_ns();
6129 timeout_ns = (uint64_t)(timeout * 1.0E9);
6146 #if !defined(NO_SSL)
6148 n = SSL_write(ssl, buf, len);
6150 err = SSL_get_error(ssl, n);
6151 if ((err == SSL_ERROR_SYSCALL) && (n == -1)) {
6153 }
else if ((err == SSL_ERROR_WANT_READ)
6154 || (err == SSL_ERROR_WANT_WRITE)) {
6157 DEBUG_TRACE(
"SSL_write() failed, error %d", err);
6166 n = (int)fwrite(buf, 1, (
size_t)len, fp);
6174 n = (int)send(sock, buf, (len_t)len, MSG_NOSIGNAL);
6175 err = (n < 0) ? ERRNO : 0;
6177 if (err == WSAEWOULDBLOCK) {
6182 if (err == EWOULDBLOCK) {
6193 if (ctx->stop_flag) {
6197 if ((n > 0) || ((n == 0) && (len == 0))) {
6203 DEBUG_TRACE(
"send() failed, error %d", err);
6223 struct pollfd pfd[1];
6227 pfd[0].events = POLLOUT;
6228 pollres = mg_poll(pfd, 1, (
int)(ms_wait), &(ctx->stop_flag));
6229 if (ctx->stop_flag) {
6238 now = mg_get_current_time_ns();
6239 if ((now - start) > timeout_ns) {
6254 push_all(
struct mg_context *ctx,
6261 double timeout = -1.0;
6262 int64_t n, nwritten = 0;
6268 if (ctx->dd.config[REQUEST_TIMEOUT]) {
6269 timeout = atoi(ctx->dd.config[REQUEST_TIMEOUT]) / 1000.0;
6272 while ((len > 0) && (ctx->stop_flag == 0)) {
6273 n = push_inner(ctx, fp, sock, ssl, buf + nwritten, (
int)len, timeout);
6275 if (nwritten == 0) {
6279 }
else if (n == 0) {
6298 pull_inner(FILE *fp,
6299 struct mg_connection *conn,
6309 typedef size_t len_t;
6311 #if !defined(NO_SSL)
6321 #if !defined(_WIN32_WCE)
6326 nread = (int)read(fileno(fp), buf, (size_t)len);
6329 nread = (int)fread(buf, 1, (
size_t)len, fp);
6331 err = (nread < 0) ? ERRNO : 0;
6332 if ((nread == 0) && (len > 0)) {
6337 #if !defined(NO_SSL)
6338 }
else if ((conn->ssl != NULL)
6339 && ((ssl_pending = SSL_pending(conn->ssl)) > 0)) {
6343 if (ssl_pending > len) {
6346 nread = SSL_read(conn->ssl, buf, ssl_pending);
6348 err = SSL_get_error(conn->ssl, nread);
6349 if ((err == SSL_ERROR_SYSCALL) && (nread == -1)) {
6351 }
else if ((err == SSL_ERROR_WANT_READ)
6352 || (err == SSL_ERROR_WANT_WRITE)) {
6355 DEBUG_TRACE(
"SSL_read() failed, error %d", err);
6362 }
else if (conn->ssl != NULL) {
6364 struct pollfd pfd[1];
6367 pfd[0].fd = conn->client.sock;
6368 pfd[0].events = POLLIN;
6369 pollres = mg_poll(pfd,
6371 (
int)(timeout * 1000.0),
6372 &(conn->phys_ctx->stop_flag));
6373 if (conn->phys_ctx->stop_flag) {
6377 nread = SSL_read(conn->ssl, buf, len);
6379 err = SSL_get_error(conn->ssl, nread);
6380 if ((err == SSL_ERROR_SYSCALL) && (nread == -1)) {
6382 }
else if ((err == SSL_ERROR_WANT_READ)
6383 || (err == SSL_ERROR_WANT_WRITE)) {
6386 DEBUG_TRACE(
"SSL_read() failed, error %d", err);
6393 }
else if (pollres < 0) {
6403 struct pollfd pfd[1];
6406 pfd[0].fd = conn->client.sock;
6407 pfd[0].events = POLLIN;
6408 pollres = mg_poll(pfd,
6410 (
int)(timeout * 1000.0),
6411 &(conn->phys_ctx->stop_flag));
6412 if (conn->phys_ctx->stop_flag) {
6416 nread = (int)recv(conn->client.sock, buf, (len_t)len, 0);
6417 err = (nread < 0) ? ERRNO : 0;
6422 }
else if (pollres < 0) {
6431 if (conn->phys_ctx->stop_flag) {
6435 if ((nread > 0) || ((nread == 0) && (len == 0))) {
6443 if (err == WSAEWOULDBLOCK) {
6447 }
else if (err == WSAETIMEDOUT) {
6451 }
else if (err == WSAECONNABORTED) {
6455 DEBUG_TRACE(
"recv() failed, error %d", err);
6464 if ((err == EAGAIN) || (err == EWOULDBLOCK) || (err == EINTR)) {
6477 DEBUG_TRACE(
"recv() failed, error %d", err);
6489 pull_all(FILE *fp,
struct mg_connection *conn,
char *buf,
int len)
6492 double timeout = -1.0;
6493 uint64_t start_time = 0, now = 0, timeout_ns = 0;
6495 if (conn->dom_ctx->config[REQUEST_TIMEOUT]) {
6496 timeout = atoi(conn->dom_ctx->config[REQUEST_TIMEOUT]) / 1000.0;
6498 if (timeout >= 0.0) {
6499 start_time = mg_get_current_time_ns();
6500 timeout_ns = (uint64_t)(timeout * 1.0E9);
6503 while ((len > 0) && (conn->phys_ctx->stop_flag == 0)) {
6504 n = pull_inner(fp, conn, buf + nread, len, timeout);
6510 }
else if (n == -1) {
6512 if (timeout >= 0.0) {
6513 now = mg_get_current_time_ns();
6514 if ((now - start_time) <= timeout_ns) {
6519 }
else if (n == 0) {
6522 conn->consumed_content += n;
6533 discard_unread_request_data(
struct mg_connection *conn)
6535 char buf[MG_BUF_LEN];
6543 to_read =
sizeof(buf);
6545 if (conn->is_chunked) {
6548 while (conn->is_chunked != 3) {
6549 nread = mg_read(conn, buf, to_read);
6557 while (conn->consumed_content < conn->content_len) {
6559 > (
size_t)(conn->content_len - conn->consumed_content)) {
6560 to_read = (size_t)(conn->content_len - conn->consumed_content);
6563 nread = mg_read(conn, buf, to_read);
6573 mg_read_inner(
struct mg_connection *conn,
void *buf,
size_t len)
6575 int64_t n, buffered_len, nread;
6577 (int64_t)((len > INT_MAX) ? INT_MAX : len);
6589 if (conn->consumed_content == 0) {
6590 if (conn->is_chunked == 1) {
6591 conn->content_len = len64;
6592 conn->is_chunked = 2;
6593 }
else if (conn->content_len == -1) {
6596 conn->content_len = INT64_MAX;
6597 conn->must_close = 1;
6602 if (conn->consumed_content < conn->content_len) {
6604 int64_t left_to_read = conn->content_len - conn->consumed_content;
6605 if (left_to_read < len64) {
6609 len64 = left_to_read;
6613 buffered_len = (int64_t)(conn->data_len) - (int64_t)conn->request_len
6614 - conn->consumed_content;
6615 if (buffered_len > 0) {
6616 if (len64 < buffered_len) {
6617 buffered_len = len64;
6619 body = conn->buf + conn->request_len + conn->consumed_content;
6620 memcpy(buf, body, (
size_t)buffered_len);
6621 len64 -= buffered_len;
6622 conn->consumed_content += buffered_len;
6623 nread += buffered_len;
6624 buf = (
char *)buf + buffered_len;
6630 if ((n = pull_all(NULL, conn, (
char *)buf, (
int)len64)) >= 0) {
6633 nread = ((nread > 0) ? nread : n);
6641 mg_getc(
struct mg_connection *conn)
6647 if (mg_read_inner(conn, &c, 1) <= 0) {
6655 mg_read(
struct mg_connection *conn,
void *buf,
size_t len)
6657 if (len > INT_MAX) {
6665 if (conn->is_chunked) {
6666 size_t all_read = 0;
6669 if (conn->is_chunked == 3) {
6674 if (conn->chunk_remainder) {
6678 ((conn->chunk_remainder > len) ? (len)
6679 : (conn->chunk_remainder));
6681 conn->content_len += (int)read_now;
6683 mg_read_inner(conn, (
char *)buf + all_read, read_now);
6690 all_read += (size_t)read_ret;
6691 conn->chunk_remainder -= (size_t)read_ret;
6692 len -= (size_t)read_ret;
6694 if (conn->chunk_remainder == 0) {
6698 conn->content_len += 2;
6701 if ((x1 !=
'\r') || (x2 !=
'\n')) {
6712 unsigned long chunkSize = 0;
6714 for (i = 0; i < ((int)
sizeof(lenbuf) - 1); i++) {
6715 conn->content_len++;
6716 lenbuf[i] = mg_getc(conn);
6717 if ((i > 0) && (lenbuf[i] ==
'\r')
6718 && (lenbuf[i - 1] !=
'\r')) {
6721 if ((i > 1) && (lenbuf[i] ==
'\n')
6722 && (lenbuf[i - 1] ==
'\r')) {
6724 chunkSize = strtoul(lenbuf, &end, 16);
6725 if (chunkSize == 0) {
6727 conn->is_chunked = 3;
6731 if (!isxdigit(lenbuf[i])) {
6736 if ((end == NULL) || (*end !=
'\r')) {
6740 if (chunkSize == 0) {
6744 conn->chunk_remainder = chunkSize;
6748 return (
int)all_read;
6750 return mg_read_inner(conn, buf, len);
6755 mg_write(
struct mg_connection *conn,
const void *buf,
size_t len)
6758 int64_t n, total, allowed;
6764 if (conn->throttle > 0) {
6765 if ((now = time(NULL)) != conn->last_throttle_time) {
6766 conn->last_throttle_time = now;
6767 conn->last_throttle_bytes = 0;
6769 allowed = conn->throttle - conn->last_throttle_bytes;
6770 if (allowed > (int64_t)len) {
6771 allowed = (int64_t)len;
6773 if ((total = push_all(conn->phys_ctx,
6780 buf = (
const char *)buf + total;
6781 conn->last_throttle_bytes += total;
6782 while ((total < (int64_t)len) && (conn->phys_ctx->stop_flag == 0)) {
6783 allowed = (conn->throttle > ((int64_t)len - total))
6784 ? (int64_t)len - total
6786 if ((n = push_all(conn->phys_ctx,
6796 conn->last_throttle_bytes = allowed;
6797 conn->last_throttle_time = time(NULL);
6798 buf = (
const char *)buf + n;
6803 total = push_all(conn->phys_ctx,
6811 conn->num_bytes_sent += total;
6819 mg_send_chunk(
struct mg_connection *conn,
6821 unsigned int chunk_len)
6829 sprintf(lenbuf,
"%x\r\n", chunk_len);
6830 lenbuf_len = strlen(lenbuf);
6833 ret = mg_write(conn, lenbuf, lenbuf_len);
6834 if (ret != (
int)lenbuf_len) {
6839 ret = mg_write(conn, chunk, chunk_len);
6840 if (ret != (
int)chunk_len) {
6845 ret = mg_write(conn,
"\r\n", 2);
6855 #if defined(GCC_DIAGNOSTIC)
6858 #pragma GCC diagnostic push
6859 #pragma GCC diagnostic ignored "-Wformat-nonliteral"
6865 alloc_vprintf2(
char **buf,
const char *fmt, va_list ap)
6868 size_t size = MG_BUF_LEN / 4;
6878 *buf = (
char *)mg_malloc(size);
6883 va_copy(ap_copy, ap);
6884 len = vsnprintf_impl(*buf, size - 1, fmt, ap_copy);
6886 (*buf)[size - 1] = 0;
6898 alloc_vprintf(
char **out_buf,
6900 size_t prealloc_size,
6913 va_copy(ap_copy, ap);
6914 len = vsnprintf_impl(NULL, 0, fmt, ap_copy);
6922 va_copy(ap_copy, ap);
6923 len = alloc_vprintf2(out_buf, fmt, ap_copy);
6926 }
else if ((
size_t)(len) >= prealloc_size) {
6929 *out_buf = (
char *)mg_malloc((
size_t)(len) + 1);
6935 va_copy(ap_copy, ap);
6936 IGNORE_UNUSED_RESULT(
6937 vsnprintf_impl(*out_buf, (
size_t)(len) + 1, fmt, ap_copy));
6943 va_copy(ap_copy, ap);
6944 IGNORE_UNUSED_RESULT(
6945 vsnprintf_impl(prealloc_buf, prealloc_size, fmt, ap_copy));
6947 *out_buf = prealloc_buf;
6954 #if defined(GCC_DIAGNOSTIC)
6956 #pragma GCC diagnostic pop
6961 mg_vprintf(
struct mg_connection *conn,
const char *fmt, va_list ap)
6963 char mem[MG_BUF_LEN];
6967 if ((len = alloc_vprintf(&buf, mem,
sizeof(mem), fmt, ap)) > 0) {
6968 len = mg_write(conn, buf, (
size_t)len);
6970 if ((buf != mem) && (buf != NULL)) {
6979 mg_printf(
struct mg_connection *conn,
const char *fmt, ...)
6985 result = mg_vprintf(conn, fmt, ap);
6993 mg_url_decode(
const char *src,
6997 int is_form_url_encoded)
7000 #define HEXTOI(x) (isdigit(x) ? (x - '0') : (x - 'W'))
7002 for (i = j = 0; (i < src_len) && (j < (dst_len - 1)); i++, j++) {
7003 if ((i < src_len - 2) && (src[i] ==
'%')
7004 && isxdigit(*(
const unsigned char *)(src + i + 1))
7005 && isxdigit(*(
const unsigned char *)(src + i + 2))) {
7006 a = tolower(*(
const unsigned char *)(src + i + 1));
7007 b = tolower(*(
const unsigned char *)(src + i + 2));
7008 dst[j] = (char)((HEXTOI(a) << 4) | HEXTOI(b));
7010 }
else if (is_form_url_encoded && (src[i] ==
'+')) {
7019 return (i >= src_len) ? j : -1;
7024 mg_get_var(
const char *data,
7030 return mg_get_var2(data, data_len, name, dst, dst_len, 0);
7035 mg_get_var2(
const char *data,
7042 const char *p, *e, *s;
7046 if ((dst == NULL) || (dst_len == 0)) {
7048 }
else if ((data == NULL) || (name == NULL) || (data_len == 0)) {
7052 name_len = strlen(name);
7053 e = data + data_len;
7058 for (p = data; p + name_len < e; p++) {
7059 if (((p == data) || (p[-1] ==
'&')) && (p[name_len] ==
'=')
7060 && !mg_strncasecmp(name, p, name_len) && 0 == occurrence--) {
7065 s = (
const char *)memchr(p,
'&', (
size_t)(e - p));
7069 DEBUG_ASSERT(s >= p);
7075 len = mg_url_decode(p, (
int)(s - p), dst, (
int)dst_len, 1);
7093 mg_get_cookie(
const char *cookie_header,
7094 const char *var_name,
7098 const char *s, *p, *end;
7099 int name_len, len = -1;
7101 if ((dst == NULL) || (dst_size == 0)) {
7106 if ((var_name == NULL) || ((s = cookie_header) == NULL)) {
7110 name_len = (int)strlen(var_name);
7111 end = s + strlen(s);
7112 for (; (s = mg_strcasestr(s, var_name)) != NULL; s += name_len) {
7113 if (s[name_len] ==
'=') {
7115 if ((s == cookie_header) || (s[-1] ==
' ')) {
7117 if ((p = strchr(s,
' ')) == NULL) {
7123 if ((*s ==
'"') && (p[-1] ==
'"') && (p > s + 1)) {
7127 if ((
size_t)(p - s) < dst_size) {
7129 mg_strlcpy(dst, s, (
size_t)len + 1);
7141 #if defined(USE_WEBSOCKET) || defined(USE_LUA)
7143 base64_encode(
const unsigned char *src,
int src_len,
char *dst)
7145 static const char *b64 =
7146 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
7149 for (i = j = 0; i < src_len; i += 3) {
7151 b = ((i + 1) >= src_len) ? 0 : src[i + 1];
7152 c = ((i + 2) >= src_len) ? 0 : src[i + 2];
7154 dst[j++] = b64[a >> 2];
7155 dst[j++] = b64[((a & 3) << 4) | (b >> 4)];
7156 if (i + 1 < src_len) {
7157 dst[j++] = b64[(b & 15) << 2 | (c >> 6)];
7159 if (i + 2 < src_len) {
7160 dst[j++] = b64[c & 63];
7163 while (j % 4 != 0) {
7171 #if defined(USE_LUA)
7172 static unsigned char
7173 b64reverse(
char letter)
7175 if ((letter >=
'A') && (letter <=
'Z')) {
7176 return letter -
'A';
7178 if ((letter >=
'a') && (letter <=
'z')) {
7179 return letter -
'a' + 26;
7181 if ((letter >=
'0') && (letter <=
'9')) {
7182 return letter -
'0' + 52;
7184 if (letter ==
'+') {
7187 if (letter ==
'/') {
7190 if (letter ==
'=') {
7198 base64_decode(
const unsigned char *src,
int src_len,
char *dst,
size_t *dst_len)
7201 unsigned char a, b, c, d;
7205 for (i = 0; i < src_len; i += 4) {
7206 a = b64reverse(src[i]);
7211 b = b64reverse(((i + 1) >= src_len) ? 0 : src[i + 1]);
7216 c = b64reverse(((i + 2) >= src_len) ? 0 : src[i + 2]);
7221 d = b64reverse(((i + 3) >= src_len) ? 0 : src[i + 3]);
7226 dst[(*dst_len)++] = (a << 2) + (b >> 4);
7228 dst[(*dst_len)++] = (b << 4) + (c >> 2);
7230 dst[(*dst_len)++] = (c << 6) + d;
7240 is_put_or_delete_method(
const struct mg_connection *conn)
7243 const char *s = conn->request_info.request_method;
7245 && (!strcmp(s,
"PUT") || !strcmp(s,
"DELETE")
7246 || !strcmp(s,
"MKCOL") || !strcmp(s,
"PATCH"));
7252 #if !defined(NO_FILES)
7254 extention_matches_script(
7255 struct mg_connection *conn,
7256 const char *filename
7259 #if !defined(NO_CGI)
7260 if (match_prefix(conn->dom_ctx->config[CGI_EXTENSIONS],
7261 strlen(conn->dom_ctx->config[CGI_EXTENSIONS]),
7267 #if defined(USE_LUA)
7268 if (match_prefix(conn->dom_ctx->config[LUA_SCRIPT_EXTENSIONS],
7269 strlen(conn->dom_ctx->config[LUA_SCRIPT_EXTENSIONS]),
7275 #if defined(USE_DUKTAPE)
7276 if (match_prefix(conn->dom_ctx->config[DUKTAPE_SCRIPT_EXTENSIONS],
7277 strlen(conn->dom_ctx->config[DUKTAPE_SCRIPT_EXTENSIONS]),
7296 substitute_index_file(
struct mg_connection *conn,
7299 struct mg_file_stat *filestat)
7301 const char *list = conn->dom_ctx->config[INDEX_FILES];
7302 struct vec filename_vec;
7303 size_t n = strlen(path);
7309 while ((n > 0) && (path[n - 1] ==
'/')) {
7316 while ((list = next_option(list, &filename_vec, NULL)) != NULL) {
7318 if ((filename_vec.len + 1) > (path_len - (n + 1))) {
7323 mg_strlcpy(path + n + 1, filename_vec.ptr, filename_vec.len + 1);
7326 if (mg_stat(conn, path, filestat)) {
7344 interpret_uri(
struct mg_connection *conn,
7346 size_t filename_buf_len,
7347 struct mg_file_stat *filestat,
7349 int *is_script_resource,
7350 int *is_websocket_request,
7351 int *is_put_or_delete_request
7354 char const *accept_encoding;
7356 #if !defined(NO_FILES)
7357 const char *uri = conn->request_info.local_uri;
7358 const char *root = conn->dom_ctx->config[DOCUMENT_ROOT];
7359 const char *rewrite;
7361 ptrdiff_t match_len;
7362 char gz_path[PATH_MAX];
7364 #if !defined(NO_CGI) || defined(USE_LUA) || defined(USE_DUKTAPE)
7366 size_t tmp_str_len, sep_pos;
7367 int allow_substitute_script_subresources;
7370 (void)filename_buf_len;
7374 memset(filestat, 0,
sizeof(*filestat));
7377 *is_script_resource = 0;
7380 *is_put_or_delete_request = is_put_or_delete_method(conn);
7384 #if defined(USE_WEBSOCKET)
7385 *is_websocket_request = is_websocket_protocol(conn);
7386 #if !defined(NO_FILES)
7387 if (*is_websocket_request && conn->dom_ctx->config[WEBSOCKET_ROOT]) {
7388 root = conn->dom_ctx->config[WEBSOCKET_ROOT];
7392 *is_websocket_request = 0;
7396 conn->accept_gzip = 0;
7397 if ((accept_encoding = mg_get_header(conn,
"Accept-Encoding")) != NULL) {
7398 if (strstr(accept_encoding,
"gzip") != NULL) {
7399 conn->accept_gzip = 1;
7403 #if !defined(NO_FILES)
7419 conn, &truncated, filename, filename_buf_len - 1,
"%s%s", root, uri);
7422 goto interpret_cleanup;
7426 rewrite = conn->dom_ctx->config[URL_REWRITE_PATTERN];
7427 while ((rewrite = next_option(rewrite, &a, &b)) != NULL) {
7428 if ((match_len = match_prefix(a.ptr, a.len, uri)) > 0) {
7432 filename_buf_len - 1,
7442 goto interpret_cleanup;
7448 if (mg_stat(conn, filename, filestat)) {
7449 int uri_len = (int)strlen(uri);
7450 int is_uri_end_slash = (uri_len > 0) && (uri[uri_len - 1] ==
'/');
7456 if (extention_matches_script(conn, filename)) {
7468 *is_script_resource = (!*is_put_or_delete_request);
7473 if (filestat->is_directory && is_uri_end_slash) {
7476 struct mg_file_stat tmp_filestat;
7477 memset(&tmp_filestat, 0,
sizeof(tmp_filestat));
7479 if (substitute_index_file(
7480 conn, filename, filename_buf_len, &tmp_filestat)) {
7484 *filestat = tmp_filestat;
7486 if (extention_matches_script(conn, filename)) {
7488 *is_script_resource = 1;
7491 *is_script_resource = 0;
7492 *is_found = (mg_stat(conn, filename, filestat) ? 1 : 0);
7508 if (conn->accept_gzip) {
7510 conn, &truncated, gz_path,
sizeof(gz_path),
"%s.gz", filename);
7513 goto interpret_cleanup;
7516 if (mg_stat(conn, gz_path, filestat)) {
7518 filestat->is_gzipped = 1;
7526 #if !defined(NO_CGI) || defined(USE_LUA) || defined(USE_DUKTAPE)
7529 tmp_str_len = strlen(filename);
7530 tmp_str = (
char *)mg_malloc_ctx(tmp_str_len + PATH_MAX + 1, conn->phys_ctx);
7533 goto interpret_cleanup;
7535 memcpy(tmp_str, filename, tmp_str_len + 1);
7538 allow_substitute_script_subresources =
7539 !mg_strcasecmp(conn->dom_ctx->config[ALLOW_INDEX_SCRIPT_SUB_RES],
7542 sep_pos = tmp_str_len;
7543 while (sep_pos > 0) {
7545 if (tmp_str[sep_pos] ==
'/') {
7546 int is_script = 0, does_exist = 0;
7548 tmp_str[sep_pos] = 0;
7550 is_script = extention_matches_script(conn, tmp_str);
7551 does_exist = mg_stat(conn, tmp_str, filestat);
7554 if (does_exist && is_script) {
7555 filename[sep_pos] = 0;
7556 memmove(filename + sep_pos + 2,
7557 filename + sep_pos + 1,
7558 strlen(filename + sep_pos + 1) + 1);
7559 conn->path_info = filename + sep_pos + 1;
7560 filename[sep_pos + 1] =
'/';
7561 *is_script_resource = 1;
7566 if (allow_substitute_script_subresources) {
7567 if (substitute_index_file(
7568 conn, tmp_str, tmp_str_len + PATH_MAX, filestat)) {
7571 if (extention_matches_script(conn, tmp_str)) {
7575 DEBUG_TRACE(
"Substitute script %s serving path %s",
7580 tmp_str2 = mg_strdup_ctx(filename + sep_pos + 1,
7593 goto interpret_cleanup;
7595 sep_pos = strlen(tmp_str);
7596 filename[sep_pos] = 0;
7597 conn->path_info = filename + sep_pos + 1;
7598 *is_script_resource = 1;
7604 DEBUG_TRACE(
"Substitute file %s serving path %s",
7609 filename[sep_pos] = 0;
7610 conn->path_info = 0;
7611 *is_script_resource = 0;
7618 tmp_str[sep_pos] =
'/';
7628 #if !defined(NO_FILES)
7631 memset(filestat, 0,
sizeof(*filestat));
7634 *is_script_resource = 0;
7635 *is_websocket_request = 0;
7636 *is_put_or_delete_request = 0;
7646 get_http_header_len(
const char *buf,
int buflen)
7649 for (i = 0; i < buflen; i++) {
7651 const unsigned char c = ((
const unsigned char *)buf)[i];
7653 if ((c < 128) && ((char)c !=
'\r') && ((char)c !=
'\n')
7659 if (i < buflen - 1) {
7660 if ((buf[i] ==
'\n') && (buf[i + 1] ==
'\n')) {
7669 if (i < buflen - 3) {
7670 if ((buf[i] ==
'\r') && (buf[i + 1] ==
'\n') && (buf[i + 2] ==
'\r')
7671 && (buf[i + 3] ==
'\n')) {
7682 #if !defined(NO_CACHING)
7685 get_month_index(
const char *s)
7689 for (i = 0; i < ARRAY_SIZE(month_names); i++) {
7690 if (!strcmp(s, month_names[i])) {
7701 parse_date_string(
const char *datetime)
7703 char month_str[32] = {0};
7704 int second, minute, hour, day, month, year;
7705 time_t result = (time_t)0;
7708 if ((sscanf(datetime,
7709 "%d/%3s/%d %d:%d:%d",
7717 || (sscanf(datetime,
7718 "%d %3s %d %d:%d:%d",
7726 || (sscanf(datetime,
7727 "%*3s, %d %3s %d %d:%d:%d",
7735 || (sscanf(datetime,
7736 "%d-%3s-%d %d:%d:%d",
7744 month = get_month_index(month_str);
7745 if ((month >= 0) && (year >= 1970)) {
7746 memset(&tm, 0,
sizeof(tm));
7747 tm.tm_year = year - 1900;
7753 result = timegm(&tm);
7765 remove_double_dots_and_double_slashes(
char *s)
7769 while ((s[0] ==
'.') && (s[1] ==
'.')) {
7773 while (*s !=
'\0') {
7775 if ((s[-1] ==
'/') || (s[-1] ==
'\\')) {
7777 while (s[0] !=
'\0') {
7778 if ((s[0] ==
'/') || (s[0] ==
'\\')) {
7780 }
else if ((s[0] ==
'.') && (s[1] ==
'.')) {
7792 static const struct {
7793 const char *extension;
7795 const char *mime_type;
7796 } builtin_mime_types[] = {
7800 {
".doc", 4,
"application/msword"},
7801 {
".eps", 4,
"application/postscript"},
7802 {
".exe", 4,
"application/octet-stream"},
7803 {
".js", 3,
"application/javascript"},
7804 {
".json", 5,
"application/json"},
7805 {
".pdf", 4,
"application/pdf"},
7806 {
".ps", 3,
"application/postscript"},
7807 {
".rtf", 4,
"application/rtf"},
7808 {
".xhtml", 6,
"application/xhtml+xml"},
7809 {
".xsl", 4,
"application/xml"},
7810 {
".xslt", 5,
"application/xml"},
7813 {
".ttf", 4,
"application/font-sfnt"},
7814 {
".cff", 4,
"application/font-sfnt"},
7815 {
".otf", 4,
"application/font-sfnt"},
7816 {
".aat", 4,
"application/font-sfnt"},
7817 {
".sil", 4,
"application/font-sfnt"},
7818 {
".pfr", 4,
"application/font-tdpfr"},
7819 {
".woff", 5,
"application/font-woff"},
7822 {
".mp3", 4,
"audio/mpeg"},
7823 {
".oga", 4,
"audio/ogg"},
7824 {
".ogg", 4,
"audio/ogg"},
7827 {
".gif", 4,
"image/gif"},
7828 {
".ief", 4,
"image/ief"},
7829 {
".jpeg", 5,
"image/jpeg"},
7830 {
".jpg", 4,
"image/jpeg"},
7831 {
".jpm", 4,
"image/jpm"},
7832 {
".jpx", 4,
"image/jpx"},
7833 {
".png", 4,
"image/png"},
7834 {
".svg", 4,
"image/svg+xml"},
7835 {
".tif", 4,
"image/tiff"},
7836 {
".tiff", 5,
"image/tiff"},
7839 {
".wrl", 4,
"model/vrml"},
7842 {
".css", 4,
"text/css"},
7843 {
".csv", 4,
"text/csv"},
7844 {
".htm", 4,
"text/html"},
7845 {
".html", 5,
"text/html"},
7846 {
".sgm", 4,
"text/sgml"},
7847 {
".shtm", 5,
"text/html"},
7848 {
".shtml", 6,
"text/html"},
7849 {
".txt", 4,
"text/plain"},
7850 {
".xml", 4,
"text/xml"},
7853 {
".mov", 4,
"video/quicktime"},
7854 {
".mp4", 4,
"video/mp4"},
7855 {
".mpeg", 5,
"video/mpeg"},
7856 {
".mpg", 4,
"video/mpeg"},
7857 {
".ogv", 4,
"video/ogg"},
7858 {
".qt", 3,
"video/quicktime"},
7863 {
".arj", 4,
"application/x-arj-compressed"},
7864 {
".gz", 3,
"application/x-gunzip"},
7865 {
".rar", 4,
"application/x-arj-compressed"},
7866 {
".swf", 4,
"application/x-shockwave-flash"},
7867 {
".tar", 4,
"application/x-tar"},
7868 {
".tgz", 4,
"application/x-tar-gz"},
7869 {
".torrent", 8,
"application/x-bittorrent"},
7870 {
".ppt", 4,
"application/x-mspowerpoint"},
7871 {
".xls", 4,
"application/x-msexcel"},
7872 {
".zip", 4,
"application/x-zip-compressed"},
7876 {
".aif", 4,
"audio/x-aif"},
7877 {
".m3u", 4,
"audio/x-mpegurl"},
7878 {
".mid", 4,
"audio/x-midi"},
7879 {
".ra", 3,
"audio/x-pn-realaudio"},
7880 {
".ram", 4,
"audio/x-pn-realaudio"},
7881 {
".wav", 4,
"audio/x-wav"},
7882 {
".bmp", 4,
"image/bmp"},
7883 {
".ico", 4,
"image/x-icon"},
7884 {
".pct", 4,
"image/x-pct"},
7885 {
".pict", 5,
"image/pict"},
7886 {
".rgb", 4,
"image/x-rgb"},
7887 {
".webm", 5,
"video/webm"},
7888 {
".asf", 4,
"video/x-ms-asf"},
7889 {
".avi", 4,
"video/x-msvideo"},
7890 {
".m4v", 4,
"video/x-m4v"},
7895 mg_get_builtin_mime_type(
const char *path)
7900 path_len = strlen(path);
7902 for (i = 0; builtin_mime_types[i].extension != NULL; i++) {
7903 ext = path + (path_len - builtin_mime_types[i].ext_len);
7904 if ((path_len > builtin_mime_types[i].ext_len)
7905 && (mg_strcasecmp(ext, builtin_mime_types[i].extension) == 0)) {
7906 return builtin_mime_types[i].mime_type;
7910 return "text/plain";
7917 get_mime_type(
struct mg_connection *conn,
const char *path,
struct vec *vec)
7919 struct vec ext_vec, mime_vec;
7920 const char *list, *ext;
7923 path_len = strlen(path);
7925 if ((conn == NULL) || (vec == NULL)) {
7927 memset(vec,
'\0',
sizeof(
struct vec));
7934 list = conn->dom_ctx->config[EXTRA_MIME_TYPES];
7935 while ((list = next_option(list, &ext_vec, &mime_vec)) != NULL) {
7937 ext = path + path_len - ext_vec.len;
7938 if (mg_strncasecmp(ext, ext_vec.ptr, ext_vec.len) == 0) {
7944 vec->ptr = mg_get_builtin_mime_type(path);
7945 vec->len = strlen(vec->ptr);
7952 bin2str(
char *to,
const unsigned char *p,
size_t len)
7954 static const char *hex =
"0123456789abcdef";
7956 for (; len--; p++) {
7957 *to++ = hex[p[0] >> 4];
7958 *to++ = hex[p[0] & 0x0f];
7967 mg_md5(
char buf[33], ...)
7969 md5_byte_t hash[16];
7977 while ((p = va_arg(ap,
const char *)) != NULL) {
7978 md5_append(&ctx, (
const md5_byte_t *)p, strlen(p));
7982 md5_finish(&ctx, hash);
7983 bin2str(buf, hash,
sizeof(hash));
7990 check_password(
const char *method,
7997 const char *response)
7999 char ha2[32 + 1], expected_response[32 + 1];
8002 if ((method == NULL) || (nonce == NULL) || (nc == NULL) || (cnonce == NULL)
8003 || (qop == NULL) || (response == NULL)) {
8008 if (strlen(response) != 32) {
8012 mg_md5(ha2, method,
":", uri, NULL);
8013 mg_md5(expected_response,
8027 return mg_strcasecmp(response, expected_response) == 0;
8034 open_auth_file(
struct mg_connection *conn,
8036 struct mg_file *filep)
8038 if ((conn != NULL) && (conn->dom_ctx != NULL)) {
8039 char name[PATH_MAX];
8041 *gpass = conn->dom_ctx->config[GLOBAL_PASSWORDS_FILE];
8044 if (gpass != NULL) {
8046 if (!mg_fopen(conn, gpass, MG_FOPEN_MODE_READ, filep)) {
8049 mg_cry_internal(conn,
"fopen(%s): %s", gpass, strerror(ERRNO));
8057 }
else if (mg_stat(conn, path, &filep->stat)
8058 && filep->stat.is_directory) {
8065 PASSWORDS_FILE_NAME);
8067 if (truncated || !mg_fopen(conn, name, MG_FOPEN_MODE_READ, filep)) {
8073 DEBUG_TRACE(
"fopen(%s): %s", name, strerror(ERRNO));
8078 for (p = path, e = p + strlen(p) - 1; e > p; e--) {
8090 PASSWORDS_FILE_NAME);
8092 if (truncated || !mg_fopen(conn, name, MG_FOPEN_MODE_READ, filep)) {
8098 DEBUG_TRACE(
"fopen(%s): %s", name, strerror(ERRNO));
8108 char *user, *uri, *cnonce, *response, *qop, *nc, *nonce;
8114 parse_auth_header(
struct mg_connection *conn,
8119 char *name, *value, *s;
8120 const char *auth_header;
8127 (void)memset(ah, 0,
sizeof(*ah));
8128 if (((auth_header = mg_get_header(conn,
"Authorization")) == NULL)
8129 || mg_strncasecmp(auth_header,
"Digest ", 7) != 0) {
8134 (void)mg_strlcpy(buf, auth_header + 7, buf_size);
8140 while (isspace(*(
unsigned char *)s)) {
8143 name = skip_quoted(&s,
"=",
" ", 0);
8148 value = skip_quoted(&s,
"\"",
" ",
'\\');
8153 value = skip_quoted(&s,
", ",
" ", 0);
8156 if (*name ==
'\0') {
8160 if (!strcmp(name,
"username")) {
8162 }
else if (!strcmp(name,
"cnonce")) {
8164 }
else if (!strcmp(name,
"response")) {
8165 ah->response = value;
8166 }
else if (!strcmp(name,
"uri")) {
8168 }
else if (!strcmp(name,
"qop")) {
8170 }
else if (!strcmp(name,
"nc")) {
8172 }
else if (!strcmp(name,
"nonce")) {
8177 #if !defined(NO_NONCE_CHECK)
8179 if (ah->nonce == NULL) {
8183 nonce = strtoull(ah->nonce, &s, 10);
8184 if ((s == NULL) || (*s != 0)) {
8189 nonce ^= conn->dom_ctx->auth_nonce_mask;
8199 if (nonce < (uint64_t)conn->phys_ctx->start_time) {
8206 if (nonce >= ((uint64_t)conn->phys_ctx->start_time
8207 + conn->dom_ctx->nonce_count)) {
8215 if (ah->user != NULL) {
8216 conn->request_info.remote_user =
8217 mg_strdup_ctx(ah->user, conn->phys_ctx);
8227 mg_fgets(
char *buf,
size_t size,
struct mg_file *filep,
char **p)
8229 #if defined(MG_USE_OPEN_FILE)
8241 #if defined(MG_USE_OPEN_FILE)
8242 if ((filep->access.membuf != NULL) && (*p != NULL)) {
8243 memend = (
const char *)&filep->access.membuf[filep->stat.size];
8245 eof = (
char *)memchr(*p,
'\n', (
size_t)(memend - *p));
8252 ((size_t)(eof - *p) > (size - 1)) ? (size - 1) : (
size_t)(eof - *p);
8253 memcpy(buf, *p, len);
8256 return len ? eof : NULL;
8259 if (filep->access.fp != NULL) {
8260 return fgets(buf, (
int)size, filep->access.fp);
8272 #define INITIAL_DEPTH 9
8273 #if INITIAL_DEPTH <= 0
8274 #error Bad INITIAL_DEPTH for recursion, set to at least 1
8277 struct read_auth_file_struct {
8278 struct mg_connection *conn;
8281 char buf[256 + 256 + 40];
8283 const char *f_domain;
8289 read_auth_file(
struct mg_file *filep,
8290 struct read_auth_file_struct *workdata,
8294 int is_authorized = 0;
8298 if (!filep || !workdata || (0 == depth)) {
8303 #if defined(MG_USE_OPEN_FILE)
8304 p = (
char *)filep->access.membuf;
8306 while (mg_fgets(workdata->buf,
sizeof(workdata->buf), filep, &p) != NULL) {
8307 l = strlen(workdata->buf);
8309 if (isspace(workdata->buf[l - 1])
8310 || iscntrl(workdata->buf[l - 1])) {
8312 workdata->buf[l] = 0;
8320 workdata->f_user = workdata->buf;
8322 if (workdata->f_user[0] ==
':') {
8326 if (workdata->f_user[1] ==
'#') {
8329 }
else if (!strncmp(workdata->f_user + 1,
"include=", 8)) {
8330 if (mg_fopen(workdata->conn,
8331 workdata->f_user + 9,
8334 is_authorized = read_auth_file(&fp, workdata, depth - 1);
8342 if (is_authorized) {
8343 return is_authorized;
8346 mg_cry_internal(workdata->conn,
8347 "%s: cannot open authorization file: %s",
8355 mg_cry_internal(workdata->conn,
8356 "%s: syntax error in authorization file: %s",
8362 workdata->f_domain = strchr(workdata->f_user,
':');
8363 if (workdata->f_domain == NULL) {
8364 mg_cry_internal(workdata->conn,
8365 "%s: syntax error in authorization file: %s",
8370 *(
char *)(workdata->f_domain) = 0;
8371 (workdata->f_domain)++;
8373 workdata->f_ha1 = strchr(workdata->f_domain,
':');
8374 if (workdata->f_ha1 == NULL) {
8375 mg_cry_internal(workdata->conn,
8376 "%s: syntax error in authorization file: %s",
8381 *(
char *)(workdata->f_ha1) = 0;
8382 (workdata->f_ha1)++;
8384 if (!strcmp(workdata->ah.user, workdata->f_user)
8385 && !strcmp(workdata->domain, workdata->f_domain)) {
8386 return check_password(workdata->conn->request_info.request_method,
8391 workdata->ah.cnonce,
8393 workdata->ah.response);
8397 return is_authorized;
8403 authorize(
struct mg_connection *conn,
struct mg_file *filep,
const char *realm)
8405 struct read_auth_file_struct workdata;
8406 char buf[MG_BUF_LEN];
8408 if (!conn || !conn->dom_ctx) {
8412 memset(&workdata, 0,
sizeof(workdata));
8413 workdata.conn = conn;
8415 if (!parse_auth_header(conn, buf,
sizeof(buf), &workdata.ah)) {
8420 workdata.domain = realm;
8422 workdata.domain = conn->dom_ctx->config[AUTHENTICATION_DOMAIN];
8425 return read_auth_file(filep, &workdata, INITIAL_DEPTH);
8431 mg_check_digest_access_authentication(
struct mg_connection *conn,
8433 const char *filename)
8435 struct mg_file file = STRUCT_FILE_INITIALIZER;
8438 if (!conn || !filename) {
8441 if (!mg_fopen(conn, filename, MG_FOPEN_MODE_READ, &file)) {
8445 auth = authorize(conn, &file, realm);
8447 mg_fclose(&file.access);
8455 check_authorization(
struct mg_connection *conn,
const char *path)
8457 char fname[PATH_MAX];
8458 struct vec uri_vec, filename_vec;
8460 struct mg_file file = STRUCT_FILE_INITIALIZER;
8461 int authorized = 1, truncated;
8463 if (!conn || !conn->dom_ctx) {
8467 list = conn->dom_ctx->config[PROTECT_URI];
8468 while ((list = next_option(list, &uri_vec, &filename_vec)) != NULL) {
8469 if (!memcmp(conn->request_info.local_uri, uri_vec.ptr, uri_vec.len)) {
8475 (
int)filename_vec.len,
8479 || !mg_fopen(conn, fname, MG_FOPEN_MODE_READ, &file)) {
8480 mg_cry_internal(conn,
8481 "%s: cannot open %s: %s",
8490 if (!is_file_opened(&file.access)) {
8491 open_auth_file(conn, path, &file);
8494 if (is_file_opened(&file.access)) {
8495 authorized = authorize(conn, &file, NULL);
8496 (void)mg_fclose(&file.access);
8505 send_authorization_request(
struct mg_connection *conn,
const char *realm)
8508 time_t curtime = time(NULL);
8509 uint64_t nonce = (uint64_t)(conn->phys_ctx->start_time);
8512 realm = conn->dom_ctx->config[AUTHENTICATION_DOMAIN];
8515 (void)pthread_mutex_lock(&conn->phys_ctx->nonce_mutex);
8516 nonce += conn->dom_ctx->nonce_count;
8517 ++conn->dom_ctx->nonce_count;
8518 (void)pthread_mutex_unlock(&conn->phys_ctx->nonce_mutex);
8520 nonce ^= conn->dom_ctx->auth_nonce_mask;
8521 conn->status_code = 401;
8522 conn->must_close = 1;
8524 gmt_time_string(date,
sizeof(date), &curtime);
8526 mg_printf(conn,
"HTTP/1.1 401 Unauthorized\r\n");
8527 send_no_cache_header(conn);
8528 send_additional_header(conn);
8531 "Connection: %s\r\n"
8532 "Content-Length: 0\r\n"
8533 "WWW-Authenticate: Digest qop=\"auth\", realm=\"%s\", "
8534 "nonce=\"%" UINT64_FMT
"\"\r\n\r\n",
8536 suggest_connection_header(conn),
8546 mg_send_digest_access_authentication_request(
struct mg_connection *conn,
8549 if (conn && conn->dom_ctx) {
8550 send_authorization_request(conn, realm);
8557 #if !defined(NO_FILES)
8559 is_authorized_for_put(
struct mg_connection *conn)
8562 struct mg_file file = STRUCT_FILE_INITIALIZER;
8563 const char *passfile = conn->dom_ctx->config[PUT_DELETE_PASSWORDS_FILE];
8566 if (passfile != NULL
8567 && mg_fopen(conn, passfile, MG_FOPEN_MODE_READ, &file)) {
8568 ret = authorize(conn, &file, NULL);
8569 (void)mg_fclose(&file.access);
8580 mg_modify_passwords_file(
const char *fname,
8586 char line[512], u[512] =
"", d[512] =
"", ha1[33], tmp[PATH_MAX + 8];
8593 if ((pass != NULL) && (pass[0] ==
'\0')) {
8598 if ((fname == NULL) || (domain == NULL) || (user == NULL)) {
8605 if (strchr(user,
':') != NULL) {
8608 if (strchr(domain,
':') != NULL) {
8614 for (i = 0; ((i < 255) && (user[i] != 0)); i++) {
8615 if (iscntrl(user[i])) {
8622 for (i = 0; ((i < 255) && (domain[i] != 0)); i++) {
8623 if (iscntrl(domain[i])) {
8632 if ((strlen(fname) + 4) >= PATH_MAX) {
8638 strcat(tmp,
".tmp");
8642 if ((fp = fopen(fname,
"a+")) != NULL) {
8647 if ((fp = fopen(fname,
"r")) == NULL) {
8649 }
else if ((fp2 = fopen(tmp,
"w+")) == NULL) {
8655 while (fgets(line,
sizeof(line), fp) != NULL) {
8656 if (sscanf(line,
"%255[^:]:%255[^:]:%*s", u, d) != 2) {
8662 if (!strcmp(u, user) && !strcmp(d, domain)) {
8665 mg_md5(ha1, user,
":", domain,
":", pass, NULL);
8666 fprintf(fp2,
"%s:%s:%s\n", user, domain, ha1);
8669 fprintf(fp2,
"%s", line);
8674 if (!found && (pass != NULL)) {
8675 mg_md5(ha1, user,
":", domain,
":", pass, NULL);
8676 fprintf(fp2,
"%s:%s:%s\n", user, domain, ha1);
8684 IGNORE_UNUSED_RESULT(
remove(fname));
8685 IGNORE_UNUSED_RESULT(rename(tmp, fname));
8692 is_valid_port(
unsigned long port)
8694 return (port <= 0xffff);
8699 mg_inet_pton(
int af,
const char *src,
void *dst,
size_t dstlen)
8701 struct addrinfo hints, *res, *ressave;
8705 memset(&hints, 0,
sizeof(
struct addrinfo));
8706 hints.ai_family = af;
8708 gai_ret = getaddrinfo(src, NULL, &hints, &res);
8723 if (dstlen >= (
size_t)res->ai_addrlen) {
8724 memcpy(dst, res->ai_addr, res->ai_addrlen);
8730 freeaddrinfo(ressave);
8736 connect_socket(
struct mg_context *ctx ,
8749 *sock = INVALID_SOCKET;
8750 memset(sa, 0,
sizeof(*sa));
8766 if ((port <= 0) || !is_valid_port((
unsigned)port)) {
8776 #if !defined(NO_SSL)
8777 #if !defined(NO_SSL_DL)
8778 #if defined(OPENSSL_API_1_1)
8779 if (use_ssl && (TLS_client_method == NULL)) {
8785 "SSL is not initialized");
8789 if (use_ssl && (SSLv23_client_method == NULL)) {
8795 "SSL is not initialized");
8807 if (mg_inet_pton(AF_INET, host, &sa->sin,
sizeof(sa->sin))) {
8808 sa->sin.sin_family = AF_INET;
8809 sa->sin.sin_port = htons((uint16_t)port);
8811 #if defined(USE_IPV6)
8812 }
else if (mg_inet_pton(AF_INET6, host, &sa->sin6,
sizeof(sa->sin6))) {
8813 sa->sin6.sin6_family = AF_INET6;
8814 sa->sin6.sin6_port = htons((uint16_t)port);
8816 }
else if (host[0] ==
'[') {
8819 size_t l = strlen(host + 1);
8820 char *h = (l > 1) ? mg_strdup_ctx(host + 1, ctx) : NULL;
8823 if (mg_inet_pton(AF_INET6, h, &sa->sin6,
sizeof(sa->sin6))) {
8824 sa->sin6.sin6_family = AF_INET6;
8825 sa->sin6.sin6_port = htons((uint16_t)port);
8844 *sock = socket(PF_INET, SOCK_STREAM, 0);
8846 #if defined(USE_IPV6)
8847 else if (ip_ver == 6) {
8848 *sock = socket(PF_INET6, SOCK_STREAM, 0);
8852 if (*sock == INVALID_SOCKET) {
8862 if (0 != set_non_blocking_mode(*sock)) {
8867 "Cannot set socket to non-blocking: %s",
8870 *sock = INVALID_SOCKET;
8874 set_close_on_exec(*sock, fc(ctx));
8878 conn_ret = connect(*sock,
8879 (
struct sockaddr *)((
void *)&sa->sin),
8882 #if defined(USE_IPV6)
8883 else if (ip_ver == 6) {
8885 conn_ret = connect(*sock,
8886 (
struct sockaddr *)((
void *)&sa->sin6),
8892 if (conn_ret != 0) {
8893 DWORD err = WSAGetLastError();
8894 conn_ret = (int)err;
8895 #if !defined(EINPROGRESS)
8896 #define EINPROGRESS (WSAEWOULDBLOCK)
8901 if ((conn_ret != 0) && (conn_ret != EINPROGRESS)) {
8904 void *psockerr = &sockerr;
8907 int len = (int)
sizeof(sockerr);
8909 socklen_t len = (socklen_t)
sizeof(sockerr);
8913 struct pollfd pfd[1];
8915 int ms_wait = 10000;
8923 pfd[0].events = POLLOUT;
8924 pollres = mg_poll(pfd, 1, (
int)(ms_wait), &(ctx->stop_flag));
8932 "connect(%s:%d): timeout",
8936 *sock = INVALID_SOCKET;
8941 ret = getsockopt(*sock, SOL_SOCKET, SO_ERROR, (
char *)psockerr, &len);
8943 ret = getsockopt(*sock, SOL_SOCKET, SO_ERROR, psockerr, &len);
8946 if ((ret != 0) || (sockerr != 0)) {
8952 "connect(%s:%d): error %s",
8957 *sock = INVALID_SOCKET;
8967 mg_url_encode(
const char *src,
char *dst,
size_t dst_len)
8969 static const char *dont_escape =
"._-$,;~()";
8970 static const char *hex =
"0123456789abcdef";
8972 const char *end = dst + dst_len - 1;
8974 for (; ((*src !=
'\0') && (pos < end)); src++, pos++) {
8975 if (isalnum(*(
const unsigned char *)src)
8976 || (strchr(dont_escape, *(
const unsigned char *)src) != NULL)) {
8978 }
else if (pos + 2 < end) {
8980 pos[1] = hex[(*(
const unsigned char *)src) >> 4];
8981 pos[2] = hex[(*(
const unsigned char *)src) & 0xf];
8989 return (*src ==
'\0') ? (int)(pos - dst) : -1;
8995 print_dir_entry(
struct de *de)
8999 char size[64], mod[64];
9000 #if defined(REENTRANT_TIME)
9002 struct tm *tm = &_tm;
9007 hrefsize = PATH_MAX * 3;
9008 href = (
char *)mg_malloc(hrefsize);
9012 if (de->file.is_directory) {
9013 mg_snprintf(de->conn,
9022 if (de->file.size < 1024) {
9023 mg_snprintf(de->conn,
9028 (
int)de->file.size);
9029 }
else if (de->file.size < 0x100000) {
9030 mg_snprintf(de->conn,
9035 (
double)de->file.size / 1024.0);
9036 }
else if (de->file.size < 0x40000000) {
9037 mg_snprintf(de->conn,
9042 (
double)de->file.size / 1048576);
9044 mg_snprintf(de->conn,
9049 (
double)de->file.size / 1073741824);
9056 #if defined(REENTRANT_TIME)
9057 localtime_r(&de->file.last_modified, tm);
9059 tm = localtime(&de->file.last_modified);
9062 strftime(mod,
sizeof(mod),
"%d-%b-%Y %H:%M", tm);
9064 mg_strlcpy(mod,
"01-Jan-1970 00:00",
sizeof(mod));
9065 mod[
sizeof(mod) - 1] =
'\0';
9067 mg_url_encode(de->file_name, href, hrefsize);
9069 "<tr><td><a href=\"%s%s%s\">%s%s</a></td>"
9070 "<td> %s</td><td> %s</td></tr>\n",
9071 de->conn->request_info.local_uri,
9073 de->file.is_directory ?
"/" :
"",
9075 de->file.is_directory ?
"/" :
"",
9088 compare_dir_entries(
const void *p1,
const void *p2)
9091 const struct de *a = (
const struct de *)p1, *b = (
const struct de *)p2;
9092 const char *query_string = a->conn->request_info.query_string;
9095 if (query_string == NULL) {
9096 query_string =
"na";
9099 if (a->file.is_directory && !b->file.is_directory) {
9101 }
else if (!a->file.is_directory && b->file.is_directory) {
9103 }
else if (*query_string ==
'n') {
9104 cmp_result = strcmp(a->file_name, b->file_name);
9105 }
else if (*query_string ==
's') {
9106 cmp_result = (a->file.size == b->file.size)
9108 : ((a->file.size > b->file.size) ? 1 : -1);
9109 }
else if (*query_string ==
'd') {
9111 (a->file.last_modified == b->file.last_modified)
9113 : ((a->file.last_modified > b->file.last_modified) ? 1
9117 return (query_string[1] ==
'd') ? -cmp_result : cmp_result;
9124 must_hide_file(
struct mg_connection *conn,
const char *path)
9126 if (conn && conn->dom_ctx) {
9127 const char *pw_pattern =
"**" PASSWORDS_FILE_NAME
"$";
9128 const char *pattern = conn->dom_ctx->config[HIDE_FILES];
9129 return (match_prefix(pw_pattern, strlen(pw_pattern), path) > 0)
9130 || ((pattern != NULL)
9131 && (match_prefix(pattern, strlen(pattern), path) > 0));
9138 scan_directory(
struct mg_connection *conn,
9141 int (*cb)(
struct de *,
void *))
9143 char path[PATH_MAX];
9149 if ((dirp = mg_opendir(conn, dir)) == NULL) {
9154 while ((dp = mg_readdir(dirp)) != NULL) {
9156 if (!strcmp(dp->d_name,
".") || !strcmp(dp->d_name,
"..")
9157 || must_hide_file(conn, dp->d_name)) {
9162 conn, &truncated, path,
sizeof(path),
"%s/%s", dir, dp->d_name);
9169 memset(&de.file, 0,
sizeof(de.file));
9176 if (!mg_stat(conn, path, &de.file)) {
9177 mg_cry_internal(conn,
9178 "%s: mg_stat(%s) failed: %s",
9183 de.file_name = dp->d_name;
9186 (void)mg_closedir(dirp);
9192 #if !defined(NO_FILES)
9194 remove_directory(
struct mg_connection *conn,
const char *dir)
9196 char path[PATH_MAX];
9203 if ((dirp = mg_opendir(conn, dir)) == NULL) {
9208 while ((dp = mg_readdir(dirp)) != NULL) {
9211 if (!strcmp(dp->d_name,
".") || !strcmp(dp->d_name,
"..")) {
9216 conn, &truncated, path,
sizeof(path),
"%s/%s", dir, dp->d_name);
9223 memset(&de.file, 0,
sizeof(de.file));
9231 if (!mg_stat(conn, path, &de.file)) {
9232 mg_cry_internal(conn,
9233 "%s: mg_stat(%s) failed: %s",
9240 if (de.file.is_directory) {
9241 if (remove_directory(conn, path) == 0) {
9246 if (mg_remove(conn, path) == 0) {
9251 (void)mg_closedir(dirp);
9253 IGNORE_UNUSED_RESULT(rmdir(dir));
9261 struct dir_scan_data {
9263 unsigned int num_entries;
9264 unsigned int arr_size;
9270 realloc2(
void *ptr,
size_t size)
9272 void *new_ptr = mg_realloc(ptr, size);
9273 if (new_ptr == NULL) {
9281 dir_scan_callback(
struct de *de,
void *data)
9283 struct dir_scan_data *dsd = (
struct dir_scan_data *)data;
9285 if ((dsd->entries == NULL) || (dsd->num_entries >= dsd->arr_size)) {
9288 (
struct de *)realloc2(dsd->entries,
9289 dsd->arr_size *
sizeof(dsd->entries[0]));
9291 if (dsd->entries == NULL) {
9293 dsd->num_entries = 0;
9295 dsd->entries[dsd->num_entries].file_name = mg_strdup(de->file_name);
9296 dsd->entries[dsd->num_entries].file = de->file;
9297 dsd->entries[dsd->num_entries].conn = de->conn;
9306 handle_directory_request(
struct mg_connection *conn,
const char *dir)
9310 struct dir_scan_data data = {NULL, 0, 128};
9312 time_t curtime = time(NULL);
9314 if (!scan_directory(conn, dir, &data, dir_scan_callback)) {
9315 mg_send_http_error(conn,
9317 "Error: Cannot open directory\nopendir(%s): %s",
9323 gmt_time_string(date,
sizeof(date), &curtime);
9329 sort_direction = ((conn->request_info.query_string != NULL)
9330 && (conn->request_info.query_string[1] ==
'd'))
9334 conn->must_close = 1;
9335 mg_printf(conn,
"HTTP/1.1 200 OK\r\n");
9336 send_static_cache_header(conn);
9337 send_additional_header(conn);
9340 "Connection: close\r\n"
9341 "Content-Type: text/html; charset=utf-8\r\n\r\n",
9344 "<html><head><title>Index of %s</title>"
9345 "<style>th {text-align: left;}</style></head>"
9346 "<body><h1>Index of %s</h1><pre><table cellpadding=\"0\">"
9347 "<tr><th><a href=\"?n%c\">Name</a></th>"
9348 "<th><a href=\"?d%c\">Modified</a></th>"
9349 "<th><a href=\"?s%c\">Size</a></th></tr>"
9350 "<tr><td colspan=\"3\"><hr></td></tr>",
9351 conn->request_info.local_uri,
9352 conn->request_info.local_uri,
9359 "<tr><td><a href=\"%s%s\">%s</a></td>"
9360 "<td> %s</td><td> %s</td></tr>\n",
9361 conn->request_info.local_uri,
9368 if (data.entries != NULL) {
9370 (
size_t)data.num_entries,
9371 sizeof(data.entries[0]),
9372 compare_dir_entries);
9373 for (i = 0; i < data.num_entries; i++) {
9374 print_dir_entry(&data.entries[i]);
9375 mg_free(data.entries[i].file_name);
9377 mg_free(data.entries);
9380 mg_printf(conn,
"%s",
"</table></body></html>");
9381 conn->status_code = 200;
9387 send_file_data(
struct mg_connection *conn,
9388 struct mg_file *filep,
9392 char buf[MG_BUF_LEN];
9393 int to_read, num_read, num_written;
9396 if (!filep || !conn) {
9401 size = (filep->stat.size > INT64_MAX) ? INT64_MAX
9402 : (int64_t)(filep->stat.size);
9403 offset = (offset < 0) ? 0 : ((offset > size) ? size : offset);
9405 #if defined(MG_USE_OPEN_FILE)
9406 if ((len > 0) && (filep->access.membuf != NULL) && (size > 0)) {
9408 if (len > size - offset) {
9409 len = size - offset;
9411 mg_write(conn, filep->access.membuf + offset, (
size_t)len);
9414 if (len > 0 && filep->access.fp != NULL) {
9416 #if defined(__linux__)
9418 if ((conn->ssl == 0) && (conn->throttle == 0)
9419 && (!mg_strcasecmp(conn->dom_ctx->config[ALLOW_SENDFILE_CALL],
9421 off_t sf_offs = (off_t)offset;
9423 int sf_file = fileno(filep->access.fp);
9430 (size_t)((len < 0x7FFFF000) ? len : 0x7FFFF000);
9432 sendfile(conn->client.sock, sf_file, &sf_offs, sf_tosend);
9436 }
else if (loop_cnt == 0) {
9442 }
else if (sf_sent == 0) {
9448 }
while ((len > 0) && (sf_sent >= 0));
9457 offset = (int64_t)sf_offs;
9460 if ((offset > 0) && (fseeko(filep->access.fp, offset, SEEK_SET) != 0)) {
9461 mg_cry_internal(conn,
9462 "%s: fseeko() failed: %s",
9469 "Error: Unable to access file at requested position.");
9473 to_read =
sizeof(buf);
9474 if ((int64_t)to_read > len) {
9480 (
int)fread(buf, 1, (
size_t)to_read, filep->access.fp))
9486 if ((num_written = mg_write(conn, buf, (
size_t)num_read))
9500 parse_range_header(
const char *header, int64_t *a, int64_t *b)
9502 return sscanf(header,
"bytes=%" INT64_FMT
"-%" INT64_FMT, a, b);
9507 construct_etag(
char *buf,
size_t buf_len,
const struct mg_file_stat *filestat)
9509 if ((filestat != NULL) && (buf != NULL)) {
9514 "\"%lx.%" INT64_FMT
"\"",
9515 (
unsigned long)filestat->last_modified,
9522 fclose_on_exec(
struct mg_file_access *filep,
struct mg_connection *conn)
9524 if (filep != NULL && filep->fp != NULL) {
9528 if (fcntl(fileno(filep->fp), F_SETFD, FD_CLOEXEC) != 0) {
9529 mg_cry_internal(conn,
9530 "%s: fcntl(F_SETFD FD_CLOEXEC) failed: %s",
9539 #if defined(USE_ZLIB)
9540 #include "mod_zlib.inl"
9545 handle_static_file_request(
struct mg_connection *conn,
9547 struct mg_file *filep,
9548 const char *mime_type,
9549 const char *additional_headers)
9551 char date[64], lm[64], etag[64];
9553 const char *msg =
"OK", *hdr;
9554 time_t curtime = time(NULL);
9556 struct vec mime_vec;
9558 char gz_path[PATH_MAX];
9559 const char *encoding =
"";
9560 const char *cors1, *cors2, *cors3;
9561 int is_head_request;
9563 #if defined(USE_ZLIB)
9567 int allow_on_the_fly_compression = 1;
9570 if ((conn == NULL) || (conn->dom_ctx == NULL) || (filep == NULL)) {
9574 is_head_request = !strcmp(conn->request_info.request_method,
"HEAD");
9576 if (mime_type == NULL) {
9577 get_mime_type(conn, path, &mime_vec);
9579 mime_vec.ptr = mime_type;
9580 mime_vec.len = strlen(mime_type);
9582 if (filep->stat.size > INT64_MAX) {
9583 mg_send_http_error(conn,
9585 "Error: File size is too large to send\n%" INT64_FMT,
9589 cl = (int64_t)filep->stat.size;
9590 conn->status_code = 200;
9593 #
if defined(USE_ZLIB)
9597 if (!conn->accept_gzip) {
9598 allow_on_the_fly_compression = 0;
9602 if (filep->stat.is_gzipped) {
9603 mg_snprintf(conn, &truncated, gz_path,
sizeof(gz_path),
"%s.gz", path);
9606 mg_send_http_error(conn,
9608 "Error: Path of zipped file too long (%s)",
9614 encoding =
"Content-Encoding: gzip\r\n";
9616 #if defined(USE_ZLIB)
9618 allow_on_the_fly_compression = 0;
9622 if (!mg_fopen(conn, path, MG_FOPEN_MODE_READ, filep)) {
9623 mg_send_http_error(conn,
9625 "Error: Cannot open file\nfopen(%s): %s",
9631 fclose_on_exec(&filep->access, conn);
9636 hdr = mg_get_header(conn,
"Range");
9637 if ((hdr != NULL) && ((n = parse_range_header(hdr, &r1, &r2)) > 0)
9638 && (r1 >= 0) && (r2 >= 0)) {
9641 if (filep->stat.is_gzipped) {
9646 "Error: Range requests in gzipped files are not supported");
9651 conn->status_code = 206;
9652 cl = (n == 2) ? (((r2 > cl) ? cl : r2) - r1 + 1) : (cl - r1);
9657 "Content-Range: bytes "
9658 "%" INT64_FMT
"-%" INT64_FMT
"/%" INT64_FMT
"\r\n",
9662 msg =
"Partial Content";
9664 #if defined(USE_ZLIB)
9666 allow_on_the_fly_compression = 0;
9672 #if defined(USE_ZLIB)
9673 if (filep->stat.size < MG_FILE_COMPRESSION_SIZE_LIMIT) {
9675 allow_on_the_fly_compression = 0;
9680 hdr = mg_get_header(conn,
"Origin");
9687 cors1 =
"Access-Control-Allow-Origin: ";
9688 cors2 = conn->dom_ctx->config[ACCESS_CONTROL_ALLOW_ORIGIN];
9691 cors1 = cors2 = cors3 =
"";
9697 gmt_time_string(date,
sizeof(date), &curtime);
9698 gmt_time_string(lm,
sizeof(lm), &filep->stat.last_modified);
9699 construct_etag(etag,
sizeof(etag), &filep->stat);
9702 (void)mg_printf(conn,
9703 "HTTP/1.1 %d %s\r\n"
9706 "Last-Modified: %s\r\n"
9708 "Content-Type: %.*s\r\n"
9709 "Connection: %s\r\n",
9720 suggest_connection_header(conn));
9721 send_static_cache_header(conn);
9722 send_additional_header(conn);
9724 #if defined(USE_ZLIB)
9726 if (allow_on_the_fly_compression) {
9729 (void)mg_printf(conn,
9730 "Content-Encoding: gzip\r\n"
9731 "Transfer-Encoding: chunked\r\n");
9738 (void)mg_printf(conn,
9739 "Content-Length: %" INT64_FMT
"\r\n"
9740 "Accept-Ranges: bytes\r\n"
9750 if (additional_headers != NULL) {
9751 (void)mg_printf(conn,
9753 (
int)strlen(additional_headers),
9754 additional_headers);
9756 (void)mg_printf(conn,
"\r\n");
9759 if (!is_head_request) {
9760 #if defined(USE_ZLIB)
9761 if (allow_on_the_fly_compression) {
9763 send_compressed_data(conn, filep);
9768 send_file_data(conn, filep, r1, cl);
9771 (void)mg_fclose(&filep->access);
9776 mg_send_file_body(
struct mg_connection *conn,
const char *path)
9778 struct mg_file file = STRUCT_FILE_INITIALIZER;
9779 if (!mg_fopen(conn, path, MG_FOPEN_MODE_READ, &file)) {
9782 fclose_on_exec(&file.access, conn);
9783 send_file_data(conn, &file, 0, INT64_MAX);
9784 (void)mg_fclose(&file.access);
9789 #if !defined(NO_CACHING)
9792 is_not_modified(
const struct mg_connection *conn,
9793 const struct mg_file_stat *filestat)
9796 const char *ims = mg_get_header(conn,
"If-Modified-Since");
9797 const char *inm = mg_get_header(conn,
"If-None-Match");
9798 construct_etag(etag,
sizeof(etag), filestat);
9800 return ((inm != NULL) && !mg_strcasecmp(etag, inm))
9802 && (filestat->last_modified <= parse_date_string(ims)));
9806 handle_not_modified_static_file_request(
struct mg_connection *conn,
9807 struct mg_file *filep)
9809 char date[64], lm[64], etag[64];
9810 time_t curtime = time(NULL);
9812 if ((conn == NULL) || (filep == NULL)) {
9815 conn->status_code = 304;
9816 gmt_time_string(date,
sizeof(date), &curtime);
9817 gmt_time_string(lm,
sizeof(lm), &filep->stat.last_modified);
9818 construct_etag(etag,
sizeof(etag), &filep->stat);
9820 (void)mg_printf(conn,
9821 "HTTP/1.1 %d %s\r\n"
9824 mg_get_response_code_text(conn, conn->status_code),
9826 send_static_cache_header(conn);
9827 send_additional_header(conn);
9828 (void)mg_printf(conn,
9829 "Last-Modified: %s\r\n"
9831 "Connection: %s\r\n"
9835 suggest_connection_header(conn));
9841 mg_send_file(
struct mg_connection *conn,
const char *path)
9843 mg_send_mime_file2(conn, path, NULL, NULL);
9848 mg_send_mime_file(
struct mg_connection *conn,
9850 const char *mime_type)
9852 mg_send_mime_file2(conn, path, mime_type, NULL);
9857 mg_send_mime_file2(
struct mg_connection *conn,
9859 const char *mime_type,
9860 const char *additional_headers)
9862 struct mg_file file = STRUCT_FILE_INITIALIZER;
9869 if (mg_stat(conn, path, &file.stat)) {
9870 #if !defined(NO_CACHING)
9871 if (is_not_modified(conn, &file.stat)) {
9873 handle_not_modified_static_file_request(conn, &file);
9876 if (file.stat.is_directory) {
9877 if (!mg_strcasecmp(conn->dom_ctx->config[ENABLE_DIRECTORY_LISTING],
9879 handle_directory_request(conn, path);
9881 mg_send_http_error(conn,
9884 "Error: Directory listing denied");
9887 handle_static_file_request(
9888 conn, path, &file, mime_type, additional_headers);
9891 mg_send_http_error(conn, 404,
"%s",
"Error: File not found");
9903 put_dir(
struct mg_connection *conn,
const char *path)
9907 struct mg_file file = STRUCT_FILE_INITIALIZER;
9911 for (s = p = path + 2; (p = strchr(s,
'/')) != NULL; s = ++p) {
9912 len = (size_t)(p - path);
9913 if (len >=
sizeof(buf)) {
9918 memcpy(buf, path, len);
9922 DEBUG_TRACE(
"mkdir(%s)", buf);
9923 if (!mg_stat(conn, buf, &file.stat) && mg_mkdir(conn, buf, 0755) != 0) {
9940 remove_bad_file(
const struct mg_connection *conn,
const char *path)
9942 int r = mg_remove(conn, path);
9944 mg_cry_internal(conn,
9945 "%s: Cannot remove invalid file %s",
9953 mg_store_body(
struct mg_connection *conn,
const char *path)
9955 char buf[MG_BUF_LEN];
9960 if (conn->consumed_content != 0) {
9961 mg_cry_internal(conn,
"%s: Contents already consumed", __func__);
9965 ret = put_dir(conn, path);
9976 if (mg_fopen(conn, path, MG_FOPEN_MODE_WRITE, &fi) == 0) {
9980 ret = mg_read(conn, buf,
sizeof(buf));
9982 n = (int)fwrite(buf, 1, (
size_t)ret, fi.access.fp);
9986 remove_bad_file(conn, path);
9990 ret = mg_read(conn, buf,
sizeof(buf));
9996 if (mg_fclose(&fi.access) != 0) {
9997 remove_bad_file(conn, path);
10010 skip_to_end_of_word_and_terminate(
char **ppw,
int eol)
10014 while (isgraph(**ppw)) {
10021 if ((**ppw !=
'\r') && (**ppw !=
'\n')) {
10026 if (**ppw !=
' ') {
10035 }
while ((**ppw) && isspace(**ppw));
10040 if (!isgraph(**ppw)) {
10055 parse_http_headers(
char **buf,
struct mg_header hdr[MG_MAX_HEADERS])
10058 int num_headers = 0;
10060 for (i = 0; i < (int)MG_MAX_HEADERS; i++) {
10062 while ((*dp !=
':') && (*dp >= 33) && (*dp <= 126)) {
10077 hdr[i].name = *buf;
10080 }
while (*dp ==
' ');
10084 *buf = dp + strcspn(dp,
"\r\n");
10085 if (((*buf)[0] !=
'\r') || ((*buf)[1] !=
'\n')) {
10089 num_headers = i + 1;
10099 if ((*buf)[0] ==
'\r') {
10104 return num_headers;
10108 struct mg_http_method_info {
10110 int request_has_body;
10111 int response_has_body;
10119 static struct mg_http_method_info http_methods[] = {
10121 {
"GET", 0, 1, 1, 1, 1},
10122 {
"POST", 1, 1, 0, 0, 0},
10123 {
"PUT", 1, 0, 0, 1, 0},
10124 {
"DELETE", 0, 0, 0, 1, 0},
10125 {
"HEAD", 0, 0, 1, 1, 1},
10126 {
"OPTIONS", 0, 0, 1, 1, 0},
10127 {
"CONNECT", 1, 1, 0, 0, 0},
10131 {
"PATCH", 1, 0, 0, 0, 0},
10135 {
"PROPFIND", 0, 1, 1, 1, 0},
10141 {
"MKCOL", 0, 0, 0, 1, 0},
10163 {
"REPORT", 1, 1, 1, 1, 1},
10170 {NULL, 0, 0, 0, 0, 0}
10175 static const struct mg_http_method_info *
10176 get_http_method_info(
const char *method)
10182 const struct mg_http_method_info *m = http_methods;
10185 if (!strcmp(m->name, method)) {
10195 is_valid_http_method(
const char *method)
10197 return (get_http_method_info(method) != NULL);
10211 parse_http_request(
char *buf,
int len,
struct mg_request_info *ri)
10213 int request_length;
10218 ri->remote_user = ri->request_method = ri->request_uri = ri->http_version =
10220 ri->num_headers = 0;
10225 while ((len > 0) && isspace(*(
unsigned char *)buf)) {
10237 if (iscntrl(*(
unsigned char *)buf)) {
10242 request_length = get_http_header_len(buf, len);
10243 if (request_length <= 0) {
10244 return request_length;
10246 buf[request_length - 1] =
'\0';
10248 if ((*buf == 0) || (*buf ==
'\r') || (*buf ==
'\n')) {
10253 ri->request_method = buf;
10255 if (skip_to_end_of_word_and_terminate(&buf, 0) <= 0) {
10260 if (!is_valid_http_method(ri->request_method)) {
10265 ri->request_uri = buf;
10267 if (skip_to_end_of_word_and_terminate(&buf, 0) <= 0) {
10272 ri->http_version = buf;
10274 if (skip_to_end_of_word_and_terminate(&buf, 1) <= 0) {
10279 if (strncmp(ri->http_version,
"HTTP/", 5) != 0) {
10283 ri->http_version += 5;
10287 ri->num_headers = parse_http_headers(&buf, ri->http_headers);
10288 if (ri->num_headers < 0) {
10293 return request_length + init_skip;
10298 parse_http_response(
char *buf,
int len,
struct mg_response_info *ri)
10300 int response_length;
10306 ri->http_version = ri->status_text = NULL;
10307 ri->num_headers = ri->status_code = 0;
10312 while ((len > 0) && isspace(*(
unsigned char *)buf)) {
10324 if (iscntrl(*(
unsigned char *)buf)) {
10329 response_length = get_http_header_len(buf, len);
10330 if (response_length <= 0) {
10331 return response_length;
10333 buf[response_length - 1] =
'\0';
10335 if ((*buf == 0) || (*buf ==
'\r') || (*buf ==
'\n')) {
10341 if (strncmp(buf,
"HTTP/", 5) != 0) {
10346 if (!isgraph(buf[0])) {
10350 ri->http_version = buf;
10352 if (skip_to_end_of_word_and_terminate(&buf, 0) <= 0) {
10359 if (skip_to_end_of_word_and_terminate(&buf, 0) <= 0) {
10363 l = strtol(tmp, &tmp2, 10);
10364 if ((l < 100) || (l >= 1000) || ((tmp2 - tmp) != 3) || (*tmp2 != 0)) {
10368 ri->status_code = (int)l;
10371 ri->status_text = buf;
10375 while (isprint(*buf)) {
10378 if ((*buf !=
'\r') && (*buf !=
'\n')) {
10385 }
while ((*buf) && isspace(*buf));
10389 ri->num_headers = parse_http_headers(&buf, ri->http_headers);
10390 if (ri->num_headers < 0) {
10395 return response_length + init_skip;
10405 read_message(FILE *fp,
10406 struct mg_connection *conn,
10411 int request_len, n = 0;
10412 struct timespec last_action_time;
10413 double request_timeout;
10419 memset(&last_action_time, 0,
sizeof(last_action_time));
10421 if (conn->dom_ctx->config[REQUEST_TIMEOUT]) {
10423 request_timeout = atof(conn->dom_ctx->config[REQUEST_TIMEOUT]) / 1000.0;
10425 request_timeout = -1.0;
10427 if (conn->handled_requests > 0) {
10428 if (conn->dom_ctx->config[KEEP_ALIVE_TIMEOUT]) {
10430 atof(conn->dom_ctx->config[KEEP_ALIVE_TIMEOUT]) / 1000.0;
10434 request_len = get_http_header_len(buf, *nread);
10437 clock_gettime(CLOCK_MONOTONIC, &last_action_time);
10439 while (request_len == 0) {
10441 if (conn->phys_ctx->stop_flag != 0) {
10446 if (*nread >= bufsiz) {
10452 fp, conn, buf + *nread, bufsiz - *nread, request_timeout);
10459 request_len = get_http_header_len(buf, *nread);
10464 if ((request_len == 0) && (request_timeout >= 0)) {
10465 if (mg_difftimespec(&last_action_time, &(conn->req_time))
10466 > request_timeout) {
10470 clock_gettime(CLOCK_MONOTONIC, &last_action_time);
10474 return request_len;
10478 #if !defined(NO_CGI) || !defined(NO_FILES)
10480 forward_body_data(
struct mg_connection *conn, FILE *fp, SOCKET sock, SSL *ssl)
10482 const char *expect, *body;
10483 char buf[MG_BUF_LEN];
10484 int to_read, nread, success = 0;
10485 int64_t buffered_len;
10486 double timeout = -1.0;
10491 if (conn->dom_ctx->config[REQUEST_TIMEOUT]) {
10492 timeout = atoi(conn->dom_ctx->config[REQUEST_TIMEOUT]) / 1000.0;
10495 expect = mg_get_header(conn,
"Expect");
10496 DEBUG_ASSERT(fp != NULL);
10498 mg_send_http_error(conn, 500,
"%s",
"Error: NULL File");
10502 if ((conn->content_len == -1) && (!conn->is_chunked)) {
10504 mg_send_http_error(conn,
10507 "Error: Client did not specify content length");
10508 }
else if ((expect != NULL)
10509 && (mg_strcasecmp(expect,
"100-continue") != 0)) {
10512 mg_send_http_error(conn,
10514 "Error: Can not fulfill expectation %s",
10517 if (expect != NULL) {
10518 (void)mg_printf(conn,
"%s",
"HTTP/1.1 100 Continue\r\n\r\n");
10519 conn->status_code = 100;
10521 conn->status_code = 200;
10524 buffered_len = (int64_t)(conn->data_len) - (int64_t)conn->request_len
10525 - conn->consumed_content;
10527 DEBUG_ASSERT(buffered_len >= 0);
10528 DEBUG_ASSERT(conn->consumed_content == 0);
10530 if ((buffered_len < 0) || (conn->consumed_content != 0)) {
10531 mg_send_http_error(conn, 500,
"%s",
"Error: Size mismatch");
10535 if (buffered_len > 0) {
10536 if ((int64_t)buffered_len > conn->content_len) {
10537 buffered_len = (int)conn->content_len;
10539 body = conn->buf + conn->request_len + conn->consumed_content;
10541 conn->phys_ctx, fp, sock, ssl, body, (int64_t)buffered_len);
10542 conn->consumed_content += buffered_len;
10546 while (conn->consumed_content < conn->content_len) {
10547 to_read =
sizeof(buf);
10548 if ((int64_t)to_read > conn->content_len - conn->consumed_content) {
10549 to_read = (int)(conn->content_len - conn->consumed_content);
10551 nread = pull_inner(NULL, conn, buf, to_read, timeout);
10557 if (push_all(conn->phys_ctx, fp, sock, ssl, buf, nread)
10562 conn->consumed_content += nread;
10565 if (conn->consumed_content == conn->content_len) {
10566 success = (nread >= 0);
10574 mg_send_http_error(conn, 500,
"%s",
"");
10583 #if defined(USE_TIMERS)
10585 #define TIMER_API static
10586 #include "timer.inl"
10591 #if !defined(NO_CGI)
10602 struct cgi_environment {
10603 struct mg_connection *conn;
10615 static void addenv(
struct cgi_environment *env,
10616 PRINTF_FORMAT_STRING(
const char *fmt),
10617 ...) PRINTF_ARGS(2, 3);
10622 addenv(struct cgi_environment *env, const
char *fmt, ...)
10630 space = (env->buflen - env->bufused);
10633 n = strlen(fmt) + 2 + 128;
10638 n = env->buflen + CGI_ENVIRONMENT_SIZE;
10639 added = (
char *)mg_realloc_ctx(env->buf, n, env->conn->phys_ctx);
10644 "%s: Cannot allocate memory for CGI variable [%s]",
10651 space = (env->buflen - env->bufused);
10655 added = env->buf + env->bufused;
10659 mg_vsnprintf(env->conn, &truncated, added, (
size_t)space, fmt, ap);
10668 }
while (truncated);
10671 n = strlen(added) + 1;
10675 space = (env->varlen - env->varused);
10677 mg_cry_internal(env->conn,
10678 "%s: Cannot register CGI variable [%s]",
10685 env->var[env->varused] = added;
10692 prepare_cgi_environment(
struct mg_connection *conn,
10694 struct cgi_environment *env)
10697 struct vec var_vec;
10698 char *p, src_addr[IP_ADDR_STR_LEN], http_var_name[128];
10699 int i, truncated, uri_len;
10701 if ((conn == NULL) || (prog == NULL) || (env == NULL)) {
10706 env->buflen = CGI_ENVIRONMENT_SIZE;
10708 env->buf = (
char *)mg_malloc_ctx(env->buflen, conn->phys_ctx);
10709 if (env->buf == NULL) {
10710 mg_cry_internal(conn,
10711 "%s: Not enough memory for environmental buffer",
10715 env->varlen = MAX_CGI_ENVIR_VARS;
10718 (
char **)mg_malloc_ctx(env->buflen *
sizeof(
char *), conn->phys_ctx);
10719 if (env->var == NULL) {
10720 mg_cry_internal(conn,
10721 "%s: Not enough memory for environmental variables",
10727 addenv(env,
"SERVER_NAME=%s", conn->dom_ctx->config[AUTHENTICATION_DOMAIN]);
10728 addenv(env,
"SERVER_ROOT=%s", conn->dom_ctx->config[DOCUMENT_ROOT]);
10729 addenv(env,
"DOCUMENT_ROOT=%s", conn->dom_ctx->config[DOCUMENT_ROOT]);
10730 addenv(env,
"SERVER_SOFTWARE=CivetWeb/%s", mg_version());
10733 addenv(env,
"%s",
"GATEWAY_INTERFACE=CGI/1.1");
10734 addenv(env,
"%s",
"SERVER_PROTOCOL=HTTP/1.1");
10735 addenv(env,
"%s",
"REDIRECT_STATUS=200");
10737 #if defined(USE_IPV6)
10738 if (conn->client.lsa.sa.sa_family == AF_INET6) {
10739 addenv(env,
"SERVER_PORT=%d", ntohs(conn->client.lsa.sin6.sin6_port));
10743 addenv(env,
"SERVER_PORT=%d", ntohs(conn->client.lsa.sin.sin_port));
10746 sockaddr_to_string(src_addr,
sizeof(src_addr), &conn->client.rsa);
10747 addenv(env,
"REMOTE_ADDR=%s", src_addr);
10749 addenv(env,
"REQUEST_METHOD=%s", conn->request_info.request_method);
10750 addenv(env,
"REMOTE_PORT=%d", conn->request_info.remote_port);
10752 addenv(env,
"REQUEST_URI=%s", conn->request_info.request_uri);
10753 addenv(env,
"LOCAL_URI=%s", conn->request_info.local_uri);
10756 uri_len = (int)strlen(conn->request_info.local_uri);
10757 if (conn->path_info == NULL) {
10758 if (conn->request_info.local_uri[uri_len - 1] !=
'/') {
10760 addenv(env,
"SCRIPT_NAME=%s", conn->request_info.local_uri);
10763 const char *index_file = strrchr(prog,
'/');
10766 "SCRIPT_NAME=%s%s",
10767 conn->request_info.local_uri,
10774 "SCRIPT_NAME=%.*s",
10775 uri_len - (
int)strlen(conn->path_info),
10776 conn->request_info.local_uri);
10779 addenv(env,
"SCRIPT_FILENAME=%s", prog);
10780 if (conn->path_info == NULL) {
10781 addenv(env,
"PATH_TRANSLATED=%s", conn->dom_ctx->config[DOCUMENT_ROOT]);
10784 "PATH_TRANSLATED=%s%s",
10785 conn->dom_ctx->config[DOCUMENT_ROOT],
10789 addenv(env,
"HTTPS=%s", (conn->ssl == NULL) ?
"off" :
"on");
10791 if ((s = mg_get_header(conn,
"Content-Type")) != NULL) {
10792 addenv(env,
"CONTENT_TYPE=%s", s);
10794 if (conn->request_info.query_string != NULL) {
10795 addenv(env,
"QUERY_STRING=%s", conn->request_info.query_string);
10797 if ((s = mg_get_header(conn,
"Content-Length")) != NULL) {
10798 addenv(env,
"CONTENT_LENGTH=%s", s);
10800 if ((s = getenv(
"PATH")) != NULL) {
10801 addenv(env,
"PATH=%s", s);
10803 if (conn->path_info != NULL) {
10804 addenv(env,
"PATH_INFO=%s", conn->path_info);
10807 if (conn->status_code > 0) {
10809 addenv(env,
"STATUS=%d", conn->status_code);
10812 #if defined(_WIN32)
10813 if ((s = getenv(
"COMSPEC")) != NULL) {
10814 addenv(env,
"COMSPEC=%s", s);
10816 if ((s = getenv(
"SYSTEMROOT")) != NULL) {
10817 addenv(env,
"SYSTEMROOT=%s", s);
10819 if ((s = getenv(
"SystemDrive")) != NULL) {
10820 addenv(env,
"SystemDrive=%s", s);
10822 if ((s = getenv(
"ProgramFiles")) != NULL) {
10823 addenv(env,
"ProgramFiles=%s", s);
10825 if ((s = getenv(
"ProgramFiles(x86)")) != NULL) {
10826 addenv(env,
"ProgramFiles(x86)=%s", s);
10829 if ((s = getenv(
"LD_LIBRARY_PATH")) != NULL) {
10830 addenv(env,
"LD_LIBRARY_PATH=%s", s);
10834 if ((s = getenv(
"PERLLIB")) != NULL) {
10835 addenv(env,
"PERLLIB=%s", s);
10838 if (conn->request_info.remote_user != NULL) {
10839 addenv(env,
"REMOTE_USER=%s", conn->request_info.remote_user);
10840 addenv(env,
"%s",
"AUTH_TYPE=Digest");
10844 for (i = 0; i < conn->request_info.num_headers; i++) {
10846 (void)mg_snprintf(conn,
10849 sizeof(http_var_name),
10851 conn->request_info.http_headers[i].name);
10854 mg_cry_internal(conn,
10855 "%s: HTTP header variable too long [%s]",
10857 conn->request_info.http_headers[i].name);
10862 for (p = http_var_name; *p !=
'\0'; p++) {
10866 *p = (char)toupper(*(
unsigned char *)p);
10872 conn->request_info.http_headers[i].value);
10876 s = conn->dom_ctx->config[CGI_ENVIRONMENT];
10877 while ((s = next_option(s, &var_vec, NULL)) != NULL) {
10878 addenv(env,
"%.*s", (
int)var_vec.len, var_vec.ptr);
10881 env->var[env->varused] = NULL;
10882 env->buf[env->bufused] =
'\0';
10889 struct process_control_data {
10895 abort_process(
void *data)
10900 struct process_control_data *proc = (
struct process_control_data *)data;
10905 ret_pid = waitpid(proc->pid, &status, WNOHANG);
10906 if ((ret_pid != (pid_t)-1) && (status == 0)) {
10908 DEBUG_TRACE(
"CGI timer: Stop child process %p\n", proc->pid);
10909 kill(proc->pid, SIGABRT);
10912 while (waitpid(proc->pid, &status, 0) != (pid_t)-1)
10915 DEBUG_TRACE(
"CGI timer: Child process %p already stopped\n", proc->pid);
10918 refs = mg_atomic_dec(&proc->references);
10929 handle_cgi_request(
struct mg_connection *conn,
const char *prog)
10933 int headers_len, data_len, i, truncated;
10934 int fdin[2] = {-1, -1}, fdout[2] = {-1, -1}, fderr[2] = {-1, -1};
10935 const char *status, *status_text, *connection_state;
10936 char *pbuf, dir[PATH_MAX], *p;
10937 struct mg_request_info ri;
10938 struct cgi_environment blk;
10939 FILE *in = NULL, *out = NULL, *err = NULL;
10940 struct mg_file fout = STRUCT_FILE_INITIALIZER;
10941 pid_t pid = (pid_t)-1;
10942 struct process_control_data *proc = NULL;
10944 #if defined(USE_TIMERS)
10945 double cgi_timeout = -1.0;
10946 if (conn->dom_ctx->config[CGI_TIMEOUT]) {
10948 cgi_timeout = atof(conn->dom_ctx->config[CGI_TIMEOUT]) * 0.001;
10952 if (conn == NULL) {
10957 buflen = conn->phys_ctx->max_request_size;
10958 i = prepare_cgi_environment(conn, prog, &blk);
10968 (void)mg_snprintf(conn, &truncated, dir,
sizeof(dir),
"%s", prog);
10971 mg_cry_internal(conn,
"Error: CGI program \"%s\": Path too long", prog);
10972 mg_send_http_error(conn, 500,
"Error: %s",
"CGI path too long");
10976 if ((p = strrchr(dir,
'/')) != NULL) {
10984 if ((pipe(fdin) != 0) || (pipe(fdout) != 0) || (pipe(fderr) != 0)) {
10985 status = strerror(ERRNO);
10988 "Error: CGI program \"%s\": Can not create CGI pipes: %s",
10991 mg_send_http_error(conn,
10993 "Error: Cannot create CGI pipe: %s",
10998 proc = (
struct process_control_data *)
10999 mg_malloc_ctx(
sizeof(
struct process_control_data), conn->phys_ctx);
11000 if (proc == NULL) {
11001 mg_cry_internal(conn,
"Error: CGI program \"%s\": Out or memory", prog);
11002 mg_send_http_error(conn, 500,
"Error: Out of memory [%s]", prog);
11006 DEBUG_TRACE(
"CGI: spawn %s %s\n", dir, p);
11007 pid = spawn_process(conn, p, blk.buf, blk.var, fdin, fdout, fderr, dir);
11009 if (pid == (pid_t)-1) {
11010 status = strerror(ERRNO);
11013 "Error: CGI program \"%s\": Can not spawn CGI process: %s",
11016 mg_send_http_error(conn,
11018 "Error: Cannot spawn CGI process [%s]: %s",
11028 proc->references = 1;
11030 #if defined(USE_TIMERS)
11031 if (cgi_timeout > 0.0) {
11032 proc->references = 2;
11035 timer_add(conn->phys_ctx,
11045 set_close_on_exec((SOCKET)fdin[0], conn);
11046 set_close_on_exec((SOCKET)fdin[1], conn);
11047 set_close_on_exec((SOCKET)fdout[0], conn);
11048 set_close_on_exec((SOCKET)fdout[1], conn);
11049 set_close_on_exec((SOCKET)fderr[0], conn);
11050 set_close_on_exec((SOCKET)fderr[1], conn);
11056 (void)close(fdin[0]);
11057 (void)close(fdout[1]);
11058 (void)close(fderr[1]);
11059 fdin[0] = fdout[1] = fderr[1] = -1;
11061 if ((in = fdopen(fdin[1],
"wb")) == NULL) {
11062 status = strerror(ERRNO);
11063 mg_cry_internal(conn,
11064 "Error: CGI program \"%s\": Can not open stdin: %s",
11067 mg_send_http_error(conn,
11069 "Error: CGI can not open fdin\nfopen: %s",
11074 if ((out = fdopen(fdout[0],
"rb")) == NULL) {
11075 status = strerror(ERRNO);
11076 mg_cry_internal(conn,
11077 "Error: CGI program \"%s\": Can not open stdout: %s",
11080 mg_send_http_error(conn,
11082 "Error: CGI can not open fdout\nfopen: %s",
11087 if ((err = fdopen(fderr[0],
"rb")) == NULL) {
11088 status = strerror(ERRNO);
11089 mg_cry_internal(conn,
11090 "Error: CGI program \"%s\": Can not open stderr: %s",
11093 mg_send_http_error(conn,
11095 "Error: CGI can not open fderr\nfopen: %s",
11103 fout.access.fp = out;
11105 if ((conn->request_info.content_length != 0) || (conn->is_chunked)) {
11106 DEBUG_TRACE(
"CGI: send body data (%lli)\n",
11107 (
signed long long)conn->request_info.content_length);
11110 if (!forward_body_data(conn, in, INVALID_SOCKET, NULL)) {
11114 "Error: CGI program \"%s\": Forward body data failed",
11130 buf = (
char *)mg_malloc_ctx(buflen, conn->phys_ctx);
11132 mg_send_http_error(conn,
11134 "Error: Not enough memory for CGI buffer (%u bytes)",
11135 (
unsigned int)buflen);
11138 "Error: CGI program \"%s\": Not enough memory for buffer (%u "
11141 (
unsigned int)buflen);
11145 DEBUG_TRACE(
"CGI: %s",
"wait for response");
11146 headers_len = read_message(out, conn, buf, (
int)buflen, &data_len);
11147 DEBUG_TRACE(
"CGI: response: %li", (
signed long)headers_len);
11149 if (headers_len <= 0) {
11153 i = pull_all(err, conn, buf, (
int)buflen);
11157 mg_cry_internal(conn,
11158 "Error: CGI program \"%s\" sent error "
11164 mg_send_http_error(conn,
11166 "Error: CGI program \"%s\" failed.",
11171 mg_cry_internal(conn,
11172 "Error: CGI program sent malformed or too big "
11173 "(>%u bytes) HTTP headers: [%.*s]",
11178 mg_send_http_error(conn,
11180 "Error: CGI program sent malformed or too big "
11181 "(>%u bytes) HTTP headers: [%.*s]",
11192 buf[headers_len - 1] =
'\0';
11193 ri.num_headers = parse_http_headers(&pbuf, ri.http_headers);
11196 status_text =
"OK";
11197 if ((status = get_header(ri.http_headers, ri.num_headers,
"Status"))
11199 conn->status_code = atoi(status);
11200 status_text = status;
11201 while (isdigit(*(
const unsigned char *)status_text)
11202 || *status_text ==
' ') {
11205 }
else if (get_header(ri.http_headers, ri.num_headers,
"Location")
11207 conn->status_code = 307;
11209 conn->status_code = 200;
11212 get_header(ri.http_headers, ri.num_headers,
"Connection");
11213 if (!header_has_option(connection_state,
"keep-alive")) {
11214 conn->must_close = 1;
11217 DEBUG_TRACE(
"CGI: response %u %s", conn->status_code, status_text);
11219 (void)mg_printf(conn,
"HTTP/1.1 %d %s\r\n", conn->status_code, status_text);
11222 for (i = 0; i < ri.num_headers; i++) {
11225 ri.http_headers[i].name,
11226 ri.http_headers[i].value);
11228 mg_write(conn,
"\r\n", 2);
11231 mg_write(conn, buf + headers_len, (
size_t)(data_len - headers_len));
11234 DEBUG_TRACE(
"CGI: %s",
"forward all data");
11235 send_file_data(conn, &fout, 0, INT64_MAX);
11236 DEBUG_TRACE(
"CGI: %s",
"all data sent");
11242 if (pid != (pid_t)-1) {
11243 abort_process((
void *)proc);
11246 if (fdin[0] != -1) {
11249 if (fdout[1] != -1) {
11255 }
else if (fdin[1] != -1) {
11261 }
else if (fdout[0] != -1) {
11267 }
else if (fderr[0] != -1) {
11278 #if !defined(NO_FILES)
11280 mkcol(
struct mg_connection *conn,
const char *path)
11285 time_t curtime = time(NULL);
11287 if (conn == NULL) {
11294 memset(&de.file, 0,
sizeof(de.file));
11295 if (!mg_stat(conn, path, &de.file)) {
11296 mg_cry_internal(conn,
11297 "%s: mg_stat(%s) failed: %s",
11303 if (de.file.last_modified) {
11307 mg_send_http_error(
11308 conn, 405,
"Error: mkcol(%s): %s", path, strerror(ERRNO));
11312 body_len = conn->data_len - conn->request_len;
11313 if (body_len > 0) {
11314 mg_send_http_error(
11315 conn, 415,
"Error: mkcol(%s): %s", path, strerror(ERRNO));
11319 rc = mg_mkdir(conn, path, 0755);
11322 conn->status_code = 201;
11323 gmt_time_string(date,
sizeof(date), &curtime);
11325 "HTTP/1.1 %d Created\r\n"
11329 send_static_cache_header(conn);
11330 send_additional_header(conn);
11332 "Content-Length: 0\r\n"
11333 "Connection: %s\r\n\r\n",
11334 suggest_connection_header(conn));
11336 if (errno == EEXIST) {
11337 mg_send_http_error(
11338 conn, 405,
"Error: mkcol(%s): %s", path, strerror(ERRNO));
11339 }
else if (errno == EACCES) {
11340 mg_send_http_error(
11341 conn, 403,
"Error: mkcol(%s): %s", path, strerror(ERRNO));
11342 }
else if (errno == ENOENT) {
11343 mg_send_http_error(
11344 conn, 409,
"Error: mkcol(%s): %s", path, strerror(ERRNO));
11346 mg_send_http_error(
11347 conn, 500,
"fopen(%s): %s", path, strerror(ERRNO));
11354 put_file(
struct mg_connection *conn,
const char *path)
11356 struct mg_file file = STRUCT_FILE_INITIALIZER;
11361 time_t curtime = time(NULL);
11363 if (conn == NULL) {
11367 if (mg_stat(conn, path, &file.stat)) {
11369 conn->status_code = 200;
11371 if (file.stat.is_directory) {
11380 #if defined(MG_USE_OPEN_FILE)
11381 if (file.access.membuf != NULL) {
11383 mg_send_http_error(conn,
11385 "Error: Put not possible\nReplacing %s "
11386 "is not supported",
11393 if (access(path, W_OK) == 0) {
11395 conn->status_code = 200;
11398 mg_send_http_error(
11401 "Error: Put not possible\nReplacing %s is not allowed",
11408 conn->status_code = 201;
11409 rc = put_dir(conn, path);
11414 gmt_time_string(date,
sizeof(date), &curtime);
11416 "HTTP/1.1 %d %s\r\n",
11418 mg_get_response_code_text(NULL, conn->status_code));
11419 send_no_cache_header(conn);
11420 send_additional_header(conn);
11423 "Content-Length: 0\r\n"
11424 "Connection: %s\r\n\r\n",
11426 suggest_connection_header(conn));
11435 mg_send_http_error(conn,
11437 "Error: Path too long\nput_dir(%s): %s",
11445 mg_send_http_error(conn,
11447 "Error: Can not create directory\nput_dir(%s): %s",
11455 if (!mg_fopen(conn, path, MG_FOPEN_MODE_WRITE, &file)
11456 || file.access.fp == NULL) {
11457 (void)mg_fclose(&file.access);
11458 mg_send_http_error(conn,
11460 "Error: Can not create file\nfopen(%s): %s",
11466 fclose_on_exec(&file.access, conn);
11467 range = mg_get_header(conn,
"Content-Range");
11469 if ((range != NULL) && parse_range_header(range, &r1, &r2) > 0) {
11470 conn->status_code = 206;
11471 fseeko(file.access.fp, r1, SEEK_SET);
11474 if (!forward_body_data(conn, file.access.fp, INVALID_SOCKET, NULL)) {
11478 (void)mg_fclose(&file.access);
11482 if (mg_fclose(&file.access) != 0) {
11485 conn->status_code = 507;
11488 gmt_time_string(date,
sizeof(date), &curtime);
11490 "HTTP/1.1 %d %s\r\n",
11492 mg_get_response_code_text(NULL, conn->status_code));
11493 send_no_cache_header(conn);
11494 send_additional_header(conn);
11497 "Content-Length: 0\r\n"
11498 "Connection: %s\r\n\r\n",
11500 suggest_connection_header(conn));
11505 delete_file(
struct mg_connection *conn,
const char *path)
11508 memset(&de.file, 0,
sizeof(de.file));
11509 if (!mg_stat(conn, path, &de.file)) {
11511 mg_send_http_error(conn,
11513 "Error: Cannot delete file\nFile %s not found",
11519 if (de.access.membuf != NULL) {
11521 mg_send_http_error(
11524 "Error: Delete not possible\nDeleting %s is not supported",
11530 if (de.file.is_directory) {
11531 if (remove_directory(conn, path)) {
11533 mg_send_http_error(conn, 204,
"%s",
"");
11536 mg_send_http_error(conn, 500,
"Error: Could not delete %s", path);
11543 if (access(path, W_OK) != 0) {
11545 mg_send_http_error(
11548 "Error: Delete not possible\nDeleting %s is not allowed",
11554 if (mg_remove(conn, path) == 0) {
11556 mg_send_http_error(conn, 204,
"%s",
"");
11559 mg_send_http_error(conn,
11561 "Error: Cannot delete file\nremove(%s): %s",
11570 send_ssi_file(
struct mg_connection *,
const char *,
struct mg_file *,
int);
11574 do_ssi_include(
struct mg_connection *conn,
11579 char file_name[MG_BUF_LEN], path[512], *p;
11580 struct mg_file file = STRUCT_FILE_INITIALIZER;
11584 if (conn == NULL) {
11591 if (sscanf(tag,
" virtual=\"%511[^\"]\"", file_name) == 1) {
11593 file_name[511] = 0;
11594 (void)mg_snprintf(conn,
11599 conn->dom_ctx->config[DOCUMENT_ROOT],
11602 }
else if (sscanf(tag,
" abspath=\"%511[^\"]\"", file_name) == 1) {
11605 file_name[511] = 0;
11607 mg_snprintf(conn, &truncated, path,
sizeof(path),
"%s", file_name);
11609 }
else if ((sscanf(tag,
" file=\"%511[^\"]\"", file_name) == 1)
11610 || (sscanf(tag,
" \"%511[^\"]\"", file_name) == 1)) {
11612 file_name[511] = 0;
11613 (void)mg_snprintf(conn, &truncated, path,
sizeof(path),
"%s", ssi);
11616 if ((p = strrchr(path,
'/')) != NULL) {
11619 len = strlen(path);
11620 (void)mg_snprintf(conn,
11623 sizeof(path) - len,
11629 mg_cry_internal(conn,
"Bad SSI #include: [%s]", tag);
11634 mg_cry_internal(conn,
"SSI #include path length overflow: [%s]", tag);
11638 if (!mg_fopen(conn, path, MG_FOPEN_MODE_READ, &file)) {
11639 mg_cry_internal(conn,
11640 "Cannot open SSI #include: [%s]: fopen(%s): %s",
11645 fclose_on_exec(&file.access, conn);
11646 if (match_prefix(conn->dom_ctx->config[SSI_EXTENSIONS],
11647 strlen(conn->dom_ctx->config[SSI_EXTENSIONS]),
11650 send_ssi_file(conn, path, &file, include_level + 1);
11652 send_file_data(conn, &file, 0, INT64_MAX);
11654 (void)mg_fclose(&file.access);
11659 #if !defined(NO_POPEN)
11661 do_ssi_exec(
struct mg_connection *conn,
char *tag)
11663 char cmd[1024] =
"";
11664 struct mg_file file = STRUCT_FILE_INITIALIZER;
11666 if (sscanf(tag,
" \"%1023[^\"]\"", cmd) != 1) {
11667 mg_cry_internal(conn,
"Bad SSI #exec: [%s]", tag);
11670 if ((file.access.fp = popen(cmd,
"r")) == NULL) {
11671 mg_cry_internal(conn,
11672 "Cannot SSI #exec: [%s]: %s",
11676 send_file_data(conn, &file, 0, INT64_MAX);
11677 pclose(file.access.fp);
11685 mg_fgetc(
struct mg_file *filep,
int offset)
11689 if (filep == NULL) {
11692 #if defined(MG_USE_OPEN_FILE)
11693 if ((filep->access.membuf != NULL) && (offset >= 0)
11694 && (((
unsigned int)(offset)) < filep->stat.size)) {
11695 return ((
const unsigned char *)filep->access.membuf)[offset];
11698 if (filep->access.fp != NULL) {
11699 return fgetc(filep->access.fp);
11707 send_ssi_file(
struct mg_connection *conn,
11709 struct mg_file *filep,
11712 char buf[MG_BUF_LEN];
11713 int ch, offset, len, in_tag, in_ssi_tag;
11715 if (include_level > 10) {
11716 mg_cry_internal(conn,
"SSI #include level is too deep (%s)", path);
11720 in_tag = in_ssi_tag = len = offset = 0;
11723 while ((ch = mg_fgetc(filep, offset++)) != EOF) {
11736 if ((len > 12) && !memcmp(buf + 5,
"include", 7)) {
11737 do_ssi_include(conn, path, buf + 12, include_level + 1);
11738 #if !defined(NO_POPEN)
11739 }
else if ((len > 9) && !memcmp(buf + 5,
"exec", 4)) {
11740 do_ssi_exec(conn, buf + 9);
11743 mg_cry_internal(conn,
11750 in_ssi_tag = in_tag = 0;
11755 (void)mg_write(conn, buf, (
size_t)len);
11762 buf[len++] = (char)(ch & 0xff);
11764 if ((len == 5) && !memcmp(buf,
"<!--#", 5)) {
11769 if ((len + 2) > (int)
sizeof(buf)) {
11771 mg_cry_internal(conn,
"%s: tag is too large", path);
11786 (void)mg_write(conn, buf, (
size_t)len);
11795 buf[len++] = (char)(ch & 0xff);
11797 if (len == (
int)
sizeof(buf)) {
11798 mg_write(conn, buf, (
size_t)len);
11807 mg_write(conn, buf, (
size_t)len);
11813 handle_ssi_file_request(
struct mg_connection *conn,
11815 struct mg_file *filep)
11818 time_t curtime = time(NULL);
11819 const char *cors1, *cors2, *cors3;
11821 if ((conn == NULL) || (path == NULL) || (filep == NULL)) {
11825 if (mg_get_header(conn,
"Origin")) {
11827 cors1 =
"Access-Control-Allow-Origin: ";
11828 cors2 = conn->dom_ctx->config[ACCESS_CONTROL_ALLOW_ORIGIN];
11831 cors1 = cors2 = cors3 =
"";
11834 if (!mg_fopen(conn, path, MG_FOPEN_MODE_READ, filep)) {
11837 mg_send_http_error(conn,
11839 "Error: Cannot read file\nfopen(%s): %s",
11843 conn->must_close = 1;
11844 gmt_time_string(date,
sizeof(date), &curtime);
11845 fclose_on_exec(&filep->access, conn);
11846 mg_printf(conn,
"HTTP/1.1 200 OK\r\n");
11847 send_no_cache_header(conn);
11848 send_additional_header(conn);
11852 "Content-Type: text/html\r\n"
11853 "Connection: %s\r\n\r\n",
11858 suggest_connection_header(conn));
11859 send_ssi_file(conn, path, filep, 0);
11860 (void)mg_fclose(&filep->access);
11865 #if !defined(NO_FILES)
11867 send_options(
struct mg_connection *conn)
11870 time_t curtime = time(NULL);
11876 conn->status_code = 200;
11877 conn->must_close = 1;
11878 gmt_time_string(date,
sizeof(date), &curtime);
11884 "HTTP/1.1 200 OK\r\n"
11886 "Connection: %s\r\n"
11887 "Allow: GET, POST, HEAD, CONNECT, PUT, DELETE, OPTIONS, "
11888 "PROPFIND, MKCOL\r\n"
11891 suggest_connection_header(conn));
11892 send_additional_header(conn);
11893 mg_printf(conn,
"\r\n");
11899 print_props(
struct mg_connection *conn,
11901 struct mg_file_stat *filep)
11905 if ((conn == NULL) || (uri == NULL) || (filep == NULL)) {
11909 gmt_time_string(mtime,
sizeof(mtime), &filep->last_modified);
11912 "<d:href>%s</d:href>"
11915 "<d:resourcetype>%s</d:resourcetype>"
11916 "<d:getcontentlength>%" INT64_FMT
"</d:getcontentlength>"
11917 "<d:getlastmodified>%s</d:getlastmodified>"
11919 "<d:status>HTTP/1.1 200 OK</d:status>"
11923 filep->is_directory ?
"<d:collection/>" :
"",
11930 print_dav_dir_entry(
struct de *de,
void *data)
11932 char href[PATH_MAX];
11935 struct mg_connection *conn = (
struct mg_connection *)data;
11936 if (!de || !conn) {
11944 conn->request_info.local_uri,
11948 size_t href_encoded_size;
11949 char *href_encoded;
11951 href_encoded_size = PATH_MAX * 3;
11952 href_encoded = (
char *)mg_malloc(href_encoded_size);
11953 if (href_encoded == NULL) {
11956 mg_url_encode(href, href_encoded, href_encoded_size);
11957 print_props(conn, href_encoded, &de->file);
11958 mg_free(href_encoded);
11966 handle_propfind(
struct mg_connection *conn,
11968 struct mg_file_stat *filep)
11970 const char *depth = mg_get_header(conn,
"Depth");
11972 time_t curtime = time(NULL);
11974 gmt_time_string(date,
sizeof(date), &curtime);
11976 if (!conn || !path || !filep || !conn->dom_ctx) {
11980 conn->must_close = 1;
11981 conn->status_code = 207;
11983 "HTTP/1.1 207 Multi-Status\r\n"
11986 send_static_cache_header(conn);
11987 send_additional_header(conn);
11989 "Connection: %s\r\n"
11990 "Content-Type: text/xml; charset=utf-8\r\n\r\n",
11991 suggest_connection_header(conn));
11994 "<?xml version=\"1.0\" encoding=\"utf-8\"?>"
11995 "<d:multistatus xmlns:d='DAV:'>\n");
11998 print_props(conn, conn->request_info.local_uri, filep);
12002 if (filep->is_directory
12003 && !mg_strcasecmp(conn->dom_ctx->config[ENABLE_DIRECTORY_LISTING],
12005 && ((depth == NULL) || (strcmp(depth,
"0") != 0))) {
12006 scan_directory(conn, path, conn, &print_dav_dir_entry);
12009 mg_printf(conn,
"%s\n",
"</d:multistatus>");
12014 mg_lock_connection(
struct mg_connection *conn)
12017 (void)pthread_mutex_lock(&conn->mutex);
12022 mg_unlock_connection(
struct mg_connection *conn)
12025 (void)pthread_mutex_unlock(&conn->mutex);
12030 mg_lock_context(
struct mg_context *ctx)
12033 (void)pthread_mutex_lock(&ctx->nonce_mutex);
12038 mg_unlock_context(
struct mg_context *ctx)
12041 (void)pthread_mutex_unlock(&ctx->nonce_mutex);
12046 #if defined(USE_LUA)
12047 #include "mod_lua.inl"
12050 #if defined(USE_DUKTAPE)
12051 #include "mod_duktape.inl"
12054 #if defined(USE_WEBSOCKET)
12056 #if !defined(NO_SSL_DL)
12057 #define SHA_API static
12062 send_websocket_handshake(
struct mg_connection *conn,
const char *websock_key)
12064 static const char *magic =
"258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
12065 char buf[100], sha[20], b64_sha[
sizeof(sha) * 2];
12070 mg_snprintf(conn, &truncated, buf,
sizeof(buf),
"%s%s", websock_key, magic);
12072 conn->must_close = 1;
12076 DEBUG_TRACE(
"%s",
"Send websocket handshake");
12078 SHA1_Init(&sha_ctx);
12079 SHA1_Update(&sha_ctx, (
unsigned char *)buf, (uint32_t)strlen(buf));
12080 SHA1_Final((
unsigned char *)sha, &sha_ctx);
12081 base64_encode((
unsigned char *)sha,
sizeof(sha), b64_sha);
12083 "HTTP/1.1 101 Switching Protocols\r\n"
12084 "Upgrade: websocket\r\n"
12085 "Connection: Upgrade\r\n"
12086 "Sec-WebSocket-Accept: %s\r\n",
12088 if (conn->request_info.acceptedWebSocketSubprotocol) {
12090 "Sec-WebSocket-Protocol: %s\r\n\r\n",
12091 conn->request_info.acceptedWebSocketSubprotocol);
12093 mg_printf(conn,
"%s",
"\r\n");
12100 #if !defined(MG_MAX_UNANSWERED_PING)
12106 #define MG_MAX_UNANSWERED_PING (5)
12111 read_websocket(
struct mg_connection *conn,
12112 mg_websocket_data_handler ws_data_handler,
12113 void *callback_data)
12119 unsigned char *buf = (
unsigned char *)conn->buf + conn->request_len;
12120 int n, error, exit_by_callback;
12127 size_t i, len, mask_len = 0, header_len, body_len;
12128 uint64_t data_len = 0;
12133 unsigned char mask[4];
12138 unsigned char mem[4096];
12143 double timeout = -1.0;
12144 int enable_ping_pong = 0;
12145 int ping_count = 0;
12147 if (conn->dom_ctx->config[ENABLE_WEBSOCKET_PING_PONG]) {
12149 !mg_strcasecmp(conn->dom_ctx->config[ENABLE_WEBSOCKET_PING_PONG],
12153 if (conn->dom_ctx->config[WEBSOCKET_TIMEOUT]) {
12154 timeout = atoi(conn->dom_ctx->config[WEBSOCKET_TIMEOUT]) / 1000.0;
12156 if ((timeout <= 0.0) && (conn->dom_ctx->config[REQUEST_TIMEOUT])) {
12157 timeout = atoi(conn->dom_ctx->config[REQUEST_TIMEOUT]) / 1000.0;
12161 DEBUG_TRACE(
"Websocket connection %s:%u start data processing loop",
12162 conn->request_info.remote_addr,
12163 conn->request_info.remote_port);
12164 conn->in_websocket_handling = 1;
12165 mg_set_thread_name(
"wsock");
12169 while (!conn->phys_ctx->stop_flag && !conn->must_close) {
12171 DEBUG_ASSERT(conn->data_len >= conn->request_len);
12172 if ((body_len = (
size_t)(conn->data_len - conn->request_len)) >= 2) {
12173 len = buf[1] & 127;
12174 mask_len = (buf[1] & 128) ? 4 : 0;
12175 if ((len < 126) && (body_len >= mask_len)) {
12178 header_len = 2 + mask_len;
12179 }
else if ((len == 126) && (body_len >= (4 + mask_len))) {
12181 header_len = 4 + mask_len;
12182 data_len = ((((size_t)buf[2]) << 8) + buf[3]);
12183 }
else if (body_len >= (10 + mask_len)) {
12186 memcpy(&l1, &buf[2], 4);
12187 memcpy(&l2, &buf[6], 4);
12188 header_len = 10 + mask_len;
12189 data_len = (((uint64_t)ntohl(l1)) << 32) + ntohl(l2);
12191 if (data_len > (uint64_t)0x7FFF0000ul) {
12196 "websocket out of memory; closing connection");
12202 if ((header_len > 0) && (body_len >= header_len)) {
12204 unsigned char *data = mem;
12206 if ((
size_t)data_len > (
size_t)
sizeof(mem)) {
12207 data = (
unsigned char *)mg_malloc_ctx((
size_t)data_len,
12209 if (data == NULL) {
12215 "websocket out of memory; closing connection");
12221 if (mask_len > 0) {
12222 memcpy(mask, buf + header_len - mask_len,
sizeof(mask));
12224 memset(mask, 0,
sizeof(mask));
12229 DEBUG_ASSERT(body_len >= header_len);
12230 if (data_len + (uint64_t)header_len > (uint64_t)body_len) {
12233 len = body_len - header_len;
12234 memcpy(data, buf + header_len, len);
12236 while ((uint64_t)len < data_len) {
12237 n = pull_inner(NULL,
12239 (
char *)(data + len),
12240 (
int)(data_len - len),
12245 }
else if (n > 0) {
12256 "Websocket pull failed; closing connection");
12263 conn->data_len = conn->request_len;
12273 len = (size_t)data_len + header_len;
12278 memcpy(data, buf + header_len, (
size_t)data_len);
12281 memmove(buf, buf + len, body_len - len);
12284 conn->data_len -= (int)len;
12288 if (mask_len > 0) {
12289 for (i = 0; i < (size_t)data_len; i++) {
12290 data[i] ^= mask[i & 3];
12294 exit_by_callback = 0;
12295 if (enable_ping_pong && ((mop & 0xF) == MG_WEBSOCKET_OPCODE_PONG)) {
12297 DEBUG_TRACE(
"PONG from %s:%u",
12298 conn->request_info.remote_addr,
12299 conn->request_info.remote_port);
12302 }
else if (enable_ping_pong
12303 && ((mop & 0xF) == MG_WEBSOCKET_OPCODE_PING)) {
12305 DEBUG_TRACE(
"Reply PING from %s:%u",
12306 conn->request_info.remote_addr,
12307 conn->request_info.remote_port);
12308 ret = mg_websocket_write(conn,
12309 MG_WEBSOCKET_OPCODE_PONG,
12314 DEBUG_TRACE(
"Reply PONG failed (%i)", ret);
12322 if ((ws_data_handler != NULL)
12323 && !ws_data_handler(conn,
12328 exit_by_callback = 1;
12337 if (exit_by_callback) {
12338 DEBUG_TRACE(
"Callback requests to close connection from %s:%u",
12339 conn->request_info.remote_addr,
12340 conn->request_info.remote_port);
12343 if ((mop & 0xf) == MG_WEBSOCKET_OPCODE_CONNECTION_CLOSE) {
12345 DEBUG_TRACE(
"Message requests to close connection from %s:%u",
12346 conn->request_info.remote_addr,
12347 conn->request_info.remote_port);
12355 n = pull_inner(NULL,
12357 conn->buf + conn->data_len,
12358 conn->buf_size - conn->data_len,
12362 DEBUG_TRACE(
"PULL from %s:%u failed",
12363 conn->request_info.remote_addr,
12364 conn->request_info.remote_port);
12368 conn->data_len += n;
12372 if (!conn->phys_ctx->stop_flag && !conn->must_close) {
12373 if (ping_count > MG_MAX_UNANSWERED_PING) {
12375 DEBUG_TRACE(
"Too many (%i) unanswered ping from %s:%u "
12376 "- closing connection",
12378 conn->request_info.remote_addr,
12379 conn->request_info.remote_port);
12382 if (enable_ping_pong) {
12384 DEBUG_TRACE(
"PING to %s:%u",
12385 conn->request_info.remote_addr,
12386 conn->request_info.remote_port);
12387 ret = mg_websocket_write(conn,
12388 MG_WEBSOCKET_OPCODE_PING,
12394 DEBUG_TRACE(
"Send PING failed (%i)", ret);
12407 mg_set_thread_name(
"worker");
12408 conn->in_websocket_handling = 0;
12409 DEBUG_TRACE(
"Websocket connection %s:%u left data processing loop",
12410 conn->request_info.remote_addr,
12411 conn->request_info.remote_port);
12416 mg_websocket_write_exec(
struct mg_connection *conn,
12420 uint32_t masking_key)
12422 unsigned char header[14];
12426 #if defined(GCC_DIAGNOSTIC)
12428 #pragma GCC diagnostic push
12429 #pragma GCC diagnostic ignored "-Wconversion"
12432 header[0] = 0x80u | (
unsigned char)((
unsigned)opcode & 0xf);
12434 #if defined(GCC_DIAGNOSTIC)
12435 #pragma GCC diagnostic pop
12439 if (dataLen < 126) {
12441 header[1] = (
unsigned char)dataLen;
12443 }
else if (dataLen <= 0xFFFF) {
12445 uint16_t len = htons((uint16_t)dataLen);
12447 memcpy(header + 2, &len, 2);
12451 uint32_t len1 = htonl((uint32_t)((uint64_t)dataLen >> 32));
12452 uint32_t len2 = htonl((uint32_t)(dataLen & 0xFFFFFFFFu));
12454 memcpy(header + 2, &len1, 4);
12455 memcpy(header + 6, &len2, 4);
12462 memcpy(header + headerLen, &masking_key, 4);
12477 (void)mg_lock_connection(conn);
12479 retval = mg_write(conn, header, headerLen);
12480 if (retval != (
int)headerLen) {
12485 retval = mg_write(conn, data, dataLen);
12491 mg_unlock_connection(conn);
12497 mg_websocket_write(
struct mg_connection *conn,
12502 return mg_websocket_write_exec(conn, opcode, data, dataLen, 0);
12507 mask_data(
const char *in,
size_t in_len, uint32_t masking_key,
char *out)
12512 if ((in_len > 3) && ((ptrdiff_t)in % 4) == 0) {
12514 while (i < (in_len - 3)) {
12515 *(uint32_t *)(
void *)(out + i) =
12516 *(uint32_t *)(
void *)(in + i) ^ masking_key;
12522 while (i < in_len) {
12523 *(uint8_t *)(
void *)(out + i) =
12524 *(uint8_t *)(
void *)(in + i)
12525 ^ *(((uint8_t *)&masking_key) + (i % 4));
12533 mg_websocket_client_write(
struct mg_connection *conn,
12539 char *masked_data =
12540 (
char *)mg_malloc_ctx(((dataLen + 7) / 4) * 4, conn->phys_ctx);
12541 uint32_t masking_key = 0;
12543 if (masked_data == NULL) {
12545 mg_cry_internal(conn,
12547 "Cannot allocate buffer for masked websocket response: "
12554 masking_key = (uint32_t)get_random();
12555 }
while (masking_key == 0);
12557 mask_data(data, dataLen, masking_key, masked_data);
12559 retval = mg_websocket_write_exec(
12560 conn, opcode, masked_data, dataLen, masking_key);
12561 mg_free(masked_data);
12568 handle_websocket_request(
struct mg_connection *conn,
12570 int is_callback_resource,
12571 struct mg_websocket_subprotocols *subprotocols,
12572 mg_websocket_connect_handler ws_connect_handler,
12573 mg_websocket_ready_handler ws_ready_handler,
12574 mg_websocket_data_handler ws_data_handler,
12575 mg_websocket_close_handler ws_close_handler,
12578 const char *websock_key = mg_get_header(conn,
"Sec-WebSocket-Key");
12579 const char *version = mg_get_header(conn,
"Sec-WebSocket-Version");
12580 ptrdiff_t lua_websock = 0;
12582 #if !defined(USE_LUA)
12588 if (!websock_key) {
12595 const char *key1 = mg_get_header(conn,
"Sec-WebSocket-Key1");
12596 const char *key2 = mg_get_header(conn,
"Sec-WebSocket-Key2");
12599 if ((key1 != NULL) && (key2 != NULL)) {
12601 conn->content_len = 8;
12602 if (8 == mg_read(conn, key3, 8)) {
12604 mg_send_http_error(conn,
12607 "Protocol upgrade to RFC 6455 required");
12612 mg_send_http_error(conn, 400,
"%s",
"Malformed websocket request");
12618 if ((version == NULL) || (strcmp(version,
"13") != 0)) {
12620 mg_send_http_error(conn, 426,
"%s",
"Protocol upgrade required");
12628 if (is_callback_resource) {
12630 const char *protocols[64];
12631 int nbSubprotocolHeader = get_req_headers(&conn->request_info,
12632 "Sec-WebSocket-Protocol",
12635 if ((nbSubprotocolHeader > 0) && subprotocols) {
12639 const char *sep, *curSubProtocol,
12640 *acceptedWebSocketSubprotocol = NULL;
12645 const char *protocol = protocols[cnt];
12648 sep = strchr(protocol,
',');
12649 curSubProtocol = protocol;
12650 len = sep ? (
unsigned long)(sep - protocol)
12651 : (unsigned long)strlen(protocol);
12652 while (sep && isspace(*++sep))
12657 for (idx = 0; idx < subprotocols->nb_subprotocols; idx++) {
12658 if ((strlen(subprotocols->subprotocols[idx]) == len)
12659 && (strncmp(curSubProtocol,
12660 subprotocols->subprotocols[idx],
12663 acceptedWebSocketSubprotocol =
12664 subprotocols->subprotocols[idx];
12668 }
while (sep && !acceptedWebSocketSubprotocol);
12669 }
while (++cnt < nbSubprotocolHeader
12670 && !acceptedWebSocketSubprotocol);
12672 conn->request_info.acceptedWebSocketSubprotocol =
12673 acceptedWebSocketSubprotocol;
12675 }
else if (nbSubprotocolHeader > 0) {
12677 const char *protocol = protocols[0];
12682 const char *sep = strrchr(protocol,
',');
12685 conn->request_info.acceptedWebSocketSubprotocol = protocol;
12695 while (isspace(*++sep)) {
12698 conn->request_info.acceptedWebSocketSubprotocol = sep;
12702 if ((ws_connect_handler != NULL)
12703 && (ws_connect_handler(conn, cbData) != 0)) {
12713 #if defined(USE_LUA)
12717 if (conn->dom_ctx->config[LUA_WEBSOCKET_EXTENSIONS]) {
12718 lua_websock = match_prefix(
12719 conn->dom_ctx->config[LUA_WEBSOCKET_EXTENSIONS],
12720 strlen(conn->dom_ctx->config[LUA_WEBSOCKET_EXTENSIONS]),
12726 conn->lua_websocket_state = lua_websocket_new(path, conn);
12727 if (!conn->lua_websocket_state) {
12736 if (!is_callback_resource && !lua_websock) {
12741 mg_send_http_error(conn, 404,
"%s",
"Not found");
12746 if (!send_websocket_handshake(conn, websock_key)) {
12747 mg_send_http_error(conn, 500,
"%s",
"Websocket handshake failed");
12752 if (is_callback_resource) {
12753 if (ws_ready_handler != NULL) {
12754 ws_ready_handler(conn, cbData);
12756 #if defined(USE_LUA)
12757 }
else if (lua_websock) {
12758 if (!lua_websocket_ready(conn, conn->lua_websocket_state)) {
12766 if (is_callback_resource) {
12767 read_websocket(conn, ws_data_handler, cbData);
12768 #if defined(USE_LUA)
12769 }
else if (lua_websock) {
12770 read_websocket(conn, lua_websocket_data, conn->lua_websocket_state);
12775 if (ws_close_handler) {
12776 ws_close_handler(conn, cbData);
12782 is_websocket_protocol(
const struct mg_connection *conn)
12784 const char *upgrade, *connection;
12792 upgrade = mg_get_header(conn,
"Upgrade");
12793 if (upgrade == NULL) {
12798 if (!mg_strcasestr(upgrade,
"websocket")) {
12802 connection = mg_get_header(conn,
"Connection");
12803 if (connection == NULL) {
12806 if (!mg_strcasestr(connection,
"upgrade")) {
12825 return (n >= 0) && (n <= 255);
12830 parse_net(
const char *spec, uint32_t *net, uint32_t *mask)
12832 int n, a, b, c, d, slash = 32, len = 0;
12834 if (((sscanf(spec,
"%d.%d.%d.%d/%d%n", &a, &b, &c, &d, &slash, &n) == 5)
12835 || (sscanf(spec,
"%d.%d.%d.%d%n", &a, &b, &c, &d, &n) == 4))
12836 && isbyte(a) && isbyte(b) && isbyte(c) && isbyte(d) && (slash >= 0)
12839 *net = ((uint32_t)a << 24) | ((uint32_t)b << 16) | ((uint32_t)c << 8)
12841 *mask = slash ? (0xffffffffU << (32 - slash)) : 0;
12849 set_throttle(
const char *spec, uint32_t remote_ip,
const char *uri)
12852 struct vec vec, val;
12853 uint32_t net, mask;
12857 while ((spec = next_option(spec, &vec, &val)) != NULL) {
12859 if ((val.ptr == NULL) || (sscanf(val.ptr,
"%lf%c", &v, &mult) < 1)
12861 || ((lowercase(&mult) !=
'k') && (lowercase(&mult) !=
'm')
12862 && (mult !=
','))) {
12865 v *= (lowercase(&mult) ==
'k')
12867 : ((lowercase(&mult) ==
'm') ? 1048576 : 1);
12868 if (vec.len == 1 && vec.ptr[0] ==
'*') {
12870 }
else if (parse_net(vec.ptr, &net, &mask) > 0) {
12871 if ((remote_ip & mask) == net) {
12874 }
else if (match_prefix(vec.ptr, vec.len, uri) > 0) {
12884 get_remote_ip(
const struct mg_connection *conn)
12889 return ntohl(*(
const uint32_t *)&conn->client.rsa.sin.sin_addr);
12897 #if defined(MG_LEGACY_INTERFACE)
12903 struct mg_upload_user_data {
12904 struct mg_connection *conn;
12905 const char *destination_dir;
12906 int num_uploaded_files;
12912 mg_upload_field_found(
const char *key,
12913 const char *filename,
12919 struct mg_upload_user_data *fud = (
struct mg_upload_user_data *)user_data;
12923 mg_cry_internal(fud->conn,
"%s: No filename set", __func__);
12924 return FORM_FIELD_STORAGE_ABORT;
12926 mg_snprintf(fud->conn,
12931 fud->destination_dir,
12934 mg_cry_internal(fud->conn,
"%s: File path too long", __func__);
12935 return FORM_FIELD_STORAGE_ABORT;
12937 return FORM_FIELD_STORAGE_STORE;
12943 mg_upload_field_get(
const char *key,
12960 mg_upload_field_stored(
const char *path,
long long file_size,
void *user_data)
12962 struct mg_upload_user_data *fud = (
struct mg_upload_user_data *)user_data;
12965 fud->num_uploaded_files++;
12966 fud->conn->phys_ctx->callbacks.upload(fud->conn, path);
12974 mg_upload(
struct mg_connection *conn,
const char *destination_dir)
12976 struct mg_upload_user_data fud = {conn, destination_dir, 0};
12977 struct mg_form_data_handler fdh = {mg_upload_field_found,
12978 mg_upload_field_get,
12979 mg_upload_field_stored,
12983 fdh.user_data = (
void *)&fud;
12984 ret = mg_handle_form_request(conn, &fdh);
12987 mg_cry_internal(conn,
"%s: Error while parsing the request", __func__);
12990 return fud.num_uploaded_files;
12996 get_first_ssl_listener_index(
const struct mg_context *ctx)
13001 for (i = 0; ((idx == -1) && (i < ctx->num_listening_sockets)); i++) {
13002 idx = ctx->listening_sockets[i].is_ssl ? ((int)(i)) : -1;
13011 static const char *
13012 alloc_get_host(
struct mg_connection *conn)
13015 size_t buflen =
sizeof(buf);
13016 const char *host_header = get_header(conn->request_info.http_headers,
13017 conn->request_info.num_headers,
13021 if (host_header != NULL) {
13026 mg_strlcpy(buf, host_header, buflen);
13027 buf[buflen - 1] =
'\0';
13029 while (isspace(*host)) {
13035 if (*host ==
'[') {
13036 pos = strchr(host,
']');
13039 DEBUG_TRACE(
"%s",
"Host name format error '[' without ']'");
13046 pos = strchr(host,
':');
13055 const char *sslhost = conn->dom_ctx->config[AUTHENTICATION_DOMAIN];
13056 if (sslhost && (conn->dom_ctx != &(conn->phys_ctx->dd))) {
13058 if (mg_strcasecmp(host, sslhost)) {
13060 DEBUG_TRACE(
"Host mismatch: SNI: %s, HTTPS: %s",
13066 DEBUG_TRACE(
"HTTPS Host: %s", host);
13069 struct mg_domain_context *dom = &(conn->phys_ctx->dd);
13071 if (!mg_strcasecmp(host, dom->config[AUTHENTICATION_DOMAIN])) {
13074 DEBUG_TRACE(
"HTTP domain %s found",
13075 dom->config[AUTHENTICATION_DOMAIN]);
13078 conn->dom_ctx = dom;
13084 DEBUG_TRACE(
"HTTP Host: %s", host);
13088 sockaddr_to_string(buf, buflen, &conn->client.lsa);
13091 DEBUG_TRACE(
"IP: %s", host);
13094 return mg_strdup_ctx(host, conn->phys_ctx);
13099 redirect_to_https_port(
struct mg_connection *conn,
int ssl_index)
13101 char target_url[MG_BUF_LEN];
13104 conn->must_close = 1;
13110 int redirect_code = 308;
13117 sizeof(target_url),
13118 "https://%s:%d%s%s%s",
13121 #
if defined(USE_IPV6)
13122 (conn->phys_ctx->listening_sockets[ssl_index].lsa.sa.sa_family
13124 ? (
int)ntohs(conn->phys_ctx->listening_sockets[ssl_index]
13125 .lsa.sin6.sin6_port)
13128 (
int)ntohs(conn->phys_ctx->listening_sockets[ssl_index]
13129 .lsa.sin.sin_port),
13130 conn->request_info.local_uri,
13131 (conn->request_info.query_string == NULL) ?
"" :
"?",
13132 (conn->request_info.query_string == NULL)
13134 : conn->request_info.query_string);
13139 mg_send_http_error(conn, 500,
"%s",
"Redirect URL too long");
13144 mg_send_http_redirect(conn, target_url, redirect_code);
13150 handler_info_acquire(
struct mg_handler_info *handler_info)
13152 pthread_mutex_lock(&handler_info->refcount_mutex);
13153 handler_info->refcount++;
13154 pthread_mutex_unlock(&handler_info->refcount_mutex);
13159 handler_info_release(
struct mg_handler_info *handler_info)
13161 pthread_mutex_lock(&handler_info->refcount_mutex);
13162 handler_info->refcount--;
13163 pthread_cond_signal(&handler_info->refcount_cond);
13164 pthread_mutex_unlock(&handler_info->refcount_mutex);
13169 handler_info_wait_unused(
struct mg_handler_info *handler_info)
13171 pthread_mutex_lock(&handler_info->refcount_mutex);
13172 while (handler_info->refcount) {
13173 pthread_cond_wait(&handler_info->refcount_cond,
13174 &handler_info->refcount_mutex);
13176 pthread_mutex_unlock(&handler_info->refcount_mutex);
13181 mg_set_handler_type(
struct mg_context *phys_ctx,
13182 struct mg_domain_context *dom_ctx,
13185 int is_delete_request,
13186 mg_request_handler handler,
13187 struct mg_websocket_subprotocols *subprotocols,
13188 mg_websocket_connect_handler connect_handler,
13189 mg_websocket_ready_handler ready_handler,
13190 mg_websocket_data_handler data_handler,
13191 mg_websocket_close_handler close_handler,
13192 mg_authorization_handler auth_handler,
13195 struct mg_handler_info *tmp_rh, **lastref;
13196 size_t urilen = strlen(uri);
13198 if (handler_type == WEBSOCKET_HANDLER) {
13199 DEBUG_ASSERT(handler == NULL);
13200 DEBUG_ASSERT(is_delete_request || connect_handler != NULL
13201 || ready_handler != NULL || data_handler != NULL
13202 || close_handler != NULL);
13204 DEBUG_ASSERT(auth_handler == NULL);
13205 if (handler != NULL) {
13208 if (!is_delete_request && (connect_handler == NULL)
13209 && (ready_handler == NULL) && (data_handler == NULL)
13210 && (close_handler == NULL)) {
13213 if (auth_handler != NULL) {
13216 }
else if (handler_type == REQUEST_HANDLER) {
13217 DEBUG_ASSERT(connect_handler == NULL && ready_handler == NULL
13218 && data_handler == NULL && close_handler == NULL);
13219 DEBUG_ASSERT(is_delete_request || (handler != NULL));
13220 DEBUG_ASSERT(auth_handler == NULL);
13222 if ((connect_handler != NULL) || (ready_handler != NULL)
13223 || (data_handler != NULL) || (close_handler != NULL)) {
13226 if (!is_delete_request && (handler == NULL)) {
13229 if (auth_handler != NULL) {
13233 DEBUG_ASSERT(handler == NULL);
13234 DEBUG_ASSERT(connect_handler == NULL && ready_handler == NULL
13235 && data_handler == NULL && close_handler == NULL);
13236 DEBUG_ASSERT(auth_handler != NULL);
13237 if (handler != NULL) {
13240 if ((connect_handler != NULL) || (ready_handler != NULL)
13241 || (data_handler != NULL) || (close_handler != NULL)) {
13244 if (!is_delete_request && (auth_handler == NULL)) {
13249 if (!phys_ctx || !dom_ctx) {
13253 mg_lock_context(phys_ctx);
13256 lastref = &(dom_ctx->handlers);
13257 for (tmp_rh = dom_ctx->handlers; tmp_rh != NULL; tmp_rh = tmp_rh->next) {
13258 if (tmp_rh->handler_type == handler_type) {
13259 if ((urilen == tmp_rh->uri_len) && !strcmp(tmp_rh->uri, uri)) {
13260 if (!is_delete_request) {
13262 if (handler_type == REQUEST_HANDLER) {
13264 handler_info_wait_unused(tmp_rh);
13267 tmp_rh->handler = handler;
13268 }
else if (handler_type == WEBSOCKET_HANDLER) {
13269 tmp_rh->subprotocols = subprotocols;
13270 tmp_rh->connect_handler = connect_handler;
13271 tmp_rh->ready_handler = ready_handler;
13272 tmp_rh->data_handler = data_handler;
13273 tmp_rh->close_handler = close_handler;
13275 tmp_rh->auth_handler = auth_handler;
13277 tmp_rh->cbdata = cbdata;
13280 if (handler_type == REQUEST_HANDLER) {
13282 handler_info_wait_unused(tmp_rh);
13286 pthread_cond_destroy(&tmp_rh->refcount_cond);
13287 pthread_mutex_destroy(&tmp_rh->refcount_mutex);
13289 *lastref = tmp_rh->next;
13290 mg_free(tmp_rh->uri);
13293 mg_unlock_context(phys_ctx);
13297 lastref = &(tmp_rh->next);
13300 if (is_delete_request) {
13303 mg_unlock_context(phys_ctx);
13308 (
struct mg_handler_info *)mg_calloc_ctx(
sizeof(
struct mg_handler_info),
13311 if (tmp_rh == NULL) {
13312 mg_unlock_context(phys_ctx);
13313 mg_cry_internal(fc(phys_ctx),
13315 "Cannot create new request handler struct, OOM");
13318 tmp_rh->uri = mg_strdup_ctx(uri, phys_ctx);
13319 if (!tmp_rh->uri) {
13320 mg_unlock_context(phys_ctx);
13322 mg_cry_internal(fc(phys_ctx),
13324 "Cannot create new request handler struct, OOM");
13327 tmp_rh->uri_len = urilen;
13328 if (handler_type == REQUEST_HANDLER) {
13330 if (0 != pthread_mutex_init(&tmp_rh->refcount_mutex, NULL)) {
13331 mg_unlock_context(phys_ctx);
13333 mg_cry_internal(fc(phys_ctx),
"%s",
"Cannot init refcount mutex");
13336 if (0 != pthread_cond_init(&tmp_rh->refcount_cond, NULL)) {
13337 mg_unlock_context(phys_ctx);
13338 pthread_mutex_destroy(&tmp_rh->refcount_mutex);
13340 mg_cry_internal(fc(phys_ctx),
"%s",
"Cannot init refcount cond");
13343 tmp_rh->refcount = 0;
13344 tmp_rh->handler = handler;
13345 }
else if (handler_type == WEBSOCKET_HANDLER) {
13346 tmp_rh->subprotocols = subprotocols;
13347 tmp_rh->connect_handler = connect_handler;
13348 tmp_rh->ready_handler = ready_handler;
13349 tmp_rh->data_handler = data_handler;
13350 tmp_rh->close_handler = close_handler;
13352 tmp_rh->auth_handler = auth_handler;
13354 tmp_rh->cbdata = cbdata;
13355 tmp_rh->handler_type = handler_type;
13356 tmp_rh->next = NULL;
13359 mg_unlock_context(phys_ctx);
13364 mg_set_request_handler(
struct mg_context *ctx,
13366 mg_request_handler handler,
13369 mg_set_handler_type(ctx,
13386 mg_set_websocket_handler(
struct mg_context *ctx,
13388 mg_websocket_connect_handler connect_handler,
13389 mg_websocket_ready_handler ready_handler,
13390 mg_websocket_data_handler data_handler,
13391 mg_websocket_close_handler close_handler,
13394 mg_set_websocket_handler_with_subprotocols(ctx,
13406 mg_set_websocket_handler_with_subprotocols(
13407 struct mg_context *ctx,
13409 struct mg_websocket_subprotocols *subprotocols,
13410 mg_websocket_connect_handler connect_handler,
13411 mg_websocket_ready_handler ready_handler,
13412 mg_websocket_data_handler data_handler,
13413 mg_websocket_close_handler close_handler,
13416 int is_delete_request = (connect_handler == NULL) && (ready_handler == NULL)
13417 && (data_handler == NULL)
13418 && (close_handler == NULL);
13419 mg_set_handler_type(ctx,
13436 mg_set_auth_handler(
struct mg_context *ctx,
13438 mg_request_handler handler,
13441 mg_set_handler_type(ctx,
13458 get_request_handler(
struct mg_connection *conn,
13460 mg_request_handler *handler,
13461 struct mg_websocket_subprotocols **subprotocols,
13462 mg_websocket_connect_handler *connect_handler,
13463 mg_websocket_ready_handler *ready_handler,
13464 mg_websocket_data_handler *data_handler,
13465 mg_websocket_close_handler *close_handler,
13466 mg_authorization_handler *auth_handler,
13468 struct mg_handler_info **handler_info)
13470 const struct mg_request_info *request_info = mg_get_request_info(conn);
13471 if (request_info) {
13472 const char *uri = request_info->local_uri;
13473 size_t urilen = strlen(uri);
13474 struct mg_handler_info *tmp_rh;
13476 if (!conn || !conn->phys_ctx || !conn->dom_ctx) {
13480 mg_lock_context(conn->phys_ctx);
13483 for (tmp_rh = conn->dom_ctx->handlers; tmp_rh != NULL;
13484 tmp_rh = tmp_rh->next) {
13485 if (tmp_rh->handler_type == handler_type) {
13486 if ((urilen == tmp_rh->uri_len) && !strcmp(tmp_rh->uri, uri)) {
13487 if (handler_type == WEBSOCKET_HANDLER) {
13488 *subprotocols = tmp_rh->subprotocols;
13489 *connect_handler = tmp_rh->connect_handler;
13490 *ready_handler = tmp_rh->ready_handler;
13491 *data_handler = tmp_rh->data_handler;
13492 *close_handler = tmp_rh->close_handler;
13493 }
else if (handler_type == REQUEST_HANDLER) {
13494 *handler = tmp_rh->handler;
13496 handler_info_acquire(tmp_rh);
13497 *handler_info = tmp_rh;
13499 *auth_handler = tmp_rh->auth_handler;
13501 *cbdata = tmp_rh->cbdata;
13502 mg_unlock_context(conn->phys_ctx);
13509 for (tmp_rh = conn->dom_ctx->handlers; tmp_rh != NULL;
13510 tmp_rh = tmp_rh->next) {
13511 if (tmp_rh->handler_type == handler_type) {
13512 if ((tmp_rh->uri_len < urilen) && (uri[tmp_rh->uri_len] ==
'/')
13513 && (memcmp(tmp_rh->uri, uri, tmp_rh->uri_len) == 0)) {
13514 if (handler_type == WEBSOCKET_HANDLER) {
13515 *subprotocols = tmp_rh->subprotocols;
13516 *connect_handler = tmp_rh->connect_handler;
13517 *ready_handler = tmp_rh->ready_handler;
13518 *data_handler = tmp_rh->data_handler;
13519 *close_handler = tmp_rh->close_handler;
13520 }
else if (handler_type == REQUEST_HANDLER) {
13521 *handler = tmp_rh->handler;
13523 handler_info_acquire(tmp_rh);
13524 *handler_info = tmp_rh;
13526 *auth_handler = tmp_rh->auth_handler;
13528 *cbdata = tmp_rh->cbdata;
13529 mg_unlock_context(conn->phys_ctx);
13536 for (tmp_rh = conn->dom_ctx->handlers; tmp_rh != NULL;
13537 tmp_rh = tmp_rh->next) {
13538 if (tmp_rh->handler_type == handler_type) {
13539 if (match_prefix(tmp_rh->uri, tmp_rh->uri_len, uri) > 0) {
13540 if (handler_type == WEBSOCKET_HANDLER) {
13541 *subprotocols = tmp_rh->subprotocols;
13542 *connect_handler = tmp_rh->connect_handler;
13543 *ready_handler = tmp_rh->ready_handler;
13544 *data_handler = tmp_rh->data_handler;
13545 *close_handler = tmp_rh->close_handler;
13546 }
else if (handler_type == REQUEST_HANDLER) {
13547 *handler = tmp_rh->handler;
13549 handler_info_acquire(tmp_rh);
13550 *handler_info = tmp_rh;
13552 *auth_handler = tmp_rh->auth_handler;
13554 *cbdata = tmp_rh->cbdata;
13555 mg_unlock_context(conn->phys_ctx);
13561 mg_unlock_context(conn->phys_ctx);
13572 is_in_script_path(
const struct mg_connection *conn,
const char *path)
13582 #if defined(USE_WEBSOCKET) && defined(MG_LEGACY_INTERFACE)
13584 deprecated_websocket_connect_wrapper(
const struct mg_connection *conn,
13587 struct mg_callbacks *pcallbacks = (
struct mg_callbacks *)cbdata;
13588 if (pcallbacks->websocket_connect) {
13589 return pcallbacks->websocket_connect(conn);
13597 deprecated_websocket_ready_wrapper(
struct mg_connection *conn,
void *cbdata)
13599 struct mg_callbacks *pcallbacks = (
struct mg_callbacks *)cbdata;
13600 if (pcallbacks->websocket_ready) {
13601 pcallbacks->websocket_ready(conn);
13607 deprecated_websocket_data_wrapper(
struct mg_connection *conn,
13613 struct mg_callbacks *pcallbacks = (
struct mg_callbacks *)cbdata;
13614 if (pcallbacks->websocket_data) {
13615 return pcallbacks->websocket_data(conn, bits, data, len);
13628 handle_request(
struct mg_connection *conn)
13630 struct mg_request_info *ri = &conn->request_info;
13631 char path[PATH_MAX];
13632 int uri_len, ssl_index;
13633 int is_found = 0, is_script_resource = 0, is_websocket_request = 0,
13634 is_put_or_delete_request = 0, is_callback_resource = 0;
13636 struct mg_file file = STRUCT_FILE_INITIALIZER;
13637 mg_request_handler callback_handler = NULL;
13638 struct mg_handler_info *handler_info = NULL;
13639 struct mg_websocket_subprotocols *subprotocols;
13640 mg_websocket_connect_handler ws_connect_handler = NULL;
13641 mg_websocket_ready_handler ws_ready_handler = NULL;
13642 mg_websocket_data_handler ws_data_handler = NULL;
13643 mg_websocket_close_handler ws_close_handler = NULL;
13644 void *callback_data = NULL;
13645 mg_authorization_handler auth_handler = NULL;
13646 void *auth_callback_data = NULL;
13648 time_t curtime = time(NULL);
13655 if ((conn->request_info.query_string = strchr(ri->request_uri,
'?'))
13657 *((
char *)conn->request_info.query_string++) =
'\0';
13661 if (!conn->client.is_ssl && conn->client.ssl_redir) {
13662 ssl_index = get_first_ssl_listener_index(conn->phys_ctx);
13663 if (ssl_index >= 0) {
13664 redirect_to_https_port(conn, ssl_index);
13668 mg_send_http_error(conn,
13671 "Error: SSL forward not configured properly");
13672 mg_cry_internal(conn,
13674 "Can not redirect to SSL, no SSL port available");
13678 uri_len = (int)strlen(ri->local_uri);
13681 if (should_decode_url(conn)) {
13683 ri->local_uri, uri_len, (
char *)ri->local_uri, uri_len + 1, 0);
13688 remove_double_dots_and_double_slashes((
char *)ri->local_uri);
13691 uri_len = (int)strlen(ri->local_uri);
13692 DEBUG_TRACE(
"URL: %s", ri->local_uri);
13695 conn->throttle = set_throttle(conn->dom_ctx->config[THROTTLE],
13696 get_remote_ip(conn),
13700 if (conn->phys_ctx->callbacks.begin_request != NULL) {
13704 i = conn->phys_ctx->callbacks.begin_request(conn);
13708 conn->status_code = i;
13709 discard_unread_request_data(conn);
13711 }
else if (i == 0) {
13725 if (!strcmp(ri->request_method,
"OPTIONS")) {
13729 const char *cors_meth_cfg =
13730 conn->dom_ctx->config[ACCESS_CONTROL_ALLOW_METHODS];
13731 const char *cors_orig_cfg =
13732 conn->dom_ctx->config[ACCESS_CONTROL_ALLOW_ORIGIN];
13733 const char *cors_origin =
13734 get_header(ri->http_headers, ri->num_headers,
"Origin");
13735 const char *cors_acrm = get_header(ri->http_headers,
13737 "Access-Control-Request-Method");
13742 if ((cors_meth_cfg != NULL) && (*cors_meth_cfg != 0)
13743 && (cors_orig_cfg != NULL) && (*cors_orig_cfg != 0)
13744 && (cors_origin != NULL) && (cors_acrm != NULL)) {
13748 const char *cors_acrh =
13749 get_header(ri->http_headers,
13751 "Access-Control-Request-Headers");
13753 gmt_time_string(date,
sizeof(date), &curtime);
13755 "HTTP/1.1 200 OK\r\n"
13757 "Access-Control-Allow-Origin: %s\r\n"
13758 "Access-Control-Allow-Methods: %s\r\n"
13759 "Content-Length: 0\r\n"
13760 "Connection: %s\r\n",
13763 ((cors_meth_cfg[0] ==
'*') ? cors_acrm : cors_meth_cfg),
13764 suggest_connection_header(conn));
13766 if (cors_acrh != NULL) {
13768 const char *cors_hdr_cfg =
13769 conn->dom_ctx->config[ACCESS_CONTROL_ALLOW_HEADERS];
13771 if ((cors_hdr_cfg != NULL) && (*cors_hdr_cfg != 0)) {
13778 "Access-Control-Allow-Headers: %s\r\n",
13779 ((cors_hdr_cfg[0] ==
'*') ? cors_acrh
13783 mg_printf(conn,
"Access-Control-Max-Age: 60\r\n");
13785 mg_printf(conn,
"\r\n");
13795 is_websocket_request = is_websocket_protocol(conn);
13796 #if defined(USE_WEBSOCKET)
13797 handler_type = is_websocket_request ? WEBSOCKET_HANDLER : REQUEST_HANDLER;
13799 handler_type = REQUEST_HANDLER;
13802 if (get_request_handler(conn,
13806 &ws_connect_handler,
13817 is_callback_resource = 1;
13818 is_script_resource = 1;
13819 is_put_or_delete_request = is_put_or_delete_method(conn);
13821 no_callback_resource:
13826 is_callback_resource = 0;
13827 interpret_uri(conn,
13832 &is_script_resource,
13833 &is_websocket_request,
13834 &is_put_or_delete_request);
13839 if (get_request_handler(conn,
13848 &auth_callback_data,
13850 if (!auth_handler(conn, auth_callback_data)) {
13853 }
else if (is_put_or_delete_request && !is_script_resource
13854 && !is_callback_resource) {
13857 #if defined(NO_FILES)
13860 if (conn->dom_ctx->config[DOCUMENT_ROOT] == NULL) {
13864 mg_send_http_error(conn,
13866 "%s method not allowed",
13867 conn->request_info.request_method);
13871 #if !defined(NO_FILES)
13875 if (!is_authorized_for_put(conn)) {
13876 send_authorization_request(conn, NULL);
13885 if (!check_authorization(conn, path)) {
13886 send_authorization_request(conn, NULL);
13894 if (is_callback_resource) {
13895 if (!is_websocket_request) {
13896 i = callback_handler(conn, callback_data);
13899 handler_info_release(handler_info);
13905 conn->status_code = i;
13906 discard_unread_request_data(conn);
13925 interpret_uri(conn,
13930 &is_script_resource,
13931 &is_websocket_request,
13932 &is_put_or_delete_request);
13933 callback_handler = NULL;
13944 goto no_callback_resource;
13947 #if defined(USE_WEBSOCKET)
13948 handle_websocket_request(conn,
13950 is_callback_resource,
13952 ws_connect_handler,
13963 #if defined(USE_WEBSOCKET)
13964 if (is_websocket_request) {
13965 if (is_script_resource) {
13967 if (is_in_script_path(conn, path)) {
13969 handle_websocket_request(conn,
13977 conn->phys_ctx->user_data);
13980 mg_send_http_error(conn, 403,
"%s",
"Forbidden");
13983 #if defined(MG_LEGACY_INTERFACE)
13984 handle_websocket_request(
13987 !is_script_resource ,
13989 deprecated_websocket_connect_wrapper,
13990 deprecated_websocket_ready_wrapper,
13991 deprecated_websocket_data_wrapper,
13993 conn->phys_ctx->user_data);
13995 mg_send_http_error(conn, 404,
"%s",
"Not found");
14002 #if defined(NO_FILES)
14006 mg_send_http_error(conn, 404,
"%s",
"Not Found");
14011 if (conn->dom_ctx->config[DOCUMENT_ROOT] == NULL) {
14012 mg_send_http_error(conn, 404,
"%s",
"Not Found");
14017 if (is_script_resource) {
14018 handle_file_based_request(conn, path, &file);
14023 if (is_put_or_delete_request) {
14025 if (!strcmp(ri->request_method,
"PUT")) {
14026 put_file(conn, path);
14030 if (!strcmp(ri->request_method,
"DELETE")) {
14031 delete_file(conn, path);
14035 if (!strcmp(ri->request_method,
"MKCOL")) {
14042 mg_send_http_error(conn,
14044 "%s method not allowed",
14045 conn->request_info.request_method);
14051 if (!is_found || (must_hide_file(conn, path))) {
14052 mg_send_http_error(conn, 404,
"%s",
"Not found");
14057 if (file.stat.is_directory && (uri_len > 0)
14058 && (ri->local_uri[uri_len - 1] !=
'/')) {
14059 gmt_time_string(date,
sizeof(date), &curtime);
14061 "HTTP/1.1 301 Moved Permanently\r\n"
14062 "Location: %s/\r\n"
14065 "Content-Length: 0\r\n"
14066 "Connection: %s\r\n",
14069 suggest_connection_header(conn));
14070 send_additional_header(conn);
14071 mg_printf(conn,
"\r\n");
14077 if (!strcmp(ri->request_method,
"PROPFIND")) {
14078 handle_propfind(conn, path, &file.stat);
14082 if (!strcmp(ri->request_method,
"OPTIONS")) {
14088 send_options(conn);
14092 if ((0 != strcmp(ri->request_method,
"GET"))
14093 && (0 != strcmp(ri->request_method,
"HEAD"))) {
14094 mg_send_http_error(conn,
14096 "%s method not allowed",
14097 conn->request_info.request_method);
14102 if (file.stat.is_directory) {
14106 if (!mg_strcasecmp(conn->dom_ctx->config[ENABLE_DIRECTORY_LISTING],
14108 handle_directory_request(conn, path);
14110 mg_send_http_error(conn,
14113 "Error: Directory listing denied");
14119 handle_file_based_request(conn, path, &file);
14125 handle_file_based_request(
struct mg_connection *conn,
14127 struct mg_file *file)
14129 if (!conn || !conn->dom_ctx) {
14134 #if defined(USE_LUA)
14135 }
else if (match_prefix(
14136 conn->dom_ctx->config[LUA_SERVER_PAGE_EXTENSIONS],
14137 strlen(conn->dom_ctx->config[LUA_SERVER_PAGE_EXTENSIONS]),
14140 if (is_in_script_path(conn, path)) {
14145 handle_lsp_request(conn, path, file, NULL);
14148 mg_send_http_error(conn, 403,
"%s",
"Forbidden");
14151 }
else if (match_prefix(conn->dom_ctx->config[LUA_SCRIPT_EXTENSIONS],
14153 conn->dom_ctx->config[LUA_SCRIPT_EXTENSIONS]),
14156 if (is_in_script_path(conn, path)) {
14161 mg_exec_lua_script(conn, path, NULL);
14164 mg_send_http_error(conn, 403,
"%s",
"Forbidden");
14167 #if defined(USE_DUKTAPE)
14168 }
else if (match_prefix(
14169 conn->dom_ctx->config[DUKTAPE_SCRIPT_EXTENSIONS],
14170 strlen(conn->dom_ctx->config[DUKTAPE_SCRIPT_EXTENSIONS]),
14173 if (is_in_script_path(conn, path)) {
14175 mg_exec_duktape_script(conn, path);
14178 mg_send_http_error(conn, 403,
"%s",
"Forbidden");
14181 #if !defined(NO_CGI)
14182 }
else if (match_prefix(conn->dom_ctx->config[CGI_EXTENSIONS],
14183 strlen(conn->dom_ctx->config[CGI_EXTENSIONS]),
14186 if (is_in_script_path(conn, path)) {
14188 handle_cgi_request(conn, path);
14191 mg_send_http_error(conn, 403,
"%s",
"Forbidden");
14194 }
else if (match_prefix(conn->dom_ctx->config[SSI_EXTENSIONS],
14195 strlen(conn->dom_ctx->config[SSI_EXTENSIONS]),
14198 if (is_in_script_path(conn, path)) {
14199 handle_ssi_file_request(conn, path, file);
14202 mg_send_http_error(conn, 403,
"%s",
"Forbidden");
14204 #if !defined(NO_CACHING)
14205 }
else if ((!conn->in_error_handler)
14206 && is_not_modified(conn, &file->stat)) {
14208 handle_not_modified_static_file_request(conn, file);
14211 handle_static_file_request(conn, path, file, NULL, NULL);
14217 close_all_listening_sockets(
struct mg_context *ctx)
14224 for (i = 0; i < ctx->num_listening_sockets; i++) {
14225 closesocket(ctx->listening_sockets[i].sock);
14226 ctx->listening_sockets[i].sock = INVALID_SOCKET;
14228 mg_free(ctx->listening_sockets);
14229 ctx->listening_sockets = NULL;
14230 mg_free(ctx->listening_socket_fds);
14231 ctx->listening_socket_fds = NULL;
14250 parse_port_string(
const struct vec *vec,
struct socket *so,
int *ip_version)
14252 unsigned int a, b, c, d, port;
14255 #if defined(USE_IPV6)
14256 char buf[100] = {0};
14262 memset(so, 0,
sizeof(*so));
14263 so->lsa.sin.sin_family = AF_INET;
14271 if (sscanf(vec->ptr,
"%u.%u.%u.%u:%u%n", &a, &b, &c, &d, &port, &len)
14274 so->lsa.sin.sin_addr.s_addr =
14275 htonl((a << 24) | (b << 16) | (c << 8) | d);
14276 so->lsa.sin.sin_port = htons((uint16_t)port);
14279 #if defined(USE_IPV6)
14280 }
else if (sscanf(vec->ptr,
"[%49[^]]]:%u%n", buf, &port, &len) == 2
14282 AF_INET6, buf, &so->lsa.sin6,
sizeof(so->lsa.sin6))) {
14286 so->lsa.sin6.sin6_port = htons((uint16_t)port);
14290 }
else if ((vec->ptr[0] ==
'+')
14291 && (sscanf(vec->ptr + 1,
"%u%n", &port, &len) == 1)) {
14297 #if defined(USE_IPV6)
14299 so->lsa.sin6.sin6_family = AF_INET6;
14300 so->lsa.sin6.sin6_port = htons((uint16_t)port);
14301 *ip_version = 4 + 6;
14304 so->lsa.sin.sin_port = htons((uint16_t)port);
14308 }
else if (sscanf(vec->ptr,
"%u%n", &port, &len) == 1) {
14310 so->lsa.sin.sin_port = htons((uint16_t)port);
14313 }
else if ((cb = strchr(vec->ptr,
':')) != NULL) {
14323 char hostname[256];
14324 size_t hostnlen = (size_t)(cb - vec->ptr);
14326 if (hostnlen >=
sizeof(hostname)) {
14332 memcpy(hostname, vec->ptr, hostnlen);
14333 hostname[hostnlen] = 0;
14336 AF_INET, vec->ptr, &so->lsa.sin,
sizeof(so->lsa.sin))) {
14337 if (sscanf(cb + 1,
"%u%n", &port, &len) == 1) {
14339 so->lsa.sin.sin_family = AF_INET;
14340 so->lsa.sin.sin_port = htons((uint16_t)port);
14341 len += (int)(hostnlen + 1);
14346 #if defined(USE_IPV6)
14347 }
else if (mg_inet_pton(AF_INET6,
14350 sizeof(so->lsa.sin6))) {
14351 if (sscanf(cb + 1,
"%u%n", &port, &len) == 1) {
14353 so->lsa.sin6.sin6_family = AF_INET6;
14354 so->lsa.sin.sin_port = htons((uint16_t)port);
14355 len += (int)(hostnlen + 1);
14370 if ((len < 0) && ((unsigned)len > (
unsigned)vec->len)) {
14374 ch = vec->ptr[len];
14375 so->is_ssl = (ch ==
's');
14376 so->ssl_redir = (ch ==
'r');
14379 if (is_valid_port(port)
14380 && ((ch ==
'\0') || (ch ==
's') || (ch ==
'r') || (ch ==
','))) {
14392 is_ssl_port_used(
const char *ports)
14430 int portslen = (int)strlen(ports);
14431 char prevIsNumber = 0;
14433 for (i = 0; i < portslen; i++) {
14434 if (prevIsNumber && (ports[i] ==
's' || ports[i] ==
'r')) {
14437 if (ports[i] >=
'0' && ports[i] <=
'9') {
14449 set_ports_option(
struct mg_context *phys_ctx)
14453 #if defined(USE_IPV6)
14457 struct socket so, *ptr;
14459 struct pollfd *pfd;
14464 int portsTotal = 0;
14471 memset(&so, 0,
sizeof(so));
14472 memset(&usa, 0,
sizeof(usa));
14474 list = phys_ctx->dd.config[LISTENING_PORTS];
14476 while ((list = next_option(list, &vec, NULL)) != NULL) {
14480 if (!parse_port_string(&vec, &so, &ip_version)) {
14483 "%.*s: invalid port spec (entry %i). Expecting list of: %s",
14487 "[IP_ADDRESS:]PORT[s|r]");
14491 #if !defined(NO_SSL)
14492 if (so.is_ssl && phys_ctx->dd.ssl_ctx == NULL) {
14494 mg_cry_internal(fc(phys_ctx),
14495 "Cannot add SSL socket (entry %i)",
14501 if ((so.sock = socket(so.lsa.sa.sa_family, SOCK_STREAM, 6))
14502 == INVALID_SOCKET) {
14504 mg_cry_internal(fc(phys_ctx),
14505 "cannot create socket (entry %i)",
14510 #if defined(_WIN32)
14520 if (setsockopt(so.sock,
14522 SO_EXCLUSIVEADDRUSE,
14523 (SOCK_OPT_TYPE)&on,
14530 "cannot set socket option SO_EXCLUSIVEADDRUSE (entry %i)",
14534 if (setsockopt(so.sock,
14537 (SOCK_OPT_TYPE)&on,
14542 mg_cry_internal(fc(phys_ctx),
14543 "cannot set socket option SO_REUSEADDR (entry %i)",
14548 if (ip_version > 4) {
14550 #if defined(USE_IPV6)
14551 if (ip_version > 6) {
14552 if (so.lsa.sa.sa_family == AF_INET6
14553 && setsockopt(so.sock,
14563 "cannot set socket option IPV6_V6ONLY=off (entry %i)",
14567 if (so.lsa.sa.sa_family == AF_INET6
14568 && setsockopt(so.sock,
14578 "cannot set socket option IPV6_V6ONLY=on (entry %i)",
14583 mg_cry_internal(fc(phys_ctx),
"%s",
"IPv6 not available");
14584 closesocket(so.sock);
14585 so.sock = INVALID_SOCKET;
14590 if (so.lsa.sa.sa_family == AF_INET) {
14592 len =
sizeof(so.lsa.sin);
14593 if (bind(so.sock, &so.lsa.sa, len) != 0) {
14594 mg_cry_internal(fc(phys_ctx),
14595 "cannot bind to %.*s: %d (%s)",
14600 closesocket(so.sock);
14601 so.sock = INVALID_SOCKET;
14605 #if defined(USE_IPV6)
14606 else if (so.lsa.sa.sa_family == AF_INET6) {
14608 len =
sizeof(so.lsa.sin6);
14609 if (bind(so.sock, &so.lsa.sa, len) != 0) {
14610 mg_cry_internal(fc(phys_ctx),
14611 "cannot bind to IPv6 %.*s: %d (%s)",
14616 closesocket(so.sock);
14617 so.sock = INVALID_SOCKET;
14625 "cannot bind: address family not supported (entry %i)",
14627 closesocket(so.sock);
14628 so.sock = INVALID_SOCKET;
14632 if (listen(so.sock, SOMAXCONN) != 0) {
14634 mg_cry_internal(fc(phys_ctx),
14635 "cannot listen to %.*s: %d (%s)",
14640 closesocket(so.sock);
14641 so.sock = INVALID_SOCKET;
14645 if ((getsockname(so.sock, &(usa.sa), &len) != 0)
14646 || (usa.sa.sa_family != so.lsa.sa.sa_family)) {
14648 int err = (int)ERRNO;
14649 mg_cry_internal(fc(phys_ctx),
14650 "call to getsockname failed %.*s: %d (%s)",
14655 closesocket(so.sock);
14656 so.sock = INVALID_SOCKET;
14661 #if defined(USE_IPV6)
14662 if (so.lsa.sa.sa_family == AF_INET6) {
14663 so.lsa.sin6.sin6_port = usa.sin6.sin6_port;
14667 so.lsa.sin.sin_port = usa.sin.sin_port;
14670 if ((ptr = (
struct socket *)
14671 mg_realloc_ctx(phys_ctx->listening_sockets,
14672 (phys_ctx->num_listening_sockets + 1)
14673 *
sizeof(phys_ctx->listening_sockets[0]),
14677 mg_cry_internal(fc(phys_ctx),
"%s",
"Out of memory");
14678 closesocket(so.sock);
14679 so.sock = INVALID_SOCKET;
14683 if ((pfd = (
struct pollfd *)
14684 mg_realloc_ctx(phys_ctx->listening_socket_fds,
14685 (phys_ctx->num_listening_sockets + 1)
14686 *
sizeof(phys_ctx->listening_socket_fds[0]),
14690 mg_cry_internal(fc(phys_ctx),
"%s",
"Out of memory");
14691 closesocket(so.sock);
14692 so.sock = INVALID_SOCKET;
14697 set_close_on_exec(so.sock, fc(phys_ctx));
14698 phys_ctx->listening_sockets = ptr;
14699 phys_ctx->listening_sockets[phys_ctx->num_listening_sockets] = so;
14700 phys_ctx->listening_socket_fds = pfd;
14701 phys_ctx->num_listening_sockets++;
14705 if (portsOk != portsTotal) {
14706 close_all_listening_sockets(phys_ctx);
14714 static const char *
14715 header_val(
const struct mg_connection *conn,
const char *header)
14717 const char *header_value;
14719 if ((header_value = mg_get_header(conn, header)) == NULL) {
14722 return header_value;
14727 #if defined(MG_EXTERNAL_FUNCTION_log_access)
14728 static void log_access(
const struct mg_connection *conn);
14729 #include "external_log_access.inl"
14733 log_access(
const struct mg_connection *conn)
14735 const struct mg_request_info *ri;
14737 char date[64], src_addr[IP_ADDR_STR_LEN];
14740 const char *referer;
14741 const char *user_agent;
14745 if (!conn || !conn->dom_ctx) {
14749 if (conn->dom_ctx->config[ACCESS_LOG_FILE] != NULL) {
14751 conn->dom_ctx->config[ACCESS_LOG_FILE],
14752 MG_FOPEN_MODE_APPEND,
14755 fi.access.fp = NULL;
14758 fi.access.fp = NULL;
14763 if ((fi.access.fp == NULL)
14764 && (conn->phys_ctx->callbacks.log_access == NULL)) {
14768 tm = localtime(&conn->conn_birth_time);
14770 strftime(date,
sizeof(date),
"%d/%b/%Y:%H:%M:%S %z", tm);
14772 mg_strlcpy(date,
"01/Jan/1970:00:00:00 +0000",
sizeof(date));
14773 date[
sizeof(date) - 1] =
'\0';
14776 ri = &conn->request_info;
14778 sockaddr_to_string(src_addr,
sizeof(src_addr), &conn->client.rsa);
14779 referer = header_val(conn,
"Referer");
14780 user_agent = header_val(conn,
"User-Agent");
14786 "%s - %s [%s] \"%s %s%s%s HTTP/%s\" %d %" INT64_FMT
" %s %s",
14788 (ri->remote_user == NULL) ?
"-" : ri->remote_user,
14790 ri->request_method ? ri->request_method :
"-",
14791 ri->request_uri ? ri->request_uri :
"-",
14792 ri->query_string ?
"?" :
"",
14793 ri->query_string ? ri->query_string :
"",
14796 conn->num_bytes_sent,
14800 if (conn->phys_ctx->callbacks.log_access) {
14801 conn->phys_ctx->callbacks.log_access(conn, buf);
14804 if (fi.access.fp) {
14806 flockfile(fi.access.fp);
14807 if (fprintf(fi.access.fp,
"%s\n", buf) < 1) {
14810 if (fflush(fi.access.fp) != 0) {
14813 funlockfile(fi.access.fp);
14814 if (mg_fclose(&fi.access) != 0) {
14818 mg_cry_internal(conn,
14819 "Error writing log file %s",
14820 conn->dom_ctx->config[ACCESS_LOG_FILE]);
14832 check_acl(
struct mg_context *phys_ctx, uint32_t remote_ip)
14835 uint32_t net, mask;
14839 const char *list = phys_ctx->dd.config[ACCESS_CONTROL_LIST];
14842 allowed = (list == NULL) ?
'+' :
'-';
14844 while ((list = next_option(list, &vec, NULL)) != NULL) {
14846 if ((flag !=
'+' && flag !=
'-')
14847 || (parse_net(&vec.ptr[1], &net, &mask) == 0)) {
14848 mg_cry_internal(fc(phys_ctx),
14849 "%s: subnet must be [+|-]x.x.x.x[/x]",
14854 if (net == (remote_ip & mask)) {
14859 return allowed ==
'+';
14865 #if !defined(_WIN32)
14867 set_uid_option(
struct mg_context *phys_ctx)
14873 const uid_t curr_uid = getuid();
14875 const char *run_as_user = phys_ctx->dd.config[RUN_AS_USER];
14876 const struct passwd *to_pw = NULL;
14878 if (run_as_user != NULL && (to_pw = getpwnam(run_as_user)) == NULL) {
14881 mg_cry_internal(fc(phys_ctx),
14882 "%s: unknown user [%s]",
14885 }
else if (run_as_user == NULL || curr_uid == to_pw->pw_uid) {
14892 if (setgid(to_pw->pw_gid) == -1) {
14893 mg_cry_internal(fc(phys_ctx),
14894 "%s: setgid(%s): %s",
14898 }
else if (setgroups(0, NULL) == -1) {
14899 mg_cry_internal(fc(phys_ctx),
14900 "%s: setgroups(): %s",
14903 }
else if (setuid(to_pw->pw_uid) == -1) {
14904 mg_cry_internal(fc(phys_ctx),
14905 "%s: setuid(%s): %s",
14921 tls_dtor(
void *key)
14923 struct mg_workerTLS *tls = (
struct mg_workerTLS *)key;
14927 if (tls->is_master == 2) {
14928 tls->is_master = -3;
14932 pthread_setspecific(sTlsKey, NULL);
14936 #if !defined(NO_SSL)
14938 static int ssl_use_pem_file(
struct mg_context *phys_ctx,
14939 struct mg_domain_context *dom_ctx,
14941 const char *chain);
14942 static const char *ssl_error(
void);
14946 refresh_trust(
struct mg_connection *conn)
14948 static int reload_lock = 0;
14949 static long int data_check = 0;
14950 volatile int *p_reload_lock = (
volatile int *)&reload_lock;
14952 struct stat cert_buf;
14956 int should_verify_peer;
14958 if ((pem = conn->dom_ctx->config[SSL_CERTIFICATE]) == NULL) {
14963 chain = conn->dom_ctx->config[SSL_CERTIFICATE_CHAIN];
14964 if (chain == NULL) {
14973 if (stat(pem, &cert_buf) != -1) {
14974 t = (
long int)cert_buf.st_mtime;
14977 if (data_check != t) {
14980 should_verify_peer = 0;
14981 if (conn->dom_ctx->config[SSL_DO_VERIFY_PEER] != NULL) {
14982 if (mg_strcasecmp(conn->dom_ctx->config[SSL_DO_VERIFY_PEER],
"yes")
14984 should_verify_peer = 1;
14985 }
else if (mg_strcasecmp(conn->dom_ctx->config[SSL_DO_VERIFY_PEER],
14988 should_verify_peer = 1;
14992 if (should_verify_peer) {
14993 char *ca_path = conn->dom_ctx->config[SSL_CA_PATH];
14994 char *ca_file = conn->dom_ctx->config[SSL_CA_FILE];
14995 if (SSL_CTX_load_verify_locations(conn->dom_ctx->ssl_ctx,
15000 fc(conn->phys_ctx),
15001 "SSL_CTX_load_verify_locations error: %s "
15002 "ssl_verify_peer requires setting "
15003 "either ssl_ca_path or ssl_ca_file. Is any of them "
15011 if (1 == mg_atomic_inc(p_reload_lock)) {
15012 if (ssl_use_pem_file(conn->phys_ctx, conn->dom_ctx, pem, chain)
15016 *p_reload_lock = 0;
15020 while (*p_reload_lock) {
15027 #if defined(OPENSSL_API_1_1)
15029 static pthread_mutex_t *ssl_mutexes;
15033 sslize(
struct mg_connection *conn,
15035 int (*func)(SSL *),
15036 volatile int *stop_server,
15037 const struct mg_client_options *client_options)
15048 (conn->dom_ctx->config[SSL_SHORT_TRUST] != NULL)
15049 && (mg_strcasecmp(conn->dom_ctx->config[SSL_SHORT_TRUST],
"yes") == 0);
15052 int trust_ret = refresh_trust(conn);
15058 conn->ssl = SSL_new(s);
15059 if (conn->ssl == NULL) {
15062 SSL_set_app_data(conn->ssl, (
char *)conn);
15064 ret = SSL_set_fd(conn->ssl, conn->client.sock);
15066 err = SSL_get_error(conn->ssl, ret);
15067 mg_cry_internal(conn,
"SSL error %i, destroying SSL context", err);
15068 SSL_free(conn->ssl);
15070 OPENSSL_REMOVE_THREAD_STATE();
15074 if (client_options) {
15075 if (client_options->host_name) {
15076 SSL_set_tlsext_host_name(conn->ssl, client_options->host_name);
15083 for (i = 16; i <= 1024; i *= 2) {
15084 ret = func(conn->ssl);
15086 err = SSL_get_error(conn->ssl, ret);
15087 if ((err == SSL_ERROR_WANT_CONNECT)
15088 || (err == SSL_ERROR_WANT_ACCEPT)
15089 || (err == SSL_ERROR_WANT_READ) || (err == SSL_ERROR_WANT_WRITE)
15090 || (err == SSL_ERROR_WANT_X509_LOOKUP)) {
15094 if (*stop_server) {
15100 }
else if (err == SSL_ERROR_SYSCALL) {
15103 mg_cry_internal(conn,
"SSL syscall error %i", err);
15108 mg_cry_internal(conn,
"sslize error: %s", ssl_error());
15119 SSL_free(conn->ssl);
15121 OPENSSL_REMOVE_THREAD_STATE();
15130 static const char *
15134 err = ERR_get_error();
15135 return ((err == 0) ?
"" : ERR_error_string(err, NULL));
15140 hexdump2string(
void *mem,
int memlen,
char *buf,
int buflen)
15143 const char hexdigit[] =
"0123456789abcdef";
15145 if ((memlen <= 0) || (buflen <= 0)) {
15148 if (buflen < (3 * memlen)) {
15152 for (i = 0; i < memlen; i++) {
15154 buf[3 * i - 1] =
' ';
15156 buf[3 * i] = hexdigit[(((uint8_t *)mem)[i] >> 4) & 0xF];
15157 buf[3 * i + 1] = hexdigit[((uint8_t *)mem)[i] & 0xF];
15159 buf[3 * memlen - 1] = 0;
15166 ssl_get_client_cert_info(
struct mg_connection *conn)
15168 X509 *cert = SSL_get_peer_certificate(conn->ssl);
15170 char str_subject[1024];
15171 char str_issuer[1024];
15172 char str_finger[1024];
15173 unsigned char buf[256];
15174 char *str_serial = NULL;
15177 unsigned char *tmp_buf;
15178 unsigned char *tmp_p;
15181 const EVP_MD *digest = EVP_get_digestbyname(
"sha1");
15184 X509_NAME *subj = X509_get_subject_name(cert);
15185 X509_NAME *iss = X509_get_issuer_name(cert);
15188 ASN1_INTEGER *serial = X509_get_serialNumber(cert);
15191 BIGNUM *serial_bn = ASN1_INTEGER_to_BN(serial, NULL);
15192 str_serial = BN_bn2hex(serial_bn);
15193 BN_free(serial_bn);
15196 (void)X509_NAME_oneline(subj, str_subject, (
int)
sizeof(str_subject));
15197 (void)X509_NAME_oneline(iss, str_issuer, (
int)
sizeof(str_issuer));
15204 ilen = i2d_X509(cert, NULL);
15205 tmp_buf = (ilen > 0)
15206 ? (
unsigned char *)mg_malloc_ctx((
unsigned)ilen + 1,
15211 (void)i2d_X509(cert, &tmp_p);
15213 tmp_buf, (
unsigned)ilen, buf, &ulen, digest, NULL)) {
15219 if (!hexdump2string(
15220 buf, (
int)ulen, str_finger, (
int)
sizeof(str_finger))) {
15224 conn->request_info.client_cert = (
struct mg_client_cert *)
15225 mg_malloc_ctx(
sizeof(
struct mg_client_cert), conn->phys_ctx);
15226 if (conn->request_info.client_cert) {
15227 conn->request_info.client_cert->peer_cert = (
void *)cert;
15228 conn->request_info.client_cert->subject =
15229 mg_strdup_ctx(str_subject, conn->phys_ctx);
15230 conn->request_info.client_cert->issuer =
15231 mg_strdup_ctx(str_issuer, conn->phys_ctx);
15232 conn->request_info.client_cert->serial =
15233 mg_strdup_ctx(str_serial, conn->phys_ctx);
15234 conn->request_info.client_cert->finger =
15235 mg_strdup_ctx(str_finger, conn->phys_ctx);
15237 mg_cry_internal(conn,
15239 "Out of memory: Cannot allocate memory for client "
15245 OPENSSL_free(str_serial);
15250 #if defined(OPENSSL_API_1_1)
15253 ssl_locking_callback(
int mode,
int mutex_num,
const char *file,
int line)
15260 (void)pthread_mutex_lock(&ssl_mutexes[mutex_num]);
15262 (void)pthread_mutex_unlock(&ssl_mutexes[mutex_num]);
15268 #if !defined(NO_SSL_DL)
15270 load_dll(
char *ebuf,
size_t ebuf_len,
const char *dll_name,
struct ssl_func *sw)
15277 struct ssl_func *fp;
15281 if ((dll_handle = dlopen(dll_name, RTLD_LAZY)) == NULL) {
15286 "%s: cannot load %s",
15293 for (fp = sw; fp->name != NULL; fp++) {
15294 #if defined(_WIN32)
15296 u.fp = (void (*)(void))dlsym(dll_handle, fp->name);
15301 u.p = dlsym(dll_handle, fp->name);
15303 if (u.fp == NULL) {
15309 "%s: %s: cannot find %s",
15315 size_t cur_len = strlen(ebuf);
15320 ebuf_len - cur_len - 3,
15325 strcat(ebuf,
"...");
15337 (void)dlclose(dll_handle);
15345 static void *ssllib_dll_handle;
15346 static void *cryptolib_dll_handle;
15351 #if defined(SSL_ALREADY_INITIALIZED)
15352 static int cryptolib_users = 1;
15354 static int cryptolib_users = 0;
15359 initialize_ssl(
char *ebuf,
size_t ebuf_len)
15361 #if defined(OPENSSL_API_1_1)
15362 if (ebuf_len > 0) {
15366 #if !defined(NO_SSL_DL)
15367 if (!cryptolib_dll_handle) {
15368 cryptolib_dll_handle = load_dll(ebuf, ebuf_len, CRYPTO_LIB, crypto_sw);
15369 if (!cryptolib_dll_handle) {
15374 "%s: error loading library %s",
15377 DEBUG_TRACE(
"%s", ebuf);
15383 if (mg_atomic_inc(&cryptolib_users) > 1) {
15391 if (ebuf_len > 0) {
15395 #if !defined(NO_SSL_DL)
15396 if (!cryptolib_dll_handle) {
15397 cryptolib_dll_handle = load_dll(ebuf, ebuf_len, CRYPTO_LIB, crypto_sw);
15398 if (!cryptolib_dll_handle) {
15403 "%s: error loading library %s",
15406 DEBUG_TRACE(
"%s", ebuf);
15412 if (mg_atomic_inc(&cryptolib_users) > 1) {
15419 num_locks = CRYPTO_num_locks();
15420 if (num_locks < 0) {
15423 size =
sizeof(pthread_mutex_t) * ((
size_t)(num_locks));
15426 if (num_locks == 0) {
15428 ssl_mutexes = NULL;
15431 ssl_mutexes = (pthread_mutex_t *)mg_malloc(size);
15434 if (ssl_mutexes == NULL) {
15439 "%s: cannot allocate mutexes: %s",
15442 DEBUG_TRACE(
"%s", ebuf);
15447 for (i = 0; i < num_locks; i++) {
15448 if (0 != pthread_mutex_init(&ssl_mutexes[i], &pthread_mutex_attr)) {
15453 "%s: error initializing mutex %i of %i",
15457 DEBUG_TRACE(
"%s", ebuf);
15458 mg_free(ssl_mutexes);
15464 CRYPTO_set_locking_callback(&ssl_locking_callback);
15465 CRYPTO_set_id_callback(&mg_current_thread_id);
15468 #if !defined(NO_SSL_DL)
15469 if (!ssllib_dll_handle) {
15470 ssllib_dll_handle = load_dll(ebuf, ebuf_len, SSL_LIB, ssl_sw);
15471 if (!ssllib_dll_handle) {
15472 #if !defined(OPENSSL_API_1_1)
15473 mg_free(ssl_mutexes);
15475 DEBUG_TRACE(
"%s", ebuf);
15481 #if defined(OPENSSL_API_1_1)
15483 OPENSSL_init_ssl(0, NULL);
15484 OPENSSL_init_ssl(OPENSSL_INIT_LOAD_SSL_STRINGS
15485 | OPENSSL_INIT_LOAD_CRYPTO_STRINGS,
15489 SSL_library_init();
15490 SSL_load_error_strings();
15498 ssl_use_pem_file(
struct mg_context *phys_ctx,
15499 struct mg_domain_context *dom_ctx,
15503 if (SSL_CTX_use_certificate_file(dom_ctx->ssl_ctx, pem, 1) == 0) {
15504 mg_cry_internal(fc(phys_ctx),
15505 "%s: cannot open certificate file %s: %s",
15513 if (SSL_CTX_use_PrivateKey_file(dom_ctx->ssl_ctx, pem, 1) == 0) {
15514 mg_cry_internal(fc(phys_ctx),
15515 "%s: cannot open private key file %s: %s",
15522 if (SSL_CTX_check_private_key(dom_ctx->ssl_ctx) == 0) {
15523 mg_cry_internal(fc(phys_ctx),
15524 "%s: certificate and private key do not match: %s",
15539 if (SSL_CTX_use_certificate_chain_file(dom_ctx->ssl_ctx, chain) == 0) {
15540 mg_cry_internal(fc(phys_ctx),
15541 "%s: cannot use certificate chain file %s: %s",
15552 #if defined(OPENSSL_API_1_1)
15553 static unsigned long
15554 ssl_get_protocol(
int version_id)
15556 long unsigned ret = (
long unsigned)SSL_OP_ALL;
15557 if (version_id > 0)
15558 ret |= SSL_OP_NO_SSLv2;
15559 if (version_id > 1)
15560 ret |= SSL_OP_NO_SSLv3;
15561 if (version_id > 2)
15562 ret |= SSL_OP_NO_TLSv1;
15563 if (version_id > 3)
15564 ret |= SSL_OP_NO_TLSv1_1;
15569 ssl_get_protocol(
int version_id)
15571 long ret = (long)SSL_OP_ALL;
15572 if (version_id > 0)
15573 ret |= SSL_OP_NO_SSLv2;
15574 if (version_id > 1)
15575 ret |= SSL_OP_NO_SSLv3;
15576 if (version_id > 2)
15577 ret |= SSL_OP_NO_TLSv1;
15578 if (version_id > 3)
15579 ret |= SSL_OP_NO_TLSv1_1;
15596 ssl_info_callback(
const SSL *ssl,
int what,
int ret)
15600 if (what & SSL_CB_HANDSHAKE_START) {
15601 SSL_get_app_data(ssl);
15603 if (what & SSL_CB_HANDSHAKE_DONE) {
15612 ssl_servername_callback(SSL *ssl,
int *ad,
void *arg)
15614 struct mg_context *ctx = (
struct mg_context *)arg;
15615 struct mg_domain_context *dom =
15616 (
struct mg_domain_context *)ctx ? &(ctx->dd) : NULL;
15618 #if defined(GCC_DIAGNOSTIC)
15619 #pragma GCC diagnostic push
15620 #pragma GCC diagnostic ignored "-Wcast-align"
15624 struct mg_connection *conn = (
struct mg_connection *)SSL_get_app_data(ssl);
15626 #if defined(GCC_DIAGNOSTIC)
15627 #pragma GCC diagnostic pop
15630 const char *servername = SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name);
15634 if ((ctx == NULL) || (conn->phys_ctx == ctx)) {
15635 DEBUG_TRACE(
"%s",
"internal error - assertion failed");
15636 return SSL_TLSEXT_ERR_NOACK;
15645 if ((servername == NULL) || (*servername == 0)) {
15646 DEBUG_TRACE(
"%s",
"SSL connection not supporting SNI");
15647 conn->dom_ctx = &(ctx->dd);
15648 SSL_set_SSL_CTX(ssl, conn->dom_ctx->ssl_ctx);
15649 return SSL_TLSEXT_ERR_NOACK;
15652 DEBUG_TRACE(
"TLS connection to host %s", servername);
15655 if (!mg_strcasecmp(servername, dom->config[AUTHENTICATION_DOMAIN])) {
15658 DEBUG_TRACE(
"TLS domain %s found",
15659 dom->config[AUTHENTICATION_DOMAIN]);
15660 SSL_set_SSL_CTX(ssl, dom->ssl_ctx);
15661 conn->dom_ctx = dom;
15662 return SSL_TLSEXT_ERR_OK;
15668 DEBUG_TRACE(
"TLS default domain %s used",
15669 ctx->dd.config[AUTHENTICATION_DOMAIN]);
15670 conn->dom_ctx = &(ctx->dd);
15671 SSL_set_SSL_CTX(ssl, conn->dom_ctx->ssl_ctx);
15672 return SSL_TLSEXT_ERR_OK;
15678 init_ssl_ctx_impl(
struct mg_context *phys_ctx,
15679 struct mg_domain_context *dom_ctx,
15684 int should_verify_peer;
15685 int peer_certificate_optional;
15686 const char *ca_path;
15687 const char *ca_file;
15688 int use_default_verify_paths;
15690 struct timespec now_mt;
15691 md5_byte_t ssl_context_id[16];
15692 md5_state_t md5state;
15695 #if defined(OPENSSL_API_1_1)
15696 if ((dom_ctx->ssl_ctx = SSL_CTX_new(TLS_server_method())) == NULL) {
15697 mg_cry_internal(fc(phys_ctx),
15698 "SSL_CTX_new (server) error: %s",
15703 if ((dom_ctx->ssl_ctx = SSL_CTX_new(SSLv23_server_method())) == NULL) {
15704 mg_cry_internal(fc(phys_ctx),
15705 "SSL_CTX_new (server) error: %s",
15711 SSL_CTX_clear_options(dom_ctx->ssl_ctx,
15712 SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | SSL_OP_NO_TLSv1
15713 | SSL_OP_NO_TLSv1_1);
15714 protocol_ver = atoi(dom_ctx->config[SSL_PROTOCOL_VERSION]);
15715 SSL_CTX_set_options(dom_ctx->ssl_ctx, ssl_get_protocol(protocol_ver));
15716 SSL_CTX_set_options(dom_ctx->ssl_ctx, SSL_OP_SINGLE_DH_USE);
15717 SSL_CTX_set_options(dom_ctx->ssl_ctx, SSL_OP_CIPHER_SERVER_PREFERENCE);
15718 SSL_CTX_set_options(dom_ctx->ssl_ctx,
15719 SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION);
15720 SSL_CTX_set_options(dom_ctx->ssl_ctx, SSL_OP_NO_COMPRESSION);
15721 #if !defined(NO_SSL_DL)
15722 SSL_CTX_set_ecdh_auto(dom_ctx->ssl_ctx, 1);
15738 SSL_CTX_set_info_callback(dom_ctx->ssl_ctx, ssl_info_callback);
15740 SSL_CTX_set_tlsext_servername_callback(dom_ctx->ssl_ctx,
15741 ssl_servername_callback);
15742 SSL_CTX_set_tlsext_servername_arg(dom_ctx->ssl_ctx, phys_ctx);
15745 callback_ret = (phys_ctx->callbacks.init_ssl == NULL)
15747 : (phys_ctx->callbacks.init_ssl(dom_ctx->ssl_ctx,
15748 phys_ctx->user_data));
15753 if (callback_ret < 0) {
15754 mg_cry_internal(fc(phys_ctx),
15755 "SSL callback returned error: %i",
15759 if (callback_ret > 0) {
15766 md5_init(&md5state);
15767 clock_gettime(CLOCK_MONOTONIC, &now_mt);
15768 md5_append(&md5state, (
const md5_byte_t *)&now_mt,
sizeof(now_mt));
15769 md5_append(&md5state,
15770 (
const md5_byte_t *)phys_ctx->dd.config[LISTENING_PORTS],
15771 strlen(phys_ctx->dd.config[LISTENING_PORTS]));
15772 md5_append(&md5state,
15773 (
const md5_byte_t *)dom_ctx->config[AUTHENTICATION_DOMAIN],
15774 strlen(dom_ctx->config[AUTHENTICATION_DOMAIN]));
15775 md5_append(&md5state, (
const md5_byte_t *)phys_ctx,
sizeof(*phys_ctx));
15776 md5_append(&md5state, (
const md5_byte_t *)dom_ctx,
sizeof(*dom_ctx));
15777 md5_finish(&md5state, ssl_context_id);
15779 SSL_CTX_set_session_id_context(dom_ctx->ssl_ctx,
15780 (
unsigned char *)ssl_context_id,
15781 sizeof(ssl_context_id));
15784 if (!ssl_use_pem_file(phys_ctx, dom_ctx, pem, chain)) {
15791 should_verify_peer = 0;
15792 peer_certificate_optional = 0;
15793 if (dom_ctx->config[SSL_DO_VERIFY_PEER] != NULL) {
15794 if (mg_strcasecmp(dom_ctx->config[SSL_DO_VERIFY_PEER],
"yes") == 0) {
15796 should_verify_peer = 1;
15797 peer_certificate_optional = 0;
15798 }
else if (mg_strcasecmp(dom_ctx->config[SSL_DO_VERIFY_PEER],
15802 should_verify_peer = 1;
15803 peer_certificate_optional = 1;
15807 use_default_verify_paths =
15808 (dom_ctx->config[SSL_DEFAULT_VERIFY_PATHS] != NULL)
15809 && (mg_strcasecmp(dom_ctx->config[SSL_DEFAULT_VERIFY_PATHS],
"yes")
15812 if (should_verify_peer) {
15813 ca_path = dom_ctx->config[SSL_CA_PATH];
15814 ca_file = dom_ctx->config[SSL_CA_FILE];
15815 if (SSL_CTX_load_verify_locations(dom_ctx->ssl_ctx, ca_file, ca_path)
15817 mg_cry_internal(fc(phys_ctx),
15818 "SSL_CTX_load_verify_locations error: %s "
15819 "ssl_verify_peer requires setting "
15820 "either ssl_ca_path or ssl_ca_file. "
15821 "Is any of them present in the "
15827 if (peer_certificate_optional) {
15828 SSL_CTX_set_verify(dom_ctx->ssl_ctx, SSL_VERIFY_PEER, NULL);
15830 SSL_CTX_set_verify(dom_ctx->ssl_ctx,
15832 | SSL_VERIFY_FAIL_IF_NO_PEER_CERT,
15836 if (use_default_verify_paths
15837 && (SSL_CTX_set_default_verify_paths(dom_ctx->ssl_ctx) != 1)) {
15838 mg_cry_internal(fc(phys_ctx),
15839 "SSL_CTX_set_default_verify_paths error: %s",
15844 if (dom_ctx->config[SSL_VERIFY_DEPTH]) {
15845 verify_depth = atoi(dom_ctx->config[SSL_VERIFY_DEPTH]);
15846 SSL_CTX_set_verify_depth(dom_ctx->ssl_ctx, verify_depth);
15850 if (dom_ctx->config[SSL_CIPHER_LIST] != NULL) {
15851 if (SSL_CTX_set_cipher_list(dom_ctx->ssl_ctx,
15852 dom_ctx->config[SSL_CIPHER_LIST])
15854 mg_cry_internal(fc(phys_ctx),
15855 "SSL_CTX_set_cipher_list error: %s",
15868 init_ssl_ctx(
struct mg_context *phys_ctx,
struct mg_domain_context *dom_ctx)
15881 dom_ctx = &(phys_ctx->dd);
15884 if (!is_ssl_port_used(dom_ctx->config[LISTENING_PORTS])) {
15891 (phys_ctx->callbacks.external_ssl_ctx == NULL)
15893 : (phys_ctx->callbacks.external_ssl_ctx(&ssl_ctx,
15894 phys_ctx->user_data));
15896 if (callback_ret < 0) {
15897 mg_cry_internal(fc(phys_ctx),
15898 "external_ssl_ctx callback returned error: %i",
15901 }
else if (callback_ret > 0) {
15902 dom_ctx->ssl_ctx = (SSL_CTX *)ssl_ctx;
15903 if (!initialize_ssl(ebuf,
sizeof(ebuf))) {
15904 mg_cry_internal(fc(phys_ctx),
"%s", ebuf);
15914 if (((pem = dom_ctx->config[SSL_CERTIFICATE]) == NULL)
15915 && (phys_ctx->callbacks.init_ssl == NULL)) {
15919 mg_cry_internal(fc(phys_ctx),
15920 "Initializing SSL failed: -%s is not set",
15921 config_options[SSL_CERTIFICATE].name);
15925 chain = dom_ctx->config[SSL_CERTIFICATE_CHAIN];
15926 if (chain == NULL) {
15929 if ((chain != NULL) && (*chain == 0)) {
15933 if (!initialize_ssl(ebuf,
sizeof(ebuf))) {
15934 mg_cry_internal(fc(phys_ctx),
"%s", ebuf);
15938 return init_ssl_ctx_impl(phys_ctx, dom_ctx, pem, chain);
15943 uninitialize_ssl(
void)
15945 #if defined(OPENSSL_API_1_1)
15947 if (mg_atomic_dec(&cryptolib_users) == 0) {
15953 CONF_modules_unload(1);
15957 if (mg_atomic_dec(&cryptolib_users) == 0) {
15963 CRYPTO_set_locking_callback(NULL);
15964 CRYPTO_set_id_callback(NULL);
15966 CONF_modules_unload(1);
15967 ERR_free_strings();
15969 CRYPTO_cleanup_all_ex_data();
15970 OPENSSL_REMOVE_THREAD_STATE();
15972 for (i = 0; i < CRYPTO_num_locks(); i++) {
15973 pthread_mutex_destroy(&ssl_mutexes[i]);
15975 mg_free(ssl_mutexes);
15976 ssl_mutexes = NULL;
15984 set_gpass_option(
struct mg_context *phys_ctx,
struct mg_domain_context *dom_ctx)
15987 struct mg_file file = STRUCT_FILE_INITIALIZER;
15990 dom_ctx = &(phys_ctx->dd);
15992 path = dom_ctx->config[GLOBAL_PASSWORDS_FILE];
15993 if ((path != NULL) && !mg_stat(fc(phys_ctx), path, &file.stat)) {
15994 mg_cry_internal(fc(phys_ctx),
15995 "Cannot open %s: %s",
16007 set_acl_option(
struct mg_context *phys_ctx)
16009 return check_acl(phys_ctx, (uint32_t)0x7f000001UL) != -1;
16014 reset_per_request_attributes(
struct mg_connection *conn)
16019 conn->connection_type =
16020 CONNECTION_TYPE_INVALID;
16022 conn->num_bytes_sent = conn->consumed_content = 0;
16024 conn->path_info = NULL;
16025 conn->status_code = -1;
16026 conn->content_len = -1;
16027 conn->is_chunked = 0;
16028 conn->must_close = 0;
16029 conn->request_len = 0;
16030 conn->throttle = 0;
16031 conn->data_len = 0;
16032 conn->chunk_remainder = 0;
16033 conn->accept_gzip = 0;
16035 conn->response_info.content_length = conn->request_info.content_length = -1;
16036 conn->response_info.http_version = conn->request_info.http_version = NULL;
16037 conn->response_info.num_headers = conn->request_info.num_headers = 0;
16038 conn->response_info.status_text = NULL;
16039 conn->response_info.status_code = 0;
16041 conn->request_info.remote_user = NULL;
16042 conn->request_info.request_method = NULL;
16043 conn->request_info.request_uri = NULL;
16044 conn->request_info.local_uri = NULL;
16046 #if defined(MG_LEGACY_INTERFACE)
16048 conn->request_info.uri = NULL;
16060 set_sock_timeout(SOCKET sock,
int milliseconds)
16062 int r0 = 0, r1, r2;
16064 #if defined(_WIN32)
16067 DWORD tv = (DWORD)milliseconds;
16081 #if defined(TCP_USER_TIMEOUT)
16082 unsigned int uto = (
unsigned int)milliseconds;
16083 r0 = setsockopt(sock, 6, TCP_USER_TIMEOUT, (
const void *)&uto,
sizeof(uto));
16086 memset(&tv, 0,
sizeof(tv));
16087 tv.tv_sec = milliseconds / 1000;
16088 tv.tv_usec = (milliseconds * 1000) % 1000000;
16093 sock, SOL_SOCKET, SO_RCVTIMEO, (SOCK_OPT_TYPE)&tv,
sizeof(tv));
16095 sock, SOL_SOCKET, SO_SNDTIMEO, (SOCK_OPT_TYPE)&tv,
sizeof(tv));
16097 return r0 || r1 || r2;
16103 set_tcp_nodelay(SOCKET sock,
int nodelay_on)
16105 if (setsockopt(sock,
16108 (SOCK_OPT_TYPE)&nodelay_on,
16109 sizeof(nodelay_on))
16120 close_socket_gracefully(
struct mg_connection *conn)
16122 #if defined(_WIN32)
16123 char buf[MG_BUF_LEN];
16126 struct linger linger;
16127 int error_code = 0;
16128 int linger_timeout = -2;
16129 socklen_t opt_len =
sizeof(error_code);
16138 set_blocking_mode(conn->client.sock);
16141 shutdown(conn->client.sock, SHUTDOWN_WR);
16144 #if defined(_WIN32)
16152 n = pull_inner(NULL, conn, buf,
sizeof(buf), 1.0);
16156 if (conn->dom_ctx->config[LINGER_TIMEOUT]) {
16157 linger_timeout = atoi(conn->dom_ctx->config[LINGER_TIMEOUT]);
16161 if (linger_timeout >= 0) {
16164 linger.l_onoff = 1;
16166 #if defined(_MSC_VER)
16167 #pragma warning(push)
16168 #pragma warning(disable : 4244)
16170 #if defined(GCC_DIAGNOSTIC)
16171 #pragma GCC diagnostic push
16172 #pragma GCC diagnostic ignored "-Wconversion"
16178 linger.l_linger = (linger_timeout + 999) / 1000;
16180 #if defined(GCC_DIAGNOSTIC)
16181 #pragma GCC diagnostic pop
16183 #if defined(_MSC_VER)
16184 #pragma warning(pop)
16188 linger.l_onoff = 0;
16189 linger.l_linger = 0;
16192 if (linger_timeout < -1) {
16194 }
else if (getsockopt(conn->client.sock,
16197 #
if defined(_WIN32)
16198 (
char *)&error_code,
16207 mg_cry_internal(conn,
16208 "%s: getsockopt(SOL_SOCKET SO_ERROR) failed: %s",
16211 }
else if (error_code == ECONNRESET) {
16217 if (setsockopt(conn->client.sock,
16225 "%s: setsockopt(SOL_SOCKET SO_LINGER(%i,%i)) failed: %s",
16234 closesocket(conn->client.sock);
16235 conn->client.sock = INVALID_SOCKET;
16240 close_connection(
struct mg_connection *conn)
16242 #if defined(USE_SERVER_STATS)
16243 conn->conn_state = 6;
16246 #if defined(USE_LUA) && defined(USE_WEBSOCKET)
16247 if (conn->lua_websocket_state) {
16248 lua_websocket_close(conn, conn->lua_websocket_state);
16249 conn->lua_websocket_state = NULL;
16253 mg_lock_connection(conn);
16256 conn->must_close = 1;
16259 if (conn->phys_ctx->callbacks.connection_close != NULL) {
16260 if (conn->phys_ctx->context_type == CONTEXT_SERVER) {
16261 conn->phys_ctx->callbacks.connection_close(conn);
16268 mg_set_user_connection_data(conn, NULL);
16271 #if defined(USE_SERVER_STATS)
16272 conn->conn_state = 7;
16275 #if !defined(NO_SSL)
16276 if (conn->ssl != NULL) {
16279 SSL_shutdown(conn->ssl);
16280 SSL_free(conn->ssl);
16281 OPENSSL_REMOVE_THREAD_STATE();
16285 if (conn->client.sock != INVALID_SOCKET) {
16286 close_socket_gracefully(conn);
16287 conn->client.sock = INVALID_SOCKET;
16291 mg_free((
void *)conn->host);
16295 mg_unlock_connection(conn);
16297 #if defined(USE_SERVER_STATS)
16298 conn->conn_state = 8;
16304 mg_close_connection(
struct mg_connection *conn)
16306 #if defined(USE_WEBSOCKET)
16307 struct mg_context *client_ctx = NULL;
16310 if ((conn == NULL) || (conn->phys_ctx == NULL)) {
16314 #if defined(USE_WEBSOCKET)
16315 if (conn->phys_ctx->context_type == CONTEXT_SERVER) {
16316 if (conn->in_websocket_handling) {
16318 conn->must_close = 1;
16322 if (conn->phys_ctx->context_type == CONTEXT_WS_CLIENT) {
16327 client_ctx = conn->phys_ctx;
16330 client_ctx->stop_flag = 1;
16331 conn->must_close = 1;
16339 for (i = 0; i < client_ctx->cfg_worker_threads; i++) {
16340 if (client_ctx->worker_threadids[i] != 0) {
16341 mg_join_thread(client_ctx->worker_threadids[i]);
16347 close_connection(conn);
16349 #if !defined(NO_SSL)
16350 if (conn->client_ssl_ctx != NULL) {
16351 SSL_CTX_free((SSL_CTX *)conn->client_ssl_ctx);
16355 #if defined(USE_WEBSOCKET)
16356 if (client_ctx != NULL) {
16358 mg_free(client_ctx->worker_threadids);
16359 mg_free(client_ctx);
16360 (void)pthread_mutex_destroy(&conn->mutex);
16362 }
else if (conn->phys_ctx->context_type == CONTEXT_HTTP_CLIENT) {
16366 if (conn->phys_ctx->context_type == CONTEXT_HTTP_CLIENT) {
16374 static struct mg_context common_client_context;
16377 static struct mg_connection *
16378 mg_connect_client_impl(
const struct mg_client_options *client_options,
16383 struct mg_connection *conn = NULL;
16386 struct sockaddr *psa;
16389 unsigned max_req_size =
16390 (unsigned)atoi(config_options[MAX_REQUEST_SIZE].default_value);
16393 size_t conn_size = ((
sizeof(
struct mg_connection) + 7) >> 3) << 3;
16394 size_t ctx_size = ((
sizeof(
struct mg_context) + 7) >> 3) << 3;
16396 conn = (
struct mg_connection *)mg_calloc_ctx(
16397 1, conn_size + ctx_size + max_req_size, &common_client_context);
16399 if (conn == NULL) {
16409 #if defined(GCC_DIAGNOSTIC)
16410 #pragma GCC diagnostic push
16411 #pragma GCC diagnostic ignored "-Wcast-align"
16415 conn->phys_ctx = (
struct mg_context *)(((
char *)conn) + conn_size);
16417 #if defined(GCC_DIAGNOSTIC)
16418 #pragma GCC diagnostic pop
16421 conn->buf = (((
char *)conn) + conn_size + ctx_size);
16422 conn->buf_size = (int)max_req_size;
16423 conn->phys_ctx->context_type = CONTEXT_HTTP_CLIENT;
16424 conn->dom_ctx = &(conn->phys_ctx->dd);
16426 if (!connect_socket(&common_client_context,
16427 client_options->host,
16428 client_options->port,
16440 #if !defined(NO_SSL)
16441 #if defined(OPENSSL_API_1_1)
16443 && (conn->client_ssl_ctx = SSL_CTX_new(TLS_client_method())) == NULL) {
16448 "SSL_CTX_new error");
16455 && (conn->client_ssl_ctx = SSL_CTX_new(SSLv23_client_method()))
16461 "SSL_CTX_new error");
16470 #if defined(USE_IPV6)
16471 len = (sa.sa.sa_family == AF_INET) ?
sizeof(conn->client.rsa.sin)
16472 :
sizeof(conn->client.rsa.sin6);
16473 psa = (sa.sa.sa_family == AF_INET)
16474 ? (
struct sockaddr *)&(conn->client.rsa.sin)
16475 : (
struct sockaddr *)&(conn->client.rsa.sin6);
16477 len =
sizeof(conn->client.rsa.sin);
16478 psa = (
struct sockaddr *)&(conn->client.rsa.sin);
16481 conn->client.sock = sock;
16482 conn->client.lsa = sa;
16484 if (getsockname(sock, psa, &len) != 0) {
16485 mg_cry_internal(conn,
16486 "%s: getsockname() failed: %s",
16491 conn->client.is_ssl = use_ssl ? 1 : 0;
16492 if (0 != pthread_mutex_init(&conn->mutex, &pthread_mutex_attr)) {
16497 "Can not create mutex");
16498 #if !defined(NO_SSL)
16499 SSL_CTX_free(conn->client_ssl_ctx);
16507 #if !defined(NO_SSL)
16509 common_client_context.dd.ssl_ctx = conn->client_ssl_ctx;
16518 if (client_options->client_cert) {
16519 if (!ssl_use_pem_file(&common_client_context,
16520 &(common_client_context.dd),
16521 client_options->client_cert,
16527 "Can not use SSL client certificate");
16528 SSL_CTX_free(conn->client_ssl_ctx);
16535 if (client_options->server_cert) {
16536 SSL_CTX_load_verify_locations(conn->client_ssl_ctx,
16537 client_options->server_cert,
16539 SSL_CTX_set_verify(conn->client_ssl_ctx, SSL_VERIFY_PEER, NULL);
16541 SSL_CTX_set_verify(conn->client_ssl_ctx, SSL_VERIFY_NONE, NULL);
16545 conn->client_ssl_ctx,
16547 &(conn->phys_ctx->stop_flag),
16553 "SSL connection error");
16554 SSL_CTX_free(conn->client_ssl_ctx);
16562 if (0 != set_non_blocking_mode(sock)) {
16563 mg_cry_internal(conn,
16564 "Cannot set non-blocking mode for client %s:%i",
16565 client_options->host,
16566 client_options->port);
16573 CIVETWEB_API
struct mg_connection *
16574 mg_connect_client_secure(
const struct mg_client_options *client_options,
16575 char *error_buffer,
16576 size_t error_buffer_size)
16578 return mg_connect_client_impl(client_options,
16581 error_buffer_size);
16585 struct mg_connection *
16586 mg_connect_client(
const char *host,
16589 char *error_buffer,
16590 size_t error_buffer_size)
16592 struct mg_client_options opts;
16593 memset(&opts, 0,
sizeof(opts));
16596 return mg_connect_client_impl(&opts,
16599 error_buffer_size);
16603 static const struct {
16606 unsigned default_port;
16607 } abs_uri_protocols[] = {{
"http://", 7, 80},
16608 {
"https://", 8, 443},
16610 {
"wss://", 6, 443},
16621 get_uri_type(
const char *uri)
16624 const char *hostend, *portbegin;
16626 unsigned long port;
16632 if ((uri[0] ==
'*') && (uri[1] ==
'\0')) {
16643 for (i = 0; uri[i] != 0; i++) {
16648 if (uri[i] > 126) {
16671 if (uri[0] ==
'/') {
16680 for (i = 0; abs_uri_protocols[i].proto != NULL; i++) {
16681 if (mg_strncasecmp(uri,
16682 abs_uri_protocols[i].proto,
16683 abs_uri_protocols[i].proto_len)
16686 hostend = strchr(uri + abs_uri_protocols[i].proto_len,
'/');
16690 portbegin = strchr(uri + abs_uri_protocols[i].proto_len,
':');
16695 port = strtoul(portbegin + 1, &portend, 10);
16696 if ((portend != hostend) || (port <= 0) || !is_valid_port(port)) {
16709 static const char *
16710 get_rel_url_at_current_server(
const char *uri,
const struct mg_connection *conn)
16712 const char *server_domain;
16713 size_t server_domain_len;
16714 size_t request_domain_len = 0;
16715 unsigned long port = 0;
16716 int i, auth_domain_check_enabled;
16717 const char *hostbegin = NULL;
16718 const char *hostend = NULL;
16719 const char *portbegin;
16722 auth_domain_check_enabled =
16723 !mg_strcasecmp(conn->dom_ctx->config[ENABLE_AUTH_DOMAIN_CHECK],
"yes");
16727 for (i = 0; abs_uri_protocols[i].proto != NULL; i++) {
16728 if (mg_strncasecmp(uri,
16729 abs_uri_protocols[i].proto,
16730 abs_uri_protocols[i].proto_len)
16733 hostbegin = uri + abs_uri_protocols[i].proto_len;
16734 hostend = strchr(hostbegin,
'/');
16738 portbegin = strchr(hostbegin,
':');
16739 if ((!portbegin) || (portbegin > hostend)) {
16740 port = abs_uri_protocols[i].default_port;
16741 request_domain_len = (size_t)(hostend - hostbegin);
16743 port = strtoul(portbegin + 1, &portend, 10);
16744 if ((portend != hostend) || (port <= 0)
16745 || !is_valid_port(port)) {
16748 request_domain_len = (size_t)(portbegin - hostbegin);
16762 #if defined(USE_IPV6)
16763 if (conn->client.lsa.sa.sa_family == AF_INET6) {
16764 if (ntohs(conn->client.lsa.sin6.sin6_port) != port) {
16771 if (ntohs(conn->client.lsa.sin.sin_port) != port) {
16785 if (auth_domain_check_enabled) {
16786 server_domain = conn->dom_ctx->config[AUTHENTICATION_DOMAIN];
16787 server_domain_len = strlen(server_domain);
16788 if ((server_domain_len == 0) || (hostbegin == NULL)) {
16791 if ((request_domain_len == server_domain_len)
16792 && (!memcmp(server_domain, hostbegin, server_domain_len))) {
16795 if (request_domain_len < (server_domain_len + 2)) {
16802 if (hostbegin[request_domain_len - server_domain_len - 1] !=
'.') {
16809 != memcmp(server_domain,
16810 hostbegin + request_domain_len - server_domain_len,
16811 server_domain_len)) {
16824 get_message(
struct mg_connection *conn,
char *ebuf,
size_t ebuf_len,
int *err)
16826 if (ebuf_len > 0) {
16831 reset_per_request_attributes(conn);
16845 clock_gettime(CLOCK_MONOTONIC, &(conn->req_time));
16847 conn->request_len =
16848 read_message(NULL, conn, conn->buf, conn->buf_size, &conn->data_len);
16849 DEBUG_ASSERT(conn->request_len < 0 || conn->data_len >= conn->request_len);
16850 if ((conn->request_len >= 0) && (conn->data_len < conn->request_len)) {
16856 "Invalid message size");
16861 if ((conn->request_len == 0) && (conn->data_len == conn->buf_size)) {
16867 "Message too large");
16872 if (conn->request_len <= 0) {
16873 if (conn->data_len > 0) {
16879 "Malformed message");
16883 conn->must_close = 1;
16889 "No data received");
16899 get_request(
struct mg_connection *conn,
char *ebuf,
size_t ebuf_len,
int *err)
16902 if (!get_message(conn, ebuf, ebuf_len, err)) {
16906 if (parse_http_request(conn->buf, conn->buf_size, &conn->request_info)
16921 conn->host = alloc_get_host(conn);
16928 "Bad request: Host mismatch");
16934 if ((cl = get_header(conn->request_info.http_headers,
16935 conn->request_info.num_headers,
16939 char *endptr = NULL;
16940 conn->content_len = strtoll(cl, &endptr, 10);
16941 if (endptr == cl) {
16952 conn->request_info.content_length = conn->content_len;
16953 }
else if ((cl = get_header(conn->request_info.http_headers,
16954 conn->request_info.num_headers,
16955 "Transfer-Encoding"))
16957 && !mg_strcasecmp(cl,
"chunked")) {
16958 conn->is_chunked = 1;
16959 conn->content_len = -1;
16961 const struct mg_http_method_info *meth =
16962 get_http_method_info(conn->request_info.request_method);
16974 if (meth->request_has_body) {
16976 conn->content_len = -1;
16979 conn->content_len = 0;
16983 conn->connection_type = CONNECTION_TYPE_REQUEST;
16990 get_response(
struct mg_connection *conn,
char *ebuf,
size_t ebuf_len,
int *err)
16993 if (!get_message(conn, ebuf, ebuf_len, err)) {
16997 if (parse_http_response(conn->buf, conn->buf_size, &conn->response_info)
17012 if ((cl = get_header(conn->response_info.http_headers,
17013 conn->response_info.num_headers,
17017 char *endptr = NULL;
17018 conn->content_len = strtoll(cl, &endptr, 10);
17019 if (endptr == cl) {
17030 conn->response_info.content_length = conn->content_len;
17033 conn->request_info.content_length = conn->content_len;
17035 }
else if ((cl = get_header(conn->response_info.http_headers,
17036 conn->response_info.num_headers,
17037 "Transfer-Encoding"))
17039 && !mg_strcasecmp(cl,
"chunked")) {
17040 conn->is_chunked = 1;
17041 conn->content_len = -1;
17043 conn->content_len = -1;
17046 conn->connection_type = CONNECTION_TYPE_RESPONSE;
17052 mg_get_response(
struct mg_connection *conn,
17059 char *save_timeout;
17062 if (ebuf_len > 0) {
17072 "Parameter error");
17077 save_timeout = conn->dom_ctx->config[REQUEST_TIMEOUT];
17079 if (timeout >= 0) {
17080 mg_snprintf(conn, NULL, txt,
sizeof(txt),
"%i", timeout);
17086 new_timeout = NULL;
17089 conn->dom_ctx->config[REQUEST_TIMEOUT] = new_timeout;
17090 ret = get_response(conn, ebuf, ebuf_len, &err);
17091 conn->dom_ctx->config[REQUEST_TIMEOUT] = save_timeout;
17093 #if defined(MG_LEGACY_INTERFACE)
17096 conn->request_info.uri = conn->request_info.request_uri;
17098 conn->request_info.local_uri = conn->request_info.request_uri;
17102 return (ret == 0) ? -1 : +1;
17106 struct mg_connection *
17107 mg_download(
const char *host,
17115 struct mg_connection *conn;
17120 if (ebuf_len > 0) {
17127 conn = mg_connect_client(host, port, use_ssl, ebuf, ebuf_len);
17129 if (conn != NULL) {
17130 i = mg_vprintf(conn, fmt, ap);
17137 "Error sending request");
17139 get_response(conn, ebuf, ebuf_len, &reqerr);
17141 #if defined(MG_LEGACY_INTERFACE)
17144 conn->request_info.uri = conn->request_info.request_uri;
17146 conn->request_info.local_uri = conn->request_info.request_uri;
17151 if ((ebuf[0] !=
'\0') && (conn != NULL)) {
17152 mg_close_connection(conn);
17161 struct websocket_client_thread_data {
17162 struct mg_connection *conn;
17163 mg_websocket_data_handler data_handler;
17164 mg_websocket_close_handler close_handler;
17165 void *callback_data;
17169 #if defined(USE_WEBSOCKET)
17170 #if defined(_WIN32)
17171 static unsigned __stdcall websocket_client_thread(
void *data)
17174 websocket_client_thread(
void *data)
17177 struct websocket_client_thread_data *cdata =
17178 (
struct websocket_client_thread_data *)data;
17180 #if !defined(_WIN32)
17181 struct sigaction sa;
17184 memset(&sa, 0,
sizeof(sa));
17185 sa.sa_handler = SIG_IGN;
17186 sigaction(SIGPIPE, &sa, NULL);
17189 mg_set_thread_name(
"ws-clnt");
17191 if (cdata->conn->phys_ctx) {
17192 if (cdata->conn->phys_ctx->callbacks.init_thread) {
17195 cdata->conn->phys_ctx->callbacks.init_thread(cdata->conn->phys_ctx,
17200 read_websocket(cdata->conn, cdata->data_handler, cdata->callback_data);
17202 DEBUG_TRACE(
"%s",
"Websocket client thread exited\n");
17204 if (cdata->close_handler != NULL) {
17205 cdata->close_handler(cdata->conn, cdata->callback_data);
17210 cdata->conn->phys_ctx->stop_flag = 2;
17212 mg_free((
void *)cdata);
17214 #if defined(_WIN32)
17223 struct mg_connection *
17224 mg_connect_websocket_client(
const char *host,
17227 char *error_buffer,
17228 size_t error_buffer_size,
17230 const char *origin,
17231 mg_websocket_data_handler data_func,
17232 mg_websocket_close_handler close_func,
17235 struct mg_connection *conn = NULL;
17237 #if defined(USE_WEBSOCKET)
17238 struct mg_context *newctx = NULL;
17239 struct websocket_client_thread_data *thread_data;
17240 static const char *magic =
"x3JJHMbDL1EzLkh9GBhXDw==";
17241 static const char *handshake_req;
17243 if (origin != NULL) {
17244 handshake_req =
"GET %s HTTP/1.1\r\n"
17246 "Upgrade: websocket\r\n"
17247 "Connection: Upgrade\r\n"
17248 "Sec-WebSocket-Key: %s\r\n"
17249 "Sec-WebSocket-Version: 13\r\n"
17253 handshake_req =
"GET %s HTTP/1.1\r\n"
17255 "Upgrade: websocket\r\n"
17256 "Connection: Upgrade\r\n"
17257 "Sec-WebSocket-Key: %s\r\n"
17258 "Sec-WebSocket-Version: 13\r\n"
17262 #if defined(__clang__)
17263 #pragma clang diagnostic push
17264 #pragma clang diagnostic ignored "-Wformat-nonliteral"
17268 conn = mg_download(host,
17279 #if defined(__clang__)
17280 #pragma clang diagnostic pop
17284 if (conn == NULL) {
17285 if (!*error_buffer) {
17291 "Unexpected error");
17296 if (conn->response_info.status_code != 101) {
17301 if (!*error_buffer) {
17307 "Unexpected server reply");
17310 DEBUG_TRACE(
"Websocket client connect error: %s\r\n", error_buffer);
17317 newctx = (
struct mg_context *)mg_malloc(
sizeof(
struct mg_context));
17319 DEBUG_TRACE(
"%s\r\n",
"Out of memory");
17324 memcpy(newctx, conn->phys_ctx,
sizeof(
struct mg_context));
17325 newctx->user_data = user_data;
17326 newctx->context_type = CONTEXT_WS_CLIENT;
17327 newctx->cfg_worker_threads = 1;
17328 newctx->worker_threadids =
17329 (pthread_t *)mg_calloc_ctx(newctx->cfg_worker_threads,
17333 conn->phys_ctx = newctx;
17334 conn->dom_ctx = &(newctx->dd);
17336 thread_data = (
struct websocket_client_thread_data *)
17337 mg_calloc_ctx(
sizeof(
struct websocket_client_thread_data), 1, newctx);
17338 if (!thread_data) {
17339 DEBUG_TRACE(
"%s\r\n",
"Out of memory");
17345 thread_data->conn = conn;
17346 thread_data->data_handler = data_func;
17347 thread_data->close_handler = close_func;
17348 thread_data->callback_data = user_data;
17353 if (mg_start_thread_with_id(websocket_client_thread,
17354 (
void *)thread_data,
17355 newctx->worker_threadids)
17357 mg_free((
void *)thread_data);
17358 mg_free((
void *)newctx->worker_threadids);
17359 mg_free((
void *)newctx);
17360 mg_free((
void *)conn);
17363 "Websocket client connect thread could not be started\r\n");
17371 (void)error_buffer;
17372 (void)error_buffer_size;
17386 init_connection(
struct mg_connection *conn)
17389 int keep_alive_enabled =
17390 !mg_strcasecmp(conn->dom_ctx->config[ENABLE_KEEP_ALIVE],
"yes");
17392 if (!keep_alive_enabled) {
17393 conn->must_close = 1;
17398 conn->data_len = 0;
17399 conn->handled_requests = 0;
17400 mg_set_user_connection_data(conn, NULL);
17402 #if defined(USE_SERVER_STATS)
17403 conn->conn_state = 2;
17407 if (conn->phys_ctx->callbacks.init_connection != NULL) {
17408 if (conn->phys_ctx->context_type == CONTEXT_SERVER) {
17409 void *conn_data = NULL;
17410 conn->phys_ctx->callbacks.init_connection(conn, &conn_data);
17411 mg_set_user_connection_data(conn, conn_data);
17423 process_new_connection(
struct mg_connection *conn)
17425 struct mg_request_info *ri = &conn->request_info;
17426 int keep_alive, discard_len;
17428 const char *hostend;
17429 int reqerr, uri_type;
17431 #if defined(USE_SERVER_STATS)
17432 int mcon = mg_atomic_inc(&(conn->phys_ctx->active_connections));
17433 mg_atomic_add(&(conn->phys_ctx->total_connections), 1);
17434 if (mcon > (conn->phys_ctx->max_connections)) {
17437 conn->phys_ctx->max_connections = mcon;
17441 init_connection(conn);
17443 DEBUG_TRACE(
"Start processing connection from %s",
17444 conn->request_info.remote_addr);
17450 DEBUG_TRACE(
"calling get_request (%i times for this connection)",
17451 conn->handled_requests + 1);
17453 #if defined(USE_SERVER_STATS)
17454 conn->conn_state = 3;
17457 if (!get_request(conn, ebuf,
sizeof(ebuf), &reqerr)) {
17462 DEBUG_ASSERT(ebuf[0] !=
'\0');
17463 mg_send_http_error(conn, reqerr,
"%s", ebuf);
17465 }
else if (strcmp(ri->http_version,
"1.0")
17466 && strcmp(ri->http_version,
"1.1")) {
17471 "Bad HTTP version: [%s]",
17473 mg_send_http_error(conn, 505,
"%s", ebuf);
17476 if (ebuf[0] ==
'\0') {
17477 uri_type = get_uri_type(conn->request_info.request_uri);
17478 switch (uri_type) {
17481 conn->request_info.local_uri = NULL;
17485 conn->request_info.local_uri = conn->request_info.request_uri;
17490 hostend = get_rel_url_at_current_server(
17491 conn->request_info.request_uri, conn);
17493 conn->request_info.local_uri = hostend;
17495 conn->request_info.local_uri = NULL;
17504 mg_send_http_error(conn, 400,
"%s", ebuf);
17505 conn->request_info.local_uri = NULL;
17509 #if defined(MG_LEGACY_INTERFACE)
17511 conn->request_info.uri = conn->request_info.local_uri;
17515 DEBUG_TRACE(
"http: %s, error: %s",
17516 (ri->http_version ? ri->http_version :
"none"),
17517 (ebuf[0] ? ebuf :
"none"));
17519 if (ebuf[0] ==
'\0') {
17520 if (conn->request_info.local_uri) {
17523 #if defined(USE_SERVER_STATS)
17524 conn->conn_state = 4;
17526 handle_request(conn);
17528 #if defined(USE_SERVER_STATS)
17529 conn->conn_state = 5;
17531 mg_atomic_add(&(conn->phys_ctx->total_data_read),
17532 conn->consumed_content);
17533 mg_atomic_add(&(conn->phys_ctx->total_data_written),
17534 conn->num_bytes_sent);
17537 DEBUG_TRACE(
"%s",
"handle_request done");
17539 if (conn->phys_ctx->callbacks.end_request != NULL) {
17540 conn->phys_ctx->callbacks.end_request(conn,
17541 conn->status_code);
17542 DEBUG_TRACE(
"%s",
"end_request callback done");
17547 conn->must_close = 1;
17550 conn->must_close = 1;
17553 if (ri->remote_user != NULL) {
17554 mg_free((
void *)ri->remote_user);
17557 ri->remote_user = NULL;
17565 keep_alive = (conn->phys_ctx->stop_flag == 0) && should_keep_alive(conn)
17566 && (conn->content_len >= 0);
17570 discard_len = ((conn->content_len >= 0) && (conn->request_len > 0)
17571 && ((conn->request_len + conn->content_len)
17572 < (int64_t)conn->data_len))
17573 ? (int)(conn->request_len + conn->content_len)
17575 DEBUG_ASSERT(discard_len >= 0);
17576 if (discard_len < 0) {
17577 DEBUG_TRACE(
"internal error: discard_len = %li",
17578 (
long int)discard_len);
17581 conn->data_len -= discard_len;
17582 if (conn->data_len > 0) {
17583 DEBUG_TRACE(
"discard_len = %lu", (
long unsigned)discard_len);
17584 memmove(conn->buf, conn->buf + discard_len, (
size_t)conn->data_len);
17587 DEBUG_ASSERT(conn->data_len >= 0);
17588 DEBUG_ASSERT(conn->data_len <= conn->buf_size);
17590 if ((conn->data_len < 0) || (conn->data_len > conn->buf_size)) {
17591 DEBUG_TRACE(
"internal error: data_len = %li, buf_size = %li",
17592 (
long int)conn->data_len,
17593 (
long int)conn->buf_size);
17597 conn->handled_requests++;
17599 }
while (keep_alive);
17601 DEBUG_TRACE(
"Done processing connection from %s (%f sec)",
17602 conn->request_info.remote_addr,
17603 difftime(time(NULL), conn->conn_birth_time));
17605 close_connection(conn);
17607 #if defined(USE_SERVER_STATS)
17608 mg_atomic_add(&(conn->phys_ctx->total_requests), conn->handled_requests);
17609 mg_atomic_dec(&(conn->phys_ctx->active_connections));
17614 #if defined(ALTERNATIVE_QUEUE)
17617 produce_socket(
struct mg_context *ctx,
const struct socket *sp)
17621 while (!ctx->stop_flag) {
17622 for (i = 0; i < ctx->cfg_worker_threads; i++) {
17624 if (ctx->client_socks[i].in_use == 0) {
17625 ctx->client_socks[i] = *sp;
17626 ctx->client_socks[i].in_use = 1;
17627 event_signal(ctx->client_wait_events[i]);
17638 consume_socket(
struct mg_context *ctx,
struct socket *sp,
int thread_index)
17640 DEBUG_TRACE(
"%s",
"going idle");
17641 ctx->client_socks[thread_index].in_use = 0;
17642 event_wait(ctx->client_wait_events[thread_index]);
17643 *sp = ctx->client_socks[thread_index];
17644 DEBUG_TRACE(
"grabbed socket %d, going busy", sp ? sp->sock : -1);
17646 return !ctx->stop_flag;
17653 consume_socket(
struct mg_context *ctx,
struct socket *sp,
int thread_index)
17655 #define QUEUE_SIZE(ctx) ((int)(ARRAY_SIZE(ctx->queue)))
17657 (void)thread_index;
17659 (void)pthread_mutex_lock(&ctx->thread_mutex);
17660 DEBUG_TRACE(
"%s",
"going idle");
17663 while ((ctx->sq_head == ctx->sq_tail) && (ctx->stop_flag == 0)) {
17664 pthread_cond_wait(&ctx->sq_full, &ctx->thread_mutex);
17668 if (ctx->sq_head > ctx->sq_tail) {
17670 *sp = ctx->queue[ctx->sq_tail % QUEUE_SIZE(ctx)];
17673 DEBUG_TRACE(
"grabbed socket %d, going busy", sp ? sp->sock : -1);
17676 while (ctx->sq_tail > QUEUE_SIZE(ctx)) {
17677 ctx->sq_tail -= QUEUE_SIZE(ctx);
17678 ctx->sq_head -= QUEUE_SIZE(ctx);
17682 (void)pthread_cond_signal(&ctx->sq_empty);
17683 (void)pthread_mutex_unlock(&ctx->thread_mutex);
17685 return !ctx->stop_flag;
17692 produce_socket(
struct mg_context *ctx,
const struct socket *sp)
17694 #define QUEUE_SIZE(ctx) ((int)(ARRAY_SIZE(ctx->queue)))
17698 (void)pthread_mutex_lock(&ctx->thread_mutex);
17701 while ((ctx->stop_flag == 0)
17702 && (ctx->sq_head - ctx->sq_tail >= QUEUE_SIZE(ctx))) {
17703 (void)pthread_cond_wait(&ctx->sq_empty, &ctx->thread_mutex);
17706 if (ctx->sq_head - ctx->sq_tail < QUEUE_SIZE(ctx)) {
17708 ctx->queue[ctx->sq_head % QUEUE_SIZE(ctx)] = *sp;
17710 DEBUG_TRACE(
"queued socket %d", sp ? sp->sock : -1);
17713 (void)pthread_cond_signal(&ctx->sq_full);
17714 (void)pthread_mutex_unlock(&ctx->thread_mutex);
17720 struct worker_thread_args {
17721 struct mg_context *ctx;
17727 worker_thread_run(
struct worker_thread_args *thread_args)
17729 struct mg_context *ctx = thread_args->ctx;
17730 struct mg_connection *conn;
17731 struct mg_workerTLS tls;
17732 #if defined(MG_LEGACY_INTERFACE)
17736 mg_set_thread_name(
"worker");
17739 tls.thread_idx = (unsigned)mg_atomic_inc(&thread_idx_max);
17740 #if defined(_WIN32)
17741 tls.pthread_cond_helper_mutex = CreateEvent(NULL, FALSE, FALSE, NULL);
17745 pthread_setspecific(sTlsKey, &tls);
17747 if (ctx->callbacks.init_thread) {
17749 ctx->callbacks.init_thread(ctx, 1);
17753 if (((
int)thread_args->index < 0)
17754 || ((
unsigned)thread_args->index
17755 >= (
unsigned)ctx->cfg_worker_threads)) {
17756 mg_cry_internal(fc(ctx),
17757 "Internal error: Invalid worker index %i",
17758 (
int)thread_args->index);
17761 conn = ctx->worker_connections + thread_args->index;
17766 conn->buf = (
char *)mg_malloc_ctx(ctx->max_request_size, conn->phys_ctx);
17767 if (conn->buf == NULL) {
17768 mg_cry_internal(fc(ctx),
17769 "Out of memory: Cannot allocate buffer for worker %i",
17770 (
int)thread_args->index);
17773 conn->buf_size = (int)ctx->max_request_size;
17775 conn->phys_ctx = ctx;
17776 conn->dom_ctx = &(ctx->dd);
17779 conn->thread_index = thread_args->index;
17780 conn->request_info.user_data = ctx->user_data;
17784 if (0 != pthread_mutex_init(&conn->mutex, &pthread_mutex_attr)) {
17785 mg_free(conn->buf);
17786 mg_cry_internal(fc(ctx),
"%s",
"Cannot create mutex");
17790 #if defined(USE_SERVER_STATS)
17791 conn->conn_state = 1;
17794 #if defined(ALTERNATIVE_QUEUE)
17795 while ((ctx->stop_flag == 0)
17796 && consume_socket(ctx, &conn->client, conn->thread_index)) {
17801 while (consume_socket(ctx, &conn->client, conn->thread_index)) {
17804 conn->conn_birth_time = time(NULL);
17810 #if defined(USE_IPV6)
17811 if (conn->client.rsa.sa.sa_family == AF_INET6) {
17812 conn->request_info.remote_port =
17813 ntohs(conn->client.rsa.sin6.sin6_port);
17817 conn->request_info.remote_port =
17818 ntohs(conn->client.rsa.sin.sin_port);
17821 sockaddr_to_string(conn->request_info.remote_addr,
17822 sizeof(conn->request_info.remote_addr),
17823 &conn->client.rsa);
17825 DEBUG_TRACE(
"Start processing connection from %s",
17826 conn->request_info.remote_addr);
17828 conn->request_info.is_ssl = conn->client.is_ssl;
17830 if (conn->client.is_ssl) {
17831 #if !defined(NO_SSL)
17834 conn->dom_ctx->ssl_ctx,
17836 &(conn->phys_ctx->stop_flag),
17841 ssl_get_client_cert_info(conn);
17844 process_new_connection(conn);
17847 if (conn->request_info.client_cert) {
17848 mg_free((
void *)(conn->request_info.client_cert->subject));
17849 mg_free((
void *)(conn->request_info.client_cert->issuer));
17850 mg_free((
void *)(conn->request_info.client_cert->serial));
17851 mg_free((
void *)(conn->request_info.client_cert->finger));
17854 (X509 *)conn->request_info.client_cert->peer_cert);
17855 conn->request_info.client_cert->peer_cert = 0;
17856 conn->request_info.client_cert->subject = 0;
17857 conn->request_info.client_cert->issuer = 0;
17858 conn->request_info.client_cert->serial = 0;
17859 conn->request_info.client_cert->finger = 0;
17860 mg_free(conn->request_info.client_cert);
17861 conn->request_info.client_cert = 0;
17865 close_connection(conn);
17870 process_new_connection(conn);
17873 DEBUG_TRACE(
"%s",
"Connection closed");
17877 pthread_setspecific(sTlsKey, NULL);
17878 #if defined(_WIN32)
17879 CloseHandle(tls.pthread_cond_helper_mutex);
17881 pthread_mutex_destroy(&conn->mutex);
17884 conn->buf_size = 0;
17885 mg_free(conn->buf);
17888 #if defined(USE_SERVER_STATS)
17889 conn->conn_state = 9;
17892 DEBUG_TRACE(
"%s",
"exiting");
17898 #if defined(_WIN32)
17899 static unsigned __stdcall worker_thread(
void *thread_func_param)
17901 struct worker_thread_args *pwta =
17902 (
struct worker_thread_args *)thread_func_param;
17903 worker_thread_run(pwta);
17904 mg_free(thread_func_param);
17909 worker_thread(
void *thread_func_param)
17911 struct worker_thread_args *pwta =
17912 (
struct worker_thread_args *)thread_func_param;
17913 struct sigaction sa;
17916 memset(&sa, 0,
sizeof(sa));
17917 sa.sa_handler = SIG_IGN;
17918 sigaction(SIGPIPE, &sa, NULL);
17920 worker_thread_run(pwta);
17921 mg_free(thread_func_param);
17930 accept_new_connection(
const struct socket *listener,
struct mg_context *ctx)
17933 char src_addr[IP_ADDR_STR_LEN];
17934 socklen_t len =
sizeof(so.rsa);
17937 if ((so.sock = accept(listener->sock, &so.rsa.sa, &len))
17938 == INVALID_SOCKET) {
17939 }
else if (!check_acl(ctx, ntohl(*(uint32_t *)&so.rsa.sin.sin_addr))) {
17940 sockaddr_to_string(src_addr,
sizeof(src_addr), &so.rsa);
17941 mg_cry_internal(fc(ctx),
17942 "%s: %s is not allowed to connect",
17945 closesocket(so.sock);
17948 DEBUG_TRACE(
"Accepted socket %d", (
int)so.sock);
17949 set_close_on_exec(so.sock, fc(ctx));
17950 so.is_ssl = listener->is_ssl;
17951 so.ssl_redir = listener->ssl_redir;
17952 if (getsockname(so.sock, &so.lsa.sa, &len) != 0) {
17953 mg_cry_internal(fc(ctx),
17954 "%s: getsockname() failed: %s",
17966 if (setsockopt(so.sock,
17969 (SOCK_OPT_TYPE)&on,
17974 "%s: setsockopt(SOL_SOCKET SO_KEEPALIVE) failed: %s",
17986 if ((ctx->dd.config[CONFIG_TCP_NODELAY] != NULL)
17987 && (!strcmp(ctx->dd.config[CONFIG_TCP_NODELAY],
"1"))) {
17988 if (set_tcp_nodelay(so.sock, 1) != 0) {
17991 "%s: setsockopt(IPPROTO_TCP TCP_NODELAY) failed: %s",
18004 set_non_blocking_mode(so.sock);
18007 produce_socket(ctx, &so);
18013 master_thread_run(
void *thread_func_param)
18015 struct mg_context *ctx = (
struct mg_context *)thread_func_param;
18016 struct mg_workerTLS tls;
18017 struct pollfd *pfd;
18019 unsigned int workerthreadcount;
18025 mg_set_thread_name(
"master");
18028 #if defined(_WIN32)
18029 SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_ABOVE_NORMAL);
18030 #elif defined(USE_MASTER_THREAD_PRIORITY)
18031 int min_prio = sched_get_priority_min(SCHED_RR);
18032 int max_prio = sched_get_priority_max(SCHED_RR);
18033 if ((min_prio >= 0) && (max_prio >= 0)
18034 && ((USE_MASTER_THREAD_PRIORITY) <= max_prio)
18035 && ((USE_MASTER_THREAD_PRIORITY) >= min_prio)) {
18036 struct sched_param sched_param = {0};
18037 sched_param.sched_priority = (USE_MASTER_THREAD_PRIORITY);
18038 pthread_setschedparam(pthread_self(), SCHED_RR, &sched_param);
18043 #if defined(_WIN32)
18044 tls.pthread_cond_helper_mutex = CreateEvent(NULL, FALSE, FALSE, NULL);
18047 pthread_setspecific(sTlsKey, &tls);
18049 if (ctx->callbacks.init_thread) {
18051 ctx->callbacks.init_thread(ctx, 0);
18055 ctx->start_time = time(NULL);
18058 pfd = ctx->listening_socket_fds;
18059 while (ctx->stop_flag == 0) {
18060 for (i = 0; i < ctx->num_listening_sockets; i++) {
18061 pfd[i].fd = ctx->listening_sockets[i].sock;
18062 pfd[i].events = POLLIN;
18065 if (poll(pfd, ctx->num_listening_sockets, 200) > 0) {
18066 for (i = 0; i < ctx->num_listening_sockets; i++) {
18072 if ((ctx->stop_flag == 0) && (pfd[i].revents & POLLIN)) {
18073 accept_new_connection(&ctx->listening_sockets[i], ctx);
18080 DEBUG_TRACE(
"%s",
"stopping workers");
18083 close_all_listening_sockets(ctx);
18086 (void)pthread_mutex_lock(&ctx->thread_mutex);
18087 #if defined(ALTERNATIVE_QUEUE)
18088 for (i = 0; i < ctx->cfg_worker_threads; i++) {
18089 event_signal(ctx->client_wait_events[i]);
18092 if (ctx->client_socks[i].in_use) {
18093 shutdown(ctx->client_socks[i].sock, SHUTDOWN_BOTH);
18097 pthread_cond_broadcast(&ctx->sq_full);
18099 (void)pthread_mutex_unlock(&ctx->thread_mutex);
18102 workerthreadcount = ctx->cfg_worker_threads;
18103 for (i = 0; i < workerthreadcount; i++) {
18104 if (ctx->worker_threadids[i] != 0) {
18105 mg_join_thread(ctx->worker_threadids[i]);
18109 #if defined(USE_LUA)
18111 if (ctx->lua_background_state) {
18112 lua_State *lstate = (lua_State *)ctx->lua_background_state;
18113 lua_getglobal(lstate, LUABACKGROUNDPARAMS);
18114 if (lua_istable(lstate, -1)) {
18115 reg_boolean(lstate,
"shutdown", 1);
18116 lua_pop(lstate, 1);
18120 ctx->lua_background_state = 0;
18124 DEBUG_TRACE(
"%s",
"exiting");
18126 #if defined(_WIN32)
18127 CloseHandle(tls.pthread_cond_helper_mutex);
18129 pthread_setspecific(sTlsKey, NULL);
18134 ctx->stop_flag = 2;
18139 #if defined(_WIN32)
18140 static unsigned __stdcall master_thread(
void *thread_func_param)
18142 master_thread_run(thread_func_param);
18147 master_thread(
void *thread_func_param)
18149 struct sigaction sa;
18152 memset(&sa, 0,
sizeof(sa));
18153 sa.sa_handler = SIG_IGN;
18154 sigaction(SIGPIPE, &sa, NULL);
18156 master_thread_run(thread_func_param);
18163 free_context(
struct mg_context *ctx)
18166 struct mg_handler_info *tmp_rh;
18172 if (ctx->callbacks.exit_context) {
18173 ctx->callbacks.exit_context(ctx);
18179 (void)pthread_mutex_destroy(&ctx->thread_mutex);
18180 #if defined(ALTERNATIVE_QUEUE)
18181 mg_free(ctx->client_socks);
18182 for (i = 0; (unsigned)i < ctx->cfg_worker_threads; i++) {
18183 event_destroy(ctx->client_wait_events[i]);
18185 mg_free(ctx->client_wait_events);
18187 (void)pthread_cond_destroy(&ctx->sq_empty);
18188 (void)pthread_cond_destroy(&ctx->sq_full);
18192 (void)pthread_mutex_destroy(&ctx->nonce_mutex);
18194 #if defined(USE_TIMERS)
18199 for (i = 0; i < NUM_OPTIONS; i++) {
18200 if (ctx->dd.config[i] != NULL) {
18201 #if defined(_MSC_VER)
18202 #pragma warning(suppress : 6001)
18204 mg_free(ctx->dd.config[i]);
18209 while (ctx->dd.handlers) {
18210 tmp_rh = ctx->dd.handlers;
18211 ctx->dd.handlers = tmp_rh->next;
18212 if (tmp_rh->handler_type == REQUEST_HANDLER) {
18213 pthread_cond_destroy(&tmp_rh->refcount_cond);
18214 pthread_mutex_destroy(&tmp_rh->refcount_mutex);
18216 mg_free(tmp_rh->uri);
18220 #if !defined(NO_SSL)
18222 if (ctx->dd.ssl_ctx != NULL) {
18223 void *ssl_ctx = (
void *)ctx->dd.ssl_ctx;
18225 (ctx->callbacks.external_ssl_ctx == NULL)
18227 : (ctx->callbacks.external_ssl_ctx(&ssl_ctx, ctx->user_data));
18229 if (callback_ret == 0) {
18230 SSL_CTX_free(ctx->dd.ssl_ctx);
18238 if (ctx->worker_threadids != NULL) {
18239 mg_free(ctx->worker_threadids);
18243 if (ctx->worker_connections != NULL) {
18244 mg_free(ctx->worker_connections);
18248 mg_free(ctx->systemName);
18256 mg_stop(
struct mg_context *ctx)
18265 mt = ctx->masterthreadid;
18270 ctx->masterthreadid = 0;
18273 ctx->stop_flag = 1;
18276 while (ctx->stop_flag != 2) {
18277 (void)mg_sleep(10);
18280 mg_join_thread(mt);
18283 #if defined(_WIN32)
18284 (void)WSACleanup();
18290 get_system_name(
char **sysName)
18292 #if defined(_WIN32)
18293 #if !defined(__SYMBIAN32__)
18294 #if defined(_WIN32_WCE)
18295 *sysName = mg_strdup(
"WinCE");
18298 DWORD dwVersion = 0;
18299 DWORD dwMajorVersion = 0;
18300 DWORD dwMinorVersion = 0;
18302 BOOL wowRet, isWoW = FALSE;
18304 #if defined(_MSC_VER)
18305 #pragma warning(push)
18307 #pragma warning(disable : 4996)
18309 dwVersion = GetVersion();
18310 #if defined(_MSC_VER)
18311 #pragma warning(pop)
18314 dwMajorVersion = (DWORD)(LOBYTE(LOWORD(dwVersion)));
18315 dwMinorVersion = (DWORD)(HIBYTE(LOWORD(dwVersion)));
18316 dwBuild = ((dwVersion < 0x80000000) ? (DWORD)(HIWORD(dwVersion)) : 0);
18319 wowRet = IsWow64Process(GetCurrentProcess(), &isWoW);
18323 (
unsigned)dwMajorVersion,
18324 (
unsigned)dwMinorVersion,
18325 (wowRet ? (isWoW ?
" (WoW64)" :
"") :
" (?)"));
18327 *sysName = mg_strdup(name);
18330 *sysName = mg_strdup(
"Symbian");
18333 struct utsname name;
18334 memset(&name, 0,
sizeof(name));
18336 *sysName = mg_strdup(name.sysname);
18341 struct mg_context *
18342 mg_start(
const struct mg_callbacks *callbacks,
18344 const char **options)
18346 struct mg_context *ctx;
18347 const char *name, *value, *default_value;
18348 int idx, ok, workerthreadcount;
18351 void (*exit_callback)(
const struct mg_context *ctx) = 0;
18353 struct mg_workerTLS tls;
18355 #if defined(_WIN32)
18357 WSAStartup(MAKEWORD(2, 2), &data);
18361 if ((ctx = (
struct mg_context *)mg_calloc(1,
sizeof(*ctx))) == NULL) {
18366 ctx->dd.auth_nonce_mask =
18367 (uint64_t)get_random() ^ (uint64_t)(ptrdiff_t)(options);
18369 if (mg_init_library_called == 0) {
18372 const char *ports_option =
18373 config_options[LISTENING_PORTS].default_value;
18376 const char **run_options = options;
18377 const char *optname = config_options[LISTENING_PORTS].name;
18380 while (*run_options) {
18381 if (!strcmp(*run_options, optname)) {
18382 ports_option = run_options[1];
18388 if (is_ssl_port_used(ports_option)) {
18390 mg_init_library(MG_FEATURES_TLS);
18393 mg_init_library(MG_FEATURES_DEFAULT);
18397 tls.is_master = -1;
18398 tls.thread_idx = (unsigned)mg_atomic_inc(&thread_idx_max);
18399 #if defined(_WIN32)
18400 tls.pthread_cond_helper_mutex = NULL;
18402 pthread_setspecific(sTlsKey, &tls);
18404 ok = (0 == pthread_mutex_init(&ctx->thread_mutex, &pthread_mutex_attr));
18405 #if !defined(ALTERNATIVE_QUEUE)
18406 ok &= (0 == pthread_cond_init(&ctx->sq_empty, NULL));
18407 ok &= (0 == pthread_cond_init(&ctx->sq_full, NULL));
18409 ok &= (0 == pthread_mutex_init(&ctx->nonce_mutex, &pthread_mutex_attr));
18413 mg_cry_internal(fc(ctx),
18415 "Cannot initialize thread synchronization objects");
18417 pthread_setspecific(sTlsKey, NULL);
18422 ctx->callbacks = *callbacks;
18423 exit_callback = callbacks->exit_context;
18424 ctx->callbacks.exit_context = 0;
18426 ctx->user_data = user_data;
18427 ctx->dd.handlers = NULL;
18428 ctx->dd.next = NULL;
18430 #if defined(USE_LUA) && defined(USE_WEBSOCKET)
18431 ctx->dd.shared_lua_websockets = NULL;
18435 while (options && (name = *options++) != NULL) {
18436 if ((idx = get_option_index(name)) == -1) {
18437 mg_cry_internal(fc(ctx),
"Invalid option: %s", name);
18439 pthread_setspecific(sTlsKey, NULL);
18441 }
else if ((value = *options++) == NULL) {
18442 mg_cry_internal(fc(ctx),
"%s: option value cannot be NULL", name);
18444 pthread_setspecific(sTlsKey, NULL);
18447 if (ctx->dd.config[idx] != NULL) {
18448 mg_cry_internal(fc(ctx),
"warning: %s: duplicate option", name);
18449 mg_free(ctx->dd.config[idx]);
18451 ctx->dd.config[idx] = mg_strdup_ctx(value, ctx);
18452 DEBUG_TRACE(
"[%s] -> [%s]", name, value);
18456 for (i = 0; config_options[i].name != NULL; i++) {
18457 default_value = config_options[i].default_value;
18458 if ((ctx->dd.config[i] == NULL) && (default_value != NULL)) {
18459 ctx->dd.config[i] = mg_strdup_ctx(default_value, ctx);
18464 itmp = atoi(ctx->dd.config[MAX_REQUEST_SIZE]);
18466 mg_cry_internal(fc(ctx),
"%s",
"max_request_size too small");
18468 pthread_setspecific(sTlsKey, NULL);
18471 ctx->max_request_size = (unsigned)itmp;
18474 workerthreadcount = atoi(ctx->dd.config[NUM_THREADS]);
18476 if (workerthreadcount > MAX_WORKER_THREADS) {
18477 mg_cry_internal(fc(ctx),
"%s",
"Too many worker threads");
18479 pthread_setspecific(sTlsKey, NULL);
18483 if (workerthreadcount <= 0) {
18484 mg_cry_internal(fc(ctx),
"%s",
"Invalid number of worker threads");
18486 pthread_setspecific(sTlsKey, NULL);
18491 #if defined(NO_FILES)
18492 if (ctx->dd.config[DOCUMENT_ROOT] != NULL) {
18493 mg_cry_internal(fc(ctx),
"%s",
"Document root must not be set");
18495 pthread_setspecific(sTlsKey, NULL);
18500 get_system_name(&ctx->systemName);
18502 #if defined(USE_LUA)
18504 if (ctx->dd.config[LUA_BACKGROUND_SCRIPT] != NULL) {
18506 struct vec opt_vec;
18508 const char *sparams;
18509 lua_State *state = mg_prepare_lua_context_script(
18510 ctx->dd.config[LUA_BACKGROUND_SCRIPT], ctx, ebuf,
sizeof(ebuf));
18512 mg_cry_internal(fc(ctx),
"lua_background_script error: %s", ebuf);
18514 pthread_setspecific(sTlsKey, NULL);
18517 ctx->lua_background_state = (
void *)state;
18519 lua_newtable(state);
18520 reg_boolean(state,
"shutdown", 0);
18522 sparams = ctx->dd.config[LUA_BACKGROUND_SCRIPT_PARAMS];
18524 while ((sparams = next_option(sparams, &opt_vec, &eq_vec)) != NULL) {
18526 state, opt_vec.ptr, opt_vec.len, eq_vec.ptr, eq_vec.len);
18527 if (mg_strncasecmp(sparams, opt_vec.ptr, opt_vec.len) == 0)
18530 lua_setglobal(state, LUABACKGROUNDPARAMS);
18533 ctx->lua_background_state = 0;
18539 if (!set_gpass_option(ctx, NULL) ||
18540 #
if !defined(NO_SSL)
18541 !init_ssl_ctx(ctx, NULL) ||
18543 !set_ports_option(ctx) ||
18544 #
if !defined(_WIN32)
18545 !set_uid_option(ctx) ||
18547 !set_acl_option(ctx)) {
18549 pthread_setspecific(sTlsKey, NULL);
18553 ctx->cfg_worker_threads = ((
unsigned int)(workerthreadcount));
18554 ctx->worker_threadids = (pthread_t *)mg_calloc_ctx(ctx->cfg_worker_threads,
18558 if (ctx->worker_threadids == NULL) {
18559 mg_cry_internal(fc(ctx),
18561 "Not enough memory for worker thread ID array");
18563 pthread_setspecific(sTlsKey, NULL);
18566 ctx->worker_connections =
18567 (
struct mg_connection *)mg_calloc_ctx(ctx->cfg_worker_threads,
18568 sizeof(
struct mg_connection),
18570 if (ctx->worker_connections == NULL) {
18571 mg_cry_internal(fc(ctx),
18573 "Not enough memory for worker thread connection array");
18575 pthread_setspecific(sTlsKey, NULL);
18580 #if defined(ALTERNATIVE_QUEUE)
18581 ctx->client_wait_events =
18582 (
void **)mg_calloc_ctx(
sizeof(ctx->client_wait_events[0]),
18583 ctx->cfg_worker_threads,
18585 if (ctx->client_wait_events == NULL) {
18586 mg_cry_internal(fc(ctx),
18588 "Not enough memory for worker event array");
18589 mg_free(ctx->worker_threadids);
18591 pthread_setspecific(sTlsKey, NULL);
18595 ctx->client_socks =
18596 (
struct socket *)mg_calloc_ctx(
sizeof(ctx->client_socks[0]),
18597 ctx->cfg_worker_threads,
18599 if (ctx->client_socks == NULL) {
18600 mg_cry_internal(fc(ctx),
18602 "Not enough memory for worker socket array");
18603 mg_free(ctx->client_wait_events);
18604 mg_free(ctx->worker_threadids);
18606 pthread_setspecific(sTlsKey, NULL);
18610 for (i = 0; (unsigned)i < ctx->cfg_worker_threads; i++) {
18611 ctx->client_wait_events[i] = event_create();
18612 if (ctx->client_wait_events[i] == 0) {
18613 mg_cry_internal(fc(ctx),
"Error creating worker event %i", i);
18616 event_destroy(ctx->client_wait_events[i]);
18618 mg_free(ctx->client_socks);
18619 mg_free(ctx->client_wait_events);
18620 mg_free(ctx->worker_threadids);
18622 pthread_setspecific(sTlsKey, NULL);
18629 #if defined(USE_TIMERS)
18630 if (timers_init(ctx) != 0) {
18631 mg_cry_internal(fc(ctx),
"%s",
"Error creating timers");
18633 pthread_setspecific(sTlsKey, NULL);
18639 if (ctx->callbacks.init_context) {
18640 ctx->callbacks.init_context(ctx);
18642 ctx->callbacks.exit_context = exit_callback;
18643 ctx->context_type = CONTEXT_SERVER;
18646 mg_start_thread_with_id(master_thread, ctx, &ctx->masterthreadid);
18649 for (i = 0; i < ctx->cfg_worker_threads; i++) {
18650 struct worker_thread_args *wta = (
struct worker_thread_args *)
18651 mg_malloc_ctx(
sizeof(
struct worker_thread_args), ctx);
18654 wta->index = (int)i;
18658 || (mg_start_thread_with_id(worker_thread,
18660 &ctx->worker_threadids[i])
18669 mg_cry_internal(fc(ctx),
18670 "Cannot start worker thread %i: error %ld",
18674 mg_cry_internal(fc(ctx),
18675 "Cannot create threads: error %ld",
18678 pthread_setspecific(sTlsKey, NULL);
18685 pthread_setspecific(sTlsKey, NULL);
18690 #if defined(MG_EXPERIMENTAL_INTERFACES)
18693 mg_start_domain(
struct mg_context *ctx,
const char **options)
18697 const char *default_value;
18698 struct mg_domain_context *new_dom;
18699 struct mg_domain_context *dom;
18702 if ((ctx == NULL) || (ctx->stop_flag != 0) || (options == NULL)) {
18706 new_dom = (
struct mg_domain_context *)
18707 mg_calloc_ctx(1,
sizeof(
struct mg_domain_context), ctx);
18715 while (options && (name = *options++) != NULL) {
18716 if ((idx = get_option_index(name)) == -1) {
18717 mg_cry_internal(fc(ctx),
"Invalid option: %s", name);
18720 }
else if ((value = *options++) == NULL) {
18721 mg_cry_internal(fc(ctx),
"%s: option value cannot be NULL", name);
18725 if (new_dom->config[idx] != NULL) {
18726 mg_cry_internal(fc(ctx),
"warning: %s: duplicate option", name);
18727 mg_free(new_dom->config[idx]);
18729 new_dom->config[idx] = mg_strdup_ctx(value, ctx);
18730 DEBUG_TRACE(
"[%s] -> [%s]", name, value);
18735 if (!new_dom->config[AUTHENTICATION_DOMAIN]) {
18736 mg_cry_internal(fc(ctx),
"%s",
"authentication domain required");
18743 for (i = 0; config_options[i].name != NULL; i++) {
18744 default_value = ctx->dd.config[i];
18745 if ((new_dom->config[i] == NULL) && (default_value != NULL)) {
18746 new_dom->config[i] = mg_strdup_ctx(default_value, ctx);
18750 new_dom->handlers = NULL;
18751 new_dom->next = NULL;
18752 new_dom->nonce_count = 0;
18753 new_dom->auth_nonce_mask =
18754 (uint64_t)get_random() ^ ((uint64_t)get_random() << 31);
18756 #if defined(USE_LUA) && defined(USE_WEBSOCKET)
18757 new_dom->shared_lua_websockets = NULL;
18760 if (!init_ssl_ctx(ctx, new_dom)) {
18767 mg_lock_context(ctx);
18772 if (!strcasecmp(new_dom->config[AUTHENTICATION_DOMAIN],
18773 dom->config[AUTHENTICATION_DOMAIN])) {
18775 mg_cry_internal(fc(ctx),
18776 "domain %s already in use",
18777 new_dom->config[AUTHENTICATION_DOMAIN]);
18785 if (dom->next == NULL) {
18786 dom->next = new_dom;
18792 mg_unlock_context(ctx);
18802 mg_check_feature(
unsigned feature)
18804 static const unsigned feature_set = 0
18808 #if !defined(NO_FILES)
18809 | MG_FEATURES_FILES
18811 #if !defined(NO_SSL)
18814 #if !defined(NO_CGI)
18817 #if defined(USE_IPV6)
18820 #if defined(USE_WEBSOCKET)
18821 | MG_FEATURES_WEBSOCKET
18823 #if defined(USE_LUA)
18826 #if defined(USE_DUKTAPE)
18829 #if !defined(NO_CACHING)
18830 | MG_FEATURES_CACHE
18832 #if defined(USE_SERVER_STATS)
18833 | MG_FEATURES_STATS
18835 #if defined(USE_ZLIB)
18836 | MG_FEATURES_COMPRESSION
18841 #if defined(MG_LEGACY_INTERFACE)
18844 #if defined(MG_EXPERIMENTAL_INTERFACES)
18847 #if defined(MEMORY_DEBUGGING)
18850 #if defined(USE_TIMERS)
18853 #if !defined(NO_NONCE_CHECK)
18856 #if !defined(NO_POPEN)
18860 return (feature & feature_set);
18865 #define strcat0(a, b) \
18867 if ((a != NULL) && (b != NULL)) { \
18876 mg_get_system_info_impl(
char *buffer,
int buflen)
18879 int system_info_length = 0;
18881 #if defined(_WIN32)
18882 const char *eol =
"\r\n";
18884 const char *eol =
"\n";
18887 const char *eoobj =
"}";
18888 int reserved_len = (int)strlen(eoobj) + (int)strlen(eol);
18890 if ((buffer == NULL) || (buflen < 1)) {
18896 mg_snprintf(NULL, NULL, block,
sizeof(block),
"{%s", eol);
18897 system_info_length += (int)strlen(block);
18898 if (system_info_length < buflen) {
18899 strcat0(buffer, block);
18904 const char *version = mg_version();
18909 "\"version\" : \"%s\",%s",
18912 system_info_length += (int)strlen(block);
18913 if (system_info_length < buflen) {
18914 strcat0(buffer, block);
18920 #if defined(_WIN32)
18921 DWORD dwVersion = 0;
18922 DWORD dwMajorVersion = 0;
18923 DWORD dwMinorVersion = 0;
18926 GetSystemInfo(&si);
18928 #if defined(_MSC_VER)
18929 #pragma warning(push)
18931 #pragma warning(disable : 4996)
18933 dwVersion = GetVersion();
18934 #if defined(_MSC_VER)
18935 #pragma warning(pop)
18938 dwMajorVersion = (DWORD)(LOBYTE(LOWORD(dwVersion)));
18939 dwMinorVersion = (DWORD)(HIBYTE(LOWORD(dwVersion)));
18945 "\"os\" : \"Windows %u.%u\",%s",
18946 (
unsigned)dwMajorVersion,
18947 (
unsigned)dwMinorVersion,
18949 system_info_length += (int)strlen(block);
18950 if (system_info_length < buflen) {
18951 strcat0(buffer, block);
18958 "\"cpu\" : \"type %u, cores %u, mask %x\",%s",
18959 (
unsigned)si.wProcessorArchitecture,
18960 (
unsigned)si.dwNumberOfProcessors,
18961 (
unsigned)si.dwActiveProcessorMask,
18963 system_info_length += (int)strlen(block);
18964 if (system_info_length < buflen) {
18965 strcat0(buffer, block);
18968 struct utsname name;
18969 memset(&name, 0,
sizeof(name));
18976 "\"os\" : \"%s %s (%s) - %s\",%s",
18982 system_info_length += (int)strlen(block);
18983 if (system_info_length < buflen) {
18984 strcat0(buffer, block);
18995 "\"features\" : %lu,%s"
18996 "\"feature_list\" : \"Server:%s%s%s%s%s%s%s%s%s\",%s",
18997 (
unsigned long)mg_check_feature(0xFFFFFFFFu),
18999 mg_check_feature(MG_FEATURES_FILES) ?
" Files" :
"",
19000 mg_check_feature(MG_FEATURES_SSL) ?
" HTTPS" :
"",
19001 mg_check_feature(MG_FEATURES_CGI) ?
" CGI" :
"",
19002 mg_check_feature(MG_FEATURES_IPV6) ?
" IPv6" :
"",
19003 mg_check_feature(MG_FEATURES_WEBSOCKET) ?
" WebSockets"
19005 mg_check_feature(MG_FEATURES_LUA) ?
" Lua" :
"",
19006 mg_check_feature(MG_FEATURES_SSJS) ?
" JavaScript" :
"",
19007 mg_check_feature(MG_FEATURES_CACHE) ?
" Cache" :
"",
19008 mg_check_feature(MG_FEATURES_STATS) ?
" Stats" :
"",
19010 system_info_length += (int)strlen(block);
19011 if (system_info_length < buflen) {
19012 strcat0(buffer, block);
19015 #if defined(USE_LUA)
19020 "\"lua_version\" : \"%u (%s)\",%s",
19021 (
unsigned)LUA_VERSION_NUM,
19024 system_info_length += (int)strlen(block);
19025 if (system_info_length < buflen) {
19026 strcat0(buffer, block);
19029 #if defined(USE_DUKTAPE)
19034 "\"javascript\" : \"Duktape %u.%u.%u\",%s",
19035 (
unsigned)DUK_VERSION / 10000,
19036 ((
unsigned)DUK_VERSION / 100) % 100,
19037 (
unsigned)DUK_VERSION % 100,
19039 system_info_length += (int)strlen(block);
19040 if (system_info_length < buflen) {
19041 strcat0(buffer, block);
19048 #if defined(GCC_DIAGNOSTIC)
19049 #if GCC_VERSION >= 50000
19050 #pragma GCC diagnostic push
19052 #pragma GCC diagnostic ignored "-Wdate-time"
19059 "\"build\" : \"%s\",%s",
19063 #if defined(GCC_DIAGNOSTIC)
19064 #if GCC_VERSION >= 50000
19065 #pragma GCC diagnostic pop
19069 system_info_length += (int)strlen(block);
19070 if (system_info_length < buflen) {
19071 strcat0(buffer, block);
19079 #if defined(_MSC_VER)
19084 "\"compiler\" : \"MSC: %u (%u)\",%s",
19085 (
unsigned)_MSC_VER,
19086 (
unsigned)_MSC_FULL_VER,
19088 system_info_length += (int)strlen(block);
19089 if (system_info_length < buflen) {
19090 strcat0(buffer, block);
19092 #elif defined(__MINGW64__)
19097 "\"compiler\" : \"MinGW64: %u.%u\",%s",
19098 (
unsigned)__MINGW64_VERSION_MAJOR,
19099 (
unsigned)__MINGW64_VERSION_MINOR,
19101 system_info_length += (int)strlen(block);
19102 if (system_info_length < buflen) {
19103 strcat0(buffer, block);
19109 "\"compiler\" : \"MinGW32: %u.%u\",%s",
19110 (
unsigned)__MINGW32_MAJOR_VERSION,
19111 (
unsigned)__MINGW32_MINOR_VERSION,
19113 system_info_length += (int)strlen(block);
19114 if (system_info_length < buflen) {
19115 strcat0(buffer, block);
19117 #elif defined(__MINGW32__)
19122 "\"compiler\" : \"MinGW32: %u.%u\",%s",
19123 (
unsigned)__MINGW32_MAJOR_VERSION,
19124 (
unsigned)__MINGW32_MINOR_VERSION,
19126 system_info_length += (int)strlen(block);
19127 if (system_info_length < buflen) {
19128 strcat0(buffer, block);
19130 #elif defined(__clang__)
19135 "\"compiler\" : \"clang: %u.%u.%u (%s)\",%s",
19138 __clang_patchlevel__,
19141 system_info_length += (int)strlen(block);
19142 if (system_info_length < buflen) {
19143 strcat0(buffer, block);
19145 #elif defined(__GNUC__)
19150 "\"compiler\" : \"gcc: %u.%u.%u\",%s",
19151 (
unsigned)__GNUC__,
19152 (
unsigned)__GNUC_MINOR__,
19153 (
unsigned)__GNUC_PATCHLEVEL__,
19155 system_info_length += (int)strlen(block);
19156 if (system_info_length < buflen) {
19157 strcat0(buffer, block);
19159 #elif defined(__INTEL_COMPILER)
19164 "\"compiler\" : \"Intel C/C++: %u\",%s",
19165 (
unsigned)__INTEL_COMPILER,
19167 system_info_length += (int)strlen(block);
19168 if (system_info_length < buflen) {
19169 strcat0(buffer, block);
19171 #elif defined(__BORLANDC__)
19176 "\"compiler\" : \"Borland C: 0x%x\",%s",
19177 (
unsigned)__BORLANDC__,
19179 system_info_length += (int)strlen(block);
19180 if (system_info_length < buflen) {
19181 strcat0(buffer, block);
19183 #elif defined(__SUNPRO_C)
19188 "\"compiler\" : \"Solaris: 0x%x\",%s",
19189 (
unsigned)__SUNPRO_C,
19191 system_info_length += (int)strlen(block);
19192 if (system_info_length < buflen) {
19193 strcat0(buffer, block);
19200 "\"compiler\" : \"other\",%s",
19202 system_info_length += (int)strlen(block);
19203 if (system_info_length < buflen) {
19204 strcat0(buffer, block);
19216 "\"data_model\" : \"int:%u/%u/%u/%u, float:%u/%u/%u, "
19218 "ptr:%u, size:%u, time:%u\"%s",
19219 (
unsigned)
sizeof(
short),
19220 (
unsigned)
sizeof(
int),
19221 (
unsigned)
sizeof(
long),
19222 (
unsigned)
sizeof(
long long),
19223 (
unsigned)
sizeof(
float),
19224 (
unsigned)
sizeof(
double),
19225 (
unsigned)
sizeof(
long double),
19226 (
unsigned)
sizeof(
char),
19227 (
unsigned)
sizeof(
wchar_t),
19228 (
unsigned)
sizeof(
void *),
19229 (
unsigned)
sizeof(
size_t),
19230 (
unsigned)
sizeof(time_t),
19232 system_info_length += (int)strlen(block);
19233 if (system_info_length < buflen) {
19234 strcat0(buffer, block);
19239 if ((buflen > 0) && buffer && buffer[0]) {
19240 if (system_info_length < buflen) {
19241 strcat0(buffer, eoobj);
19242 strcat0(buffer, eol);
19245 system_info_length += reserved_len;
19247 return system_info_length;
19251 #if defined(USE_SERVER_STATS)
19255 mg_get_context_info_impl(
const struct mg_context *ctx,
char *buffer,
int buflen)
19259 int context_info_length = 0;
19261 #if defined(_WIN32)
19262 const char *eol =
"\r\n";
19264 const char *eol =
"\n";
19266 struct mg_memory_stat *ms = get_memory_stat((
struct mg_context *)ctx);
19268 const char *eoobj =
"}";
19269 int reserved_len = (int)strlen(eoobj) + (int)strlen(eol);
19271 if ((buffer == NULL) || (buflen < 1)) {
19277 mg_snprintf(NULL, NULL, block,
sizeof(block),
"{%s", eol);
19278 context_info_length += (int)strlen(block);
19279 if (context_info_length < buflen) {
19280 strcat0(buffer, block);
19290 "\"blocks\" : %i,%s"
19291 "\"used\" : %" INT64_FMT
",%s"
19292 "\"maxUsed\" : %" INT64_FMT
"%s"
19304 context_info_length += (int)strlen(block);
19305 if (context_info_length + reserved_len < buflen) {
19306 strcat0(buffer, block);
19313 char start_time_str[64] = {0};
19314 char now_str[64] = {0};
19315 time_t start_time = ctx->start_time;
19316 time_t now = time(NULL);
19323 "\"connections\" : {%s"
19324 "\"active\" : %i,%s"
19325 "\"maxActive\" : %i,%s"
19326 "\"total\" : %" INT64_FMT
"%s"
19329 ctx->active_connections,
19331 ctx->max_connections,
19333 ctx->total_connections,
19337 context_info_length += (int)strlen(block);
19338 if (context_info_length + reserved_len < buflen) {
19339 strcat0(buffer, block);
19347 "\"requests\" : {%s"
19348 "\"total\" : %" INT64_FMT
"%s"
19351 ctx->total_requests,
19355 context_info_length += (int)strlen(block);
19356 if (context_info_length + reserved_len < buflen) {
19357 strcat0(buffer, block);
19366 "\"read\" : %" INT64_FMT
"%s,"
19367 "\"written\" : %" INT64_FMT
"%s"
19370 ctx->total_data_read,
19372 ctx->total_data_written,
19376 context_info_length += (int)strlen(block);
19377 if (context_info_length + reserved_len < buflen) {
19378 strcat0(buffer, block);
19382 gmt_time_string(start_time_str,
19383 sizeof(start_time_str) - 1,
19385 gmt_time_string(now_str,
sizeof(now_str) - 1, &now);
19392 "\"uptime\" : %.0f,%s"
19393 "\"start\" : \"%s\",%s"
19394 "\"now\" : \"%s\"%s"
19397 difftime(now, start_time),
19405 context_info_length += (int)strlen(block);
19406 if (context_info_length + reserved_len < buflen) {
19407 strcat0(buffer, block);
19412 if ((buflen > 0) && buffer && buffer[0]) {
19413 if (context_info_length < buflen) {
19414 strcat0(buffer, eoobj);
19415 strcat0(buffer, eol);
19418 context_info_length += reserved_len;
19420 return context_info_length;
19425 #if defined(MG_EXPERIMENTAL_INTERFACES)
19429 mg_get_connection_info_impl(
const struct mg_context *ctx,
19434 const struct mg_connection *conn;
19435 const struct mg_request_info *ri;
19437 int connection_info_length = 0;
19439 const char *state_str =
"unknown";
19441 #if defined(_WIN32)
19442 const char *eol =
"\r\n";
19444 const char *eol =
"\n";
19447 const char *eoobj =
"}";
19448 int reserved_len = (int)strlen(eoobj) + (int)strlen(eol);
19450 if ((buffer == NULL) || (buflen < 1)) {
19456 if ((ctx == NULL) || (idx < 0)) {
19461 if ((
unsigned)idx >= ctx->cfg_worker_threads) {
19468 conn = (ctx->worker_connections) + idx;
19471 mg_snprintf(NULL, NULL, block,
sizeof(block),
"{%s", eol);
19472 connection_info_length += (int)strlen(block);
19473 if (connection_info_length < buflen) {
19474 strcat0(buffer, block);
19478 ri = &(conn->request_info);
19480 #if defined(USE_SERVER_STATS)
19481 state = conn->conn_state;
19486 state_str =
"undefined";
19489 state_str =
"not used";
19492 state_str =
"init";
19495 state_str =
"ready";
19498 state_str =
"processing";
19501 state_str =
"processed";
19504 state_str =
"to close";
19507 state_str =
"closing";
19510 state_str =
"closed";
19513 state_str =
"done";
19519 if ((state >= 3) && (state < 9)) {
19524 "\"connection\" : {%s"
19526 "\"protocol\" : \"%s\",%s"
19527 "\"addr\" : \"%s\",%s"
19530 "\"handled_requests\" : %u%s"
19534 get_proto_name(conn),
19541 conn->handled_requests,
19545 connection_info_length += (int)strlen(block);
19546 if (connection_info_length + reserved_len < buflen) {
19547 strcat0(buffer, block);
19552 if ((state >= 4) && (state < 6)) {
19557 "\"request_info\" : {%s"
19558 "\"method\" : \"%s\",%s"
19559 "\"uri\" : \"%s\",%s"
19560 "\"query\" : %s%s%s%s"
19563 ri->request_method,
19567 ri->query_string ?
"\"" :
"",
19568 ri->query_string ? ri->query_string :
"null",
19569 ri->query_string ?
"\"" :
"",
19573 connection_info_length += (int)strlen(block);
19574 if (connection_info_length + reserved_len < buflen) {
19575 strcat0(buffer, block);
19580 if ((state >= 2) && (state < 9)) {
19581 char start_time_str[64] = {0};
19582 char now_str[64] = {0};
19583 time_t start_time = conn->conn_birth_time;
19584 time_t now = time(NULL);
19586 gmt_time_string(start_time_str,
19587 sizeof(start_time_str) - 1,
19589 gmt_time_string(now_str,
sizeof(now_str) - 1, &now);
19596 "\"uptime\" : %.0f,%s"
19597 "\"start\" : \"%s\",%s"
19598 "\"now\" : \"%s\"%s"
19601 difftime(now, start_time),
19609 connection_info_length += (int)strlen(block);
19610 if (connection_info_length + reserved_len < buflen) {
19611 strcat0(buffer, block);
19616 if ((ri->remote_user) && (state < 9)) {
19622 "\"name\" : \"%s\",%s"
19629 connection_info_length += (int)strlen(block);
19630 if (connection_info_length + reserved_len < buflen) {
19631 strcat0(buffer, block);
19642 "\"read\" : %" INT64_FMT
",%s"
19643 "\"written\" : %" INT64_FMT
"%s"
19646 conn->consumed_content,
19648 conn->num_bytes_sent,
19652 connection_info_length += (int)strlen(block);
19653 if (connection_info_length + reserved_len < buflen) {
19654 strcat0(buffer, block);
19663 "\"state\" : \"%s\"%s",
19667 connection_info_length += (int)strlen(block);
19668 if (connection_info_length + reserved_len < buflen) {
19669 strcat0(buffer, block);
19673 if ((buflen > 0) && buffer && buffer[0]) {
19674 if (connection_info_length < buflen) {
19675 strcat0(buffer, eoobj);
19676 strcat0(buffer, eol);
19679 connection_info_length += reserved_len;
19681 return connection_info_length;
19689 mg_get_system_info(
char *buffer,
int buflen)
19691 if ((buffer == NULL) || (buflen < 1)) {
19692 return mg_get_system_info_impl(NULL, 0);
19696 return mg_get_system_info_impl(buffer, buflen);
19704 mg_get_context_info(
const struct mg_context *ctx,
char *buffer,
int buflen)
19706 #if defined(USE_SERVER_STATS)
19707 if ((buffer == NULL) || (buflen < 1)) {
19708 return mg_get_context_info_impl(ctx, NULL, 0);
19712 return mg_get_context_info_impl(ctx, buffer, buflen);
19716 if ((buffer != NULL) && (buflen > 0)) {
19724 #if defined(MG_EXPERIMENTAL_INTERFACES)
19726 mg_get_connection_info(
const struct mg_context *ctx,
19731 if ((buffer == NULL) || (buflen < 1)) {
19732 return mg_get_connection_info_impl(ctx, idx, NULL, 0);
19736 return mg_get_connection_info_impl(ctx, idx, buffer, buflen);
19745 mg_init_library(
unsigned features)
19747 #if !defined(NO_SSL)
19751 unsigned features_to_init = mg_check_feature(features & 0xFFu);
19752 unsigned features_inited = features_to_init;
19754 if (mg_init_library_called <= 0) {
19756 if (0 != pthread_mutex_init(&global_lock_mutex, NULL)) {
19763 if (mg_init_library_called <= 0) {
19764 if (0 != pthread_key_create(&sTlsKey, tls_dtor)) {
19767 mg_global_unlock();
19771 #if defined(_WIN32)
19772 InitializeCriticalSection(&global_log_file_lock);
19774 #if !defined(_WIN32)
19775 pthread_mutexattr_init(&pthread_mutex_attr);
19776 pthread_mutexattr_settype(&pthread_mutex_attr, PTHREAD_MUTEX_RECURSIVE);
19779 #if defined(USE_LUA)
19780 lua_init_optional_libraries();
19784 mg_global_unlock();
19786 #if !defined(NO_SSL)
19787 if (features_to_init & MG_FEATURES_SSL) {
19788 if (!mg_ssl_initialized) {
19789 if (initialize_ssl(ebuf,
sizeof(ebuf))) {
19790 mg_ssl_initialized = 1;
19793 DEBUG_TRACE(
"Initializing SSL failed: %s", ebuf);
19794 features_inited &= ~((unsigned)(MG_FEATURES_SSL));
19804 if (mg_init_library_called <= 0) {
19805 #if defined(_WIN32)
19807 WSAStartup(MAKEWORD(2, 2), &data);
19809 mg_init_library_called = 1;
19811 mg_init_library_called++;
19813 mg_global_unlock();
19815 return features_inited;
19821 mg_exit_library(
void)
19823 if (mg_init_library_called <= 0) {
19829 mg_init_library_called--;
19830 if (mg_init_library_called == 0) {
19831 #if defined(_WIN32)
19832 (void)WSACleanup();
19834 #if !defined(NO_SSL)
19835 if (mg_ssl_initialized) {
19836 uninitialize_ssl();
19837 mg_ssl_initialized = 0;
19841 #if defined(_WIN32)
19842 (void)DeleteCriticalSection(&global_log_file_lock);
19844 #if !defined(_WIN32)
19845 (void)pthread_mutexattr_destroy(&pthread_mutex_attr);
19848 (void)pthread_key_delete(sTlsKey);
19850 #if defined(USE_LUA)
19851 lua_exit_optional_libraries();
19854 mg_global_unlock();
19855 (void)pthread_mutex_destroy(&global_lock_mutex);
19859 mg_global_unlock();