コード例 #1
0
ファイル: sl_ssl_if.c プロジェクト: mirinae/mongoose
/* If the file's extension is .pem, convert it to DER format and put on SLFS. */
static char *sl_pem2der(const char *pem_file) {
  const char *pem_ext = strstr(pem_file, ".pem");
  if (pem_ext == NULL || *(pem_ext + 4) != '\0') {
    return strdup(pem_file);
  }
  char *der_file = NULL;
  /* DER file must be located on SLFS, add prefix. */
  int l = mg_asprintf(&der_file, 0, MG_SSL_IF_SIMPLELINK_SLFS_PREFIX "%.*s.der",
                      (int) (pem_ext - pem_file), pem_file);
  if (der_file == NULL) return NULL;
  bool result = false;
  cs_stat_t st;
  if (mg_stat(der_file, &st) != 0) {
    result = pem_to_der(pem_file, der_file);
    LOG(LL_DEBUG, ("%s -> %s = %d", pem_file, der_file, result));
  } else {
    /* File exists, assume it's already been converted. */
    result = true;
  }
  if (result) {
    /* Strip the SL: prefix we added since NWP does not expect it. */
    memmove(der_file, der_file + MG_SSL_IF_SIMPLELINK_SLFS_PREFIX_LEN,
            l - 2 /* including \0 */);
  } else {
    MG_FREE(der_file);
    der_file = NULL;
  }
  return der_file;
}
コード例 #2
0
ファイル: lua.c プロジェクト: Andersbakken/mongoose
static int handle_lsp_request(struct mg_connection *conn, const char *path,
                               struct file *filep, struct lua_State *ls) {
  void *p = NULL;
  lua_State *L = NULL;
  FILE *fp = NULL;
  int error = 1;

  // We need both mg_stat to get file size, and mg_fopen to get fd
  if (!mg_stat(path, filep) || (fp = mg_fopen(path, "r")) == NULL) {
    lsp_send_err(conn, ls, "File [%s] not found", path);
  } else if ((p = mmap(NULL, (size_t) filep->size, PROT_READ, MAP_PRIVATE,
                       fileno(fp), 0)) == MAP_FAILED) {
    lsp_send_err(conn, ls, "mmap(%s, %zu, %d): %s", path, (size_t) filep->size,
              fileno(fp), strerror(errno));
  } else if ((L = ls != NULL ? ls : luaL_newstate()) == NULL) {
    send_http_error(conn, 500, http_500_error, "%s", "luaL_newstate failed");
  } else {
    // We're not sending HTTP headers here, Lua page must do it.
    if (ls == NULL) {
      prepare_lua_environment(conn, L);
    }
    error = lsp(conn, path, p, filep->size, L);
  }

  if (L != NULL && ls == NULL) lua_close(L);
  if (p != NULL) munmap(p, filep->size);
  fclose(fp);

  return error;
}
コード例 #3
0
ファイル: auth.c プロジェクト: Andersbakken/mongoose
// Use the global passwords file, if specified by auth_gpass option,
// or search for .htpasswd in the requested directory.
static FILE *open_auth_file(struct mg_connection *conn, const char *path) {
  char name[PATH_MAX];
  const char *p, *e, *gpass = conn->ctx->config[GLOBAL_PASSWORDS_FILE];
  struct file file = STRUCT_FILE_INITIALIZER;
  FILE *fp = NULL;

  if (gpass != NULL) {
    // Use global passwords file
    fp = mg_fopen(gpass, "r");
    // Important: using local struct file to test path for is_directory flag.
    // If filep is used, mg_stat() makes it appear as if auth file was opened.
  } else if (mg_stat(path, &file) && file.is_directory) {
    mg_snprintf(name, sizeof(name), "%s%c%s",
                path, '/', PASSWORDS_FILE_NAME);
    fp = mg_fopen(name, "r");
  } else {
     // Try to find .htpasswd in requested directory.
    for (p = path, e = p + strlen(p) - 1; e > p; e--)
      if (e[0] == '/')
        break;
    mg_snprintf(name, sizeof(name), "%.*s%c%s",
                (int) (e - p), p, '/', PASSWORDS_FILE_NAME);
    fp = mg_fopen(name, "r");
  }

  return fp;
}
コード例 #4
0
ファイル: main.c プロジェクト: cobookman/mongoose
static void mg_ev_handler(struct mg_connection *nc, int ev, void *ev_data) {
  switch (ev) {
    case MG_EV_ACCEPT: {
      char addr[32];
      mg_conn_addr_to_str(nc, addr, sizeof(addr), MG_SOCK_STRINGIFY_REMOTE |
                                                      MG_SOCK_STRINGIFY_IP |
                                                      MG_SOCK_STRINGIFY_PORT);
      LOG(LL_INFO, ("%p conn from %s", nc, addr));
      break;
    }
    case MG_EV_HTTP_REQUEST: {
      char addr[32];
      struct http_message *hm = (struct http_message *) ev_data;
      cs_stat_t st;
      mg_conn_addr_to_str(nc, addr, sizeof(addr), MG_SOCK_STRINGIFY_REMOTE |
                                                      MG_SOCK_STRINGIFY_IP |
                                                      MG_SOCK_STRINGIFY_PORT);
      LOG(LL_INFO,
          ("HTTP request from %s: %.*s %.*s", addr, (int) hm->method.len,
           hm->method.p, (int) hm->uri.len, hm->uri.p));
      if (mg_vcmp(&hm->uri, "/upload") == 0 ||
          (mg_vcmp(&hm->uri, "/") == 0 && mg_stat("SL:index.html", &st) != 0)) {
        mg_send(nc, upload_form, strlen(upload_form));
        nc->flags |= MG_F_SEND_AND_CLOSE;
        break;
      }
      struct mg_serve_http_opts opts;
      memset(&opts, 0, sizeof(opts));
      opts.document_root = "SL:";
      mg_serve_http(nc, hm, opts);
      break;
    }
    case MG_EV_CLOSE: {
      LOG(LL_INFO, ("%p closed", nc));
      break;
    }
    case MG_EV_WEBSOCKET_HANDSHAKE_DONE: {
      LOG(LL_INFO, ("%p switching to data mode", nc));
      nc->handler = data_conn_handler;
      nc->ev_timer_time = mg_time(); /* Immediately */
      break;
    }
    case MG_EV_TIMER: {
      data_collect();
      nc->ev_timer_time = mg_time() + (DATA_COLLECTION_INTERVAL_MS * 0.001);
    }
    case MG_EV_HTTP_PART_BEGIN:
    case MG_EV_HTTP_PART_DATA:
    case MG_EV_HTTP_PART_END: {
      struct mg_http_multipart_part *mp =
          (struct mg_http_multipart_part *) ev_data;
      if (ev == MG_EV_HTTP_PART_BEGIN) {
        LOG(LL_INFO, ("Begin file upload: %s", mp->file_name));
      } else if (ev == MG_EV_HTTP_PART_END) {
        LOG(LL_INFO, ("End file upload: %s", mp->file_name));
      }
      mg_file_upload_handler(nc, ev, ev_data, upload_fname);
    }
  }
}
コード例 #5
0
ファイル: server.c プロジェクト: 2bright/mongoose
int main(void) {
  struct mg_mgr mgr;
  struct mg_connection *nc;
  cs_stat_t st;

  mg_mgr_init(&mgr, NULL);
  nc = mg_bind(&mgr, s_http_port, ev_handler);
  if (nc == NULL) {
    fprintf(stderr, "Cannot bind to %s\n", s_http_port);
    exit(1);
  }

  // Set up HTTP server parameters
  mg_set_protocol_http_websocket(nc);
  s_http_server_opts.document_root = "web_root";  // Set up web root directory

  if (mg_stat(s_http_server_opts.document_root, &st) != 0) {
    fprintf(stderr, "%s", "Cannot find web_root directory, exiting\n");
    exit(1);
  }

  printf("Starting web server on port %s\n", s_http_port);
  for (;;) {
    static time_t last_time;
    time_t now = time(NULL);
    mg_mgr_poll(&mgr, 1000);
    if (now - last_time > 0) {
      push_data_to_all_websocket_connections(&mgr);
      last_time = now;
    }
  }
  mg_mgr_free(&mgr);

  return 0;
}
コード例 #6
0
ファイル: main.c プロジェクト: commshare/civet-webserver
static void verify_document_root(const char *root) {
  struct mgstat st;
  char buf[PATH_MAX];

  getcwd(buf, sizeof(buf));
  if (mg_stat(root, &st) != 0 || !st.is_directory) {
    die("Invalid root directory: [%s]: %s; current directory = [%s]", root, mg_strerror(ERRNO), buf);
  }
}
コード例 #7
0
ファイル: unit_test.c プロジェクト: okigan/mongoose
static void test_mg_fetch(void) {
  static const char *options[] = {
    "document_root", ".",
    "listening_ports", "33796",
    NULL,
  };
  char buf[2000], buf2[2000];
  int length;
  struct mg_context *ctx;
  struct mg_request_info ri;
  const char *tmp_file = "temporary_file_name_for_unit_test.txt";
  struct mgstat st;
  FILE *fp;

  ASSERT((ctx = mg_start(event_handler, NULL, options)) != NULL);

  // Failed fetch, pass invalid URL
  ASSERT(mg_fetch(ctx, "localhost", tmp_file, buf, sizeof(buf), &ri) == NULL);
  ASSERT(mg_fetch(ctx, "localhost:33796", tmp_file,
                  buf, sizeof(buf), &ri) == NULL);
  ASSERT(mg_fetch(ctx, "http://$$$.$$$", tmp_file,
                  buf, sizeof(buf), &ri) == NULL);

  // Failed fetch, pass invalid file name
  ASSERT(mg_fetch(ctx, "http://localhost:33796/data",
                  "/this/file/must/not/exist/ever",
                  buf, sizeof(buf), &ri) == NULL);

  // Successful fetch
  ASSERT((fp = mg_fetch(ctx, "http://localhost:33796/data",
                        tmp_file, buf, sizeof(buf), &ri)) != NULL);
  ASSERT(ri.num_headers == 2);
  ASSERT(!strcmp(ri.request_method, "HTTP/1.1"));
  ASSERT(!strcmp(ri.uri, "200"));
  ASSERT(!strcmp(ri.http_version, "OK"));
  ASSERT((length = ftell(fp)) == (int) strlen(fetch_data));
  fseek(fp, 0, SEEK_SET);
  ASSERT(fread(buf2, 1, length, fp) == (size_t) length);
  ASSERT(memcmp(buf2, fetch_data, length) == 0);
  fclose(fp);

  // Fetch big file, mongoose.c
  ASSERT((fp = mg_fetch(ctx, "http://localhost:33796/mongoose.c",
                        tmp_file, buf, sizeof(buf), &ri)) != NULL);
  ASSERT(mg_stat("mongoose.c", &st) == 0);
  ASSERT(st.size == ftell(fp));
  ASSERT(!strcmp(ri.request_method, "HTTP/1.1"));

  remove(tmp_file);
  mg_stop(ctx);
}
コード例 #8
0
void mg_ev_handler(struct mg_connection *nc, int ev, void *ev_data) {
  LOG(LL_DEBUG, ("%p ev %d", nc, ev));

  switch (ev) {
    case MG_EV_ACCEPT: {
      char addr[32];
      mg_sock_addr_to_str(&nc->sa, addr, sizeof(addr),
                          MG_SOCK_STRINGIFY_IP | MG_SOCK_STRINGIFY_PORT);
      LOG(LL_INFO, ("Connection %p from %s", nc, addr));
      break;
    }
    case MG_EV_HTTP_REQUEST: {
      char addr[32];
      struct http_message *hm = (struct http_message *) ev_data;
      cs_stat_t st;
      mg_sock_addr_to_str(&nc->sa, addr, sizeof(addr),
                          MG_SOCK_STRINGIFY_IP | MG_SOCK_STRINGIFY_PORT);
      LOG(LL_INFO,
          ("HTTP request from %s: %.*s %.*s", addr, (int) hm->method.len,
           hm->method.p, (int) hm->uri.len, hm->uri.p));
      if (mg_vcmp(&hm->uri, "/upload") == 0 ||
          (mg_vcmp(&hm->uri, "/") == 0 && mg_stat("SL:index.html", &st) != 0)) {
        mg_send(nc, upload_form, strlen(upload_form));
        nc->flags |= MG_F_SEND_AND_CLOSE;
        break;
      }
      struct mg_serve_http_opts opts;
      memset(&opts, 0, sizeof(opts));
      opts.document_root = "SL:";
      mg_serve_http(nc, hm, opts);
      break;
    }
    case MG_EV_CLOSE: {
      LOG(LL_INFO, ("Connection %p closed", nc));
      break;
    }
    case MG_EV_HTTP_PART_BEGIN:
    case MG_EV_HTTP_PART_DATA:
    case MG_EV_HTTP_PART_END: {
      struct mg_http_multipart_part *mp =
          (struct mg_http_multipart_part *) ev_data;
      if (ev == MG_EV_HTTP_PART_BEGIN) {
        LOG(LL_INFO, ("Begin file upload: %s", mp->file_name));
      } else if (ev == MG_EV_HTTP_PART_END) {
        LOG(LL_INFO, ("End file upload: %s", mp->file_name));
      }
      mg_file_upload_handler(nc, ev, ev_data, upload_fname);
    }
  }
}
コード例 #9
0
ファイル: unit_test.c プロジェクト: BenoitHuon/civetweb
static void test_mg_stat(void) {
  static struct mg_context ctx;
  struct file file = STRUCT_FILE_INITIALIZER;
  ASSERT(!mg_stat(fc(&ctx), " does not exist ", &file));
}
コード例 #10
0
ファイル: unit_test.c プロジェクト: anton-nesterenko/mongoose
static void test_mg_fetch(void) {
  static const char *options[] = {
    "document_root", ".",
    "listening_ports", UNUSED_PORT,
    NULL,
  };
  char buf[2000], buf2[2000];
  int n, length;
  struct mg_context *ctx;
  struct mg_request_info ri;
  const char *tmp_file = "temporary_file_name_for_unit_test.txt";
  struct file file;
  FILE *fp;

  ASSERT((ctx = mg_start(event_handler, NULL, options)) != NULL);

  // Failed fetch, pass invalid URL
  ASSERT(mg_fetch(ctx, "localhost", tmp_file, buf, sizeof(buf), &ri) == NULL);
  ASSERT(mg_fetch(ctx, "localhost:" UNUSED_PORT, tmp_file,
                  buf, sizeof(buf), &ri) == NULL);
  ASSERT(mg_fetch(ctx, "http://$$$.$$$", tmp_file,
                  buf, sizeof(buf), &ri) == NULL);

  // Failed fetch, pass invalid file name
  ASSERT(mg_fetch(ctx, "http://localhost:" UNUSED_PORT "/data",
                  "/this/file/must/not/exist/ever",
                  buf, sizeof(buf), &ri) == NULL);

  // Successful fetch
  ASSERT((fp = mg_fetch(ctx, "http://localhost:" UNUSED_PORT "/data",
                        tmp_file, buf, sizeof(buf), &ri)) != NULL);
  ASSERT(ri.num_headers == 2);
  ASSERT(!strcmp(ri.request_method, "HTTP/1.1"));
  ASSERT(!strcmp(ri.uri, "200"));
  ASSERT(!strcmp(ri.http_version, "OK"));
  ASSERT((length = ftell(fp)) == (int) strlen(fetch_data));
  fseek(fp, 0, SEEK_SET);
  ASSERT(fread(buf2, 1, length, fp) == (size_t) length);
  ASSERT(memcmp(buf2, fetch_data, length) == 0);
  fclose(fp);

  // Fetch big file, mongoose.c
  ASSERT((fp = mg_fetch(ctx, "http://localhost:" UNUSED_PORT "/mongoose.c",
                        tmp_file, buf, sizeof(buf), &ri)) != NULL);
  ASSERT(mg_stat(fc(ctx), "mongoose.c", &file));
  ASSERT(file.size == ftell(fp));
  ASSERT(!strcmp(ri.request_method, "HTTP/1.1"));

  // Fetch nonexistent file, /blah
  ASSERT((fp = mg_fetch(ctx, "http://localhost:" UNUSED_PORT "/boo",
                        tmp_file, buf, sizeof(buf), &ri)) != NULL);
  ASSERT(!mg_strcasecmp(ri.uri, "404"));
  fclose(fp);

  // Fetch existing inmemory file
  ASSERT((fp = mg_fetch(ctx, "http://localhost:" UNUSED_PORT "/blah",
                        tmp_file, buf, sizeof(buf), &ri)) != NULL);
  ASSERT(!mg_strcasecmp(ri.uri, "200"));
  n = ftell(fp);
  fseek(fp, 0, SEEK_SET);
  printf("%s %d %d [%.*s]\n", __func__, n, (int) feof(fp), n, buf2);
  n = fread(buf2, 1, n, fp);
  printf("%s %d %d [%.*s]\n", __func__, n, (int) feof(fp), n, buf2);
  ASSERT((size_t) ftell(fp) == (size_t) strlen(inmemory_file_data));
  ASSERT(!memcmp(inmemory_file_data, buf2, ftell(fp)));
  fclose(fp);

  remove(tmp_file);
  mg_stop(ctx);
}
コード例 #11
0
ファイル: main.c プロジェクト: commshare/civet-webserver
static void *mongoose_callback(enum mg_event event, struct mg_connection *conn) {
  const struct mg_request_info *request_info = mg_get_request_info(conn);

  if (event == MG_INIT0)
  {
    verify_document_root(mg_get_conn_option(conn, "document_root"));
    return (void *)1;
  }

#if defined(_WIN32)
  if (event == MG_EVENT_LOG &&
      strstr(request_info->log_message, "cannot bind to") &&
      !strcmp(request_info->log_severity, "error"))
  {
    if (!error_dialog_shown_previously)
    {
      MessageBoxA(NULL, request_info->log_message, "Error", MB_OK);
      error_dialog_shown_previously = 1;
    }
    return 0;
  }
#endif

  if (event != MG_NEW_REQUEST) {
    // This callback currently only handles new requests
    return NULL;
  }

  {
    int file_found;
    struct mgstat fst;

    assert(request_info->phys_path);
    file_found = (0 == mg_stat(request_info->phys_path, &fst) && !fst.is_directory);
    if (file_found) {
      return NULL; // let mongoose handle the default of 'file exists'...
    }

#ifdef _WIN32
    // Send the systray icon as favicon
    if (!strcmp("/favicon.ico", request_info->uri)) {
      HMODULE module;
      HRSRC icon;
      DWORD len;
      void *data;

      module = GetModuleHandle(NULL);

      icon = FindResource(module, MAKEINTRESOURCE(IDR_FAVICON), RT_RCDATA);
      data = LockResource(LoadResource(module, icon));
      len = SizeofResource(module, icon);

      mg_add_response_header(conn, 0, "Content-Type", "image/x-icon");
      mg_add_response_header(conn, 0, "Cache-Control", "no-cache");
      mg_add_response_header(conn, 0, "Content-Length", "%u", (unsigned int)len);
      //mg_add_response_header(conn, 0, "Connection", suggest_connection_header(conn)); -- not needed any longer
      mg_write_http_response_head(conn, 200, NULL);

      if ((int)len != mg_write(conn, data, len)) {
        mg_send_http_error(conn, 580, NULL, "not all data was written to the socket (len: %u)", (unsigned int)len); // internal error in our custom handler or client closed connection prematurely
      }
      return (void *)1;
    }
#endif
  }

  return NULL;
}
コード例 #12
0
static void *mongoose_callback(enum mg_event event, struct mg_connection *conn) {
  const struct mg_request_info *request_info = mg_get_request_info(conn);

  if (event == MG_INIT0)
  {
    verify_document_root(mg_get_conn_option(conn, "document_root"));
    return (void *)1;
  }

#if defined(_WIN32)
  if (event == MG_EVENT_LOG &&
      strstr(request_info->log_message, "cannot bind to") &&
      !strcmp(request_info->log_severity, "error"))
  {
    if (!error_dialog_shown_previously)
    {
      MessageBoxA(NULL, request_info->log_message, "Error", MB_OK);
      error_dialog_shown_previously = 1;
    }
    return 0;
  }
#endif

#if USE_BEL2125_TEST_NR_18_EVENT_HANDLER
  {
    int contentLength = 0;
    int dataRead = 0;
    char postData[BUFFER_SIZE] = { 0 };
    const char* contentType = NULL;

    if (event == MG_NEW_REQUEST)
    {
        if (strstr(request_info->uri, "/echo") == request_info->uri)
        {
            int ie_hack = 0;  // testing an assumption; turns out it doesn't matter whether headers make it into TCP stack before you expect to fetch all input data at once.
            int ie_hack2 = 0;

            contentLength = atoi(mg_get_header(conn, "Content-Length"));
            assert(contentLength <= BUFFER_SIZE);

            mg_set_response_code(conn, 200);

            if (ie_hack2) mg_connection_must_close(conn);  // the stackoverflow suggested fix: http://stackoverflow.com/questions/3731420/why-does-ie-issue-random-xhr-408-12152-responses-using-jquery-post

            contentType = mg_get_header(conn, "Content-Type");

            if (ie_hack)
            {
                //mg_add_response_header(conn, 0, "Connection", mg_suggest_connection_header(conn)); -- not needed any longer
                mg_add_response_header(conn, 0, "Content-Type", contentType);
                mg_add_response_header(conn, 0, "Content-Length", "%d", contentLength);
                mg_write_http_response_head(conn, 0, 0);  // let the previous mg_set_response_code() decide for us
            }

            dataRead = mg_read(conn, postData, contentLength);
            if (dataRead > 0)
            {
                assert(dataRead == contentLength);

                if (!ie_hack)
                {
                    //mg_add_response_header(conn, 0, "Connection", mg_suggest_connection_header(conn)); -- not needed any longer
                    mg_add_response_header(conn, 0, "Content-Type", contentType);
                    mg_add_response_header(conn, 0, "Content-Length", "%d", dataRead);
                    mg_write_http_response_head(conn, 0, 0);  // let the previous mg_set_response_code() decide for us
                }

                if (mg_write(conn, postData, dataRead) != contentLength)
                {
                    mg_send_http_error(conn, 580, NULL, "not all data was written to the socket (len: %u)", (unsigned int)contentLength); // internal error in our custom handler or client closed connection prematurely
                }

                return (void*)1;
            }
            else
            {
                mg_send_http_error(conn, 500, NULL, "I/O failure during mg_read() from connection: %s", mg_strerror(ERRNO));
            }
        }
    }
  }
#endif // USE_BEL2125_TEST_NR_18_EVENT_HANDLER

  if (event != MG_NEW_REQUEST) {
    // This callback currently only handles new requests
    return NULL;
  }

  {
    int file_found;
    struct mgstat fst;

    assert(request_info->phys_path);
    file_found = (0 == mg_stat(request_info->phys_path, &fst) && !fst.is_directory);
    if (file_found) {
      return NULL; // let mongoose handle the default of 'file exists'...
    }

#ifdef _WIN32
    // Send the systray icon as favicon
    if (!strcmp("/favicon.ico", request_info->uri)) {
      HMODULE module;
      HRSRC icon;
      DWORD len;
      void *data;

      module = GetModuleHandle(NULL);

      icon = FindResource(module, MAKEINTRESOURCE(IDR_FAVICON), RT_RCDATA);
      data = LockResource(LoadResource(module, icon));
      len = SizeofResource(module, icon);

      mg_add_response_header(conn, 0, "Content-Type", "image/x-icon");
      mg_add_response_header(conn, 0, "Cache-Control", "no-cache");
      mg_add_response_header(conn, 0, "Content-Length", "%u", (unsigned int)len);
      //mg_add_response_header(conn, 0, "Connection", suggest_connection_header(conn)); -- not needed any longer
      mg_write_http_response_head(conn, 200, NULL);

      if ((int)len != mg_write(conn, data, len)) {
        mg_send_http_error(conn, 580, NULL, "not all data was written to the socket (len: %u)", (unsigned int)len); // internal error in our custom handler or client closed connection prematurely
      }
      return (void *)1;
    }
#endif
  }

  return NULL;
}
コード例 #13
0
ファイル: unit_test.c プロジェクト: HeidCloud/mongoose
static void test_mg_stat(void) {
  struct file file = STRUCT_FILE_INITIALIZER;
  ASSERT(!mg_stat(" does not exist ", &file));
}
コード例 #14
0
ファイル: main.c プロジェクト: gavinying/mongoose
static void mg_ev_handler(struct mg_connection *nc, int ev, void *ev_data) {
  switch (ev) {
    case MG_EV_ACCEPT: {
      char addr[32];
      mg_conn_addr_to_str(nc, addr, sizeof(addr), MG_SOCK_STRINGIFY_REMOTE |
                                                      MG_SOCK_STRINGIFY_IP |
                                                      MG_SOCK_STRINGIFY_PORT);
      LOG(LL_INFO, ("%p conn from %s", nc, addr));
      break;
    }
    case MG_EV_HTTP_REQUEST: {
      char addr[32];
      struct http_message *hm = (struct http_message *) ev_data;
      cs_stat_t st;
      mg_conn_addr_to_str(nc, addr, sizeof(addr), MG_SOCK_STRINGIFY_REMOTE |
                                                      MG_SOCK_STRINGIFY_IP |
                                                      MG_SOCK_STRINGIFY_PORT);
      LOG(LL_INFO,
          ("HTTP request from %s: %.*s %.*s", addr, (int) hm->method.len,
           hm->method.p, (int) hm->uri.len, hm->uri.p));
      if (mg_vcmp(&hm->uri, "/upload") == 0 ||
          (mg_vcmp(&hm->uri, "/") == 0 && mg_stat("SL:index.html", &st) != 0)) {
        mg_send(nc, upload_form, strlen(upload_form));
        nc->flags |= MG_F_SEND_AND_CLOSE;
        break;
      }
      struct mg_serve_http_opts opts;
      memset(&opts, 0, sizeof(opts));
      opts.document_root = "SL:";
      mg_serve_http(nc, hm, opts);
      break;
    }
    case MG_EV_CLOSE: {
      LOG(LL_INFO, ("%p closed", nc));
      break;
    }
    case MG_EV_WEBSOCKET_HANDSHAKE_DONE: {
      LOG(LL_INFO, ("%p switching to data mode", nc));
      nc->handler = data_conn_handler;
      nc->ev_timer_time = mg_time(); /* Immediately */
      break;
    }
    case MG_EV_TIMER: {
      data_collect();
      nc->ev_timer_time = mg_time() + (DATA_COLLECTION_INTERVAL_MS * 0.001);
      break;
    }
    /* SimpleLink FS requires pre-declaring max file size. We use Content-Length
     * for that purpose - it will not exactly match file size, but is guaranteed
     * to exceed it and should be close enough. */
    case MG_EV_HTTP_MULTIPART_REQUEST: {
      struct http_message *hm = (struct http_message *) ev_data;
      struct mg_str *cl_header = mg_get_http_header(hm, "Content-Length");
      intptr_t cl = -1;
      if (cl_header != NULL && cl_header->len < 20) {
        char buf[20];
        memcpy(buf, cl_header->p, cl_header->len);
        buf[cl_header->len] = '\0';
        cl = atoi(buf);
        if (cl < 0) cl = -1;
      }
      nc->user_data = (void *) cl;
      break;
    }
    case MG_EV_HTTP_PART_BEGIN:
    case MG_EV_HTTP_PART_DATA:
    case MG_EV_HTTP_PART_END: {
      struct mg_http_multipart_part *mp =
          (struct mg_http_multipart_part *) ev_data;
      if (ev == MG_EV_HTTP_PART_BEGIN) {
        LOG(LL_INFO, ("Begin file upload: %s", mp->file_name));
      } else if (ev == MG_EV_HTTP_PART_END) {
        LOG(LL_INFO, ("End file upload: %s", mp->file_name));
      }
      mg_file_upload_handler(nc, ev, ev_data, upload_fname);
    }
  }
}