Skip to content
Snippets Groups Projects
mongoose.c 104 KiB
Newer Older
  int keep_alive = should_keep_alive(&conn->mg_conn) &&
    conn->endpoint_type == EP_FILE;
  switch (conn->endpoint_type) {
    case EP_FILE: close(conn->endpoint.fd); break;
    case EP_CGI: closesocket(conn->endpoint.cgi_sock); break;
    default: break;
  }
#ifndef NO_LOGGING
  if (conn->mg_conn.status_code != 400) {
    log_access(conn, conn->server->config_options[ACCESS_LOG_FILE]);
  }
#endif
  conn->endpoint_type = EP_NONE;
  conn->cl = conn->num_bytes_sent = conn->request_len = 0;
  free(conn->request);
  conn->request = NULL;
  if (keep_alive) {
    process_request(conn);  // Can call us recursively if pipelining is used
  } else {
    conn->flags |= conn->remote_iobuf.len == 0 ? CONN_CLOSE : CONN_SPOOL_DONE;
static void transfer_file_data(struct connection *conn) {
  char buf[IOBUF_SIZE];
  int n = read(conn->endpoint.fd, buf,
               conn->cl < (int64_t) sizeof(buf) ? conn->cl : (int) sizeof(buf));
  if (is_error(n)) {
    close_local_endpoint(conn);
  } else if (n > 0) {
    conn->cl -= n;
    spool(&conn->remote_iobuf, buf, n);
    if (conn->cl <= 0) {
      close_local_endpoint(conn);
void add_to_set(sock_t sock, fd_set *set, sock_t *max_fd) {
  FD_SET(sock, set);
  if (sock > *max_fd) {
    *max_fd = sock;
void mg_poll_server(struct mg_server *server, int milliseconds) {
  struct linked_list_link *lp, *tmp;
  struct connection *conn;
  struct timeval tv;
  fd_set read_set, write_set;
  sock_t max_fd = -1;
  time_t current_time = time(NULL), expire_time = current_time -
    atoi(server->config_options[IDLE_TIMEOUT_MS]) / 1000;
  if (server->listening_sock == INVALID_SOCKET) return;
  FD_ZERO(&read_set);
  FD_ZERO(&write_set);
  add_to_set(server->listening_sock, &read_set, &max_fd);
  add_to_set(server->ctl[0], &read_set, &max_fd);

  LINKED_LIST_FOREACH(&server->active_connections, lp, tmp) {
    conn = LINKED_LIST_ENTRY(lp, struct connection, link);
    add_to_set(conn->client_sock, &read_set, &max_fd);
    if (conn->endpoint_type == EP_FILE) {
      transfer_file_data(conn);
    } else if (conn->endpoint_type == EP_CGI) {
      add_to_set(conn->endpoint.cgi_sock, &read_set, &max_fd);
    }
    if (conn->remote_iobuf.len > 0) {
      add_to_set(conn->client_sock, &write_set, &max_fd);
    } else if (conn->flags & CONN_CLOSE) {
      close_conn(conn);
  tv.tv_sec = milliseconds / 1000;
  tv.tv_usec = (milliseconds % 1000) * 1000;
  if (select(max_fd + 1, &read_set, &write_set, NULL, &tv) > 0) {
    // If control socket is set, just read from it. It meant to wake up
    // this select loop when another thread writes to any connection
    if (FD_ISSET(server->ctl[0], &read_set)) {
      char buf[500];
      recv(server->ctl[0], buf, sizeof(buf), 0);
    }
    // Accept new connections
    if (FD_ISSET(server->listening_sock, &read_set)) {
      while ((conn = accept_new_connection(server)) != NULL) {
        conn->birth_time = conn->last_activity_time = current_time;
      }
    }

    // Read/write from clients
    LINKED_LIST_FOREACH(&server->active_connections, lp, tmp) {
      conn = LINKED_LIST_ENTRY(lp, struct connection, link);
      if (FD_ISSET(conn->client_sock, &read_set)) {
        conn->last_activity_time = current_time;
        read_from_client(conn);
      }
      if (conn->endpoint_type == EP_CGI &&
          FD_ISSET(conn->endpoint.cgi_sock, &read_set)) {
        read_from_cgi(conn);
      if (FD_ISSET(conn->client_sock, &write_set)) {
        conn->last_activity_time = current_time;
        write_to_client(conn);
  // Close expired connections and those that need to be closed
  LINKED_LIST_FOREACH(&server->active_connections, lp, tmp) {
    conn = LINKED_LIST_ENTRY(lp, struct connection, link);
    ping_idle_websocket_connection(conn, current_time);
    if (conn->flags & CONN_CLOSE || conn->last_activity_time < expire_time) {
      close_conn(conn);
    }
void mg_destroy_server(struct mg_server **server) {
  struct linked_list_link *lp, *tmp;

  if (server != NULL && *server != NULL) {
    closesocket((*server)->listening_sock);
    closesocket((*server)->ctl[0]);
    closesocket((*server)->ctl[1]);
    LINKED_LIST_FOREACH(&(*server)->active_connections, lp, tmp) {
      free(LINKED_LIST_ENTRY(lp, struct connection, link));
    LINKED_LIST_FOREACH(&(*server)->uri_handlers, lp, tmp) {
      free(LINKED_LIST_ENTRY(lp, struct uri_handler, link));
    }
    free(*server);
    *server = NULL;
// Apply function to all active connections. Return number of active
// connections. Function could be NULL.
int mg_iterate_over_connections(struct mg_server *server,
                                void (*func)(struct mg_connection *, void *),
                                void *param) {
  struct linked_list_link *lp, *tmp;
  struct connection *conn;
  int num_connections = 0;
  LINKED_LIST_FOREACH(&server->active_connections, lp, tmp) {
    num_connections++;
    conn = LINKED_LIST_ENTRY(lp, struct connection, link);
    if (conn->endpoint_type == EP_USER && func != NULL) {
      func((struct mg_connection *) conn, param);
    }
  }
  return num_connections;
void mg_add_uri_handler(struct mg_server *server, const char *uri,
                        mg_handler_t handler) {
  struct uri_handler *p = (struct uri_handler *) malloc(sizeof(*p));
  if (p != NULL) {
    LINKED_LIST_ADD_TO_FRONT(&server->uri_handlers, &p->link);
    p->uri = mg_strdup(uri);
    p->handler = handler;
static int get_var(const char *data, size_t data_len, const char *name,
                   char *dst, size_t dst_len) {
  const char *p, *e, *s;
  size_t name_len;
  int len;
  if (dst == NULL || dst_len == 0) {
    len = -2;
  } else if (data == NULL || name == NULL || data_len == 0) {
    len = -1;
    dst[0] = '\0';
  } else {
    name_len = strlen(name);
    e = data + data_len;
    len = -1;
    dst[0] = '\0';
    // data is "var1=val1&var2=val2...". Find variable first
    for (p = data; p + name_len < e; p++) {
      if ((p == data || p[-1] == '&') && p[name_len] == '=' &&
          !mg_strncasecmp(name, p, name_len)) {
        // Point p to variable value
        p += name_len + 1;
        // Point s to the end of the value
        s = (const char *) memchr(p, '&', (size_t)(e - p));
        if (s == NULL) {
          s = e;
        }
        assert(s >= p);
        // Decode variable into destination buffer
        len = mg_url_decode(p, (size_t)(s - p), dst, dst_len, 1);
        // Redirect error code from -1 to -2 (destination buffer too small).
        if (len == -1) {
          len = -2;
        }
        break;
      }
    }
  }
int mg_get_var(const struct mg_connection *conn, const char *name,
               char *dst, size_t dst_len) {
  int len = get_var(conn->query_string, conn->query_string == NULL ? 0 :
                    strlen(conn->query_string), name, dst, dst_len);
  if (len < 0) {
    len = get_var(conn->content, conn->content_len, name, dst, dst_len);
const char **mg_get_valid_option_names(void) {
  return static_config_options;
static int get_option_index(const char *name) {
  int i;

  for (i = 0; static_config_options[i * 2] != NULL; i++) {
    if (strcmp(static_config_options[i * 2], name) == 0) {
      return i;
  }
  return -1;
}

static void set_default_option_values(char **opts) {
  const char *value, **all_opts = mg_get_valid_option_names();
  int i;
  for (i = 0; all_opts[i * 2] != NULL; i++) {
    value = all_opts[i * 2 + 1];
    if (opts[i] == NULL && value != NULL) {
      opts[i] = mg_strdup(value);
// Valid listening port spec is: [ip_address:]port, e.g. "80", "127.0.0.1:3128"
static int parse_port_string(const char *str, union socket_address *sa) {
  unsigned int a, b, c, d, port;
  int len = 0;
#ifdef USE_IPV6
  char buf[100];
#endif
  // MacOS needs that. If we do not zero it, subsequent bind() will fail.
  // Also, all-zeroes in the socket address means binding to all addresses
  // for both IPv4 and IPv6 (INADDR_ANY and IN6ADDR_ANY_INIT).
  memset(sa, 0, sizeof(*sa));
  sa->sin.sin_family = AF_INET;
  if (sscanf(str, "%u.%u.%u.%u:%u%n", &a, &b, &c, &d, &port, &len) == 5) {
    // Bind to a specific IPv4 address, e.g. 192.168.1.5:8080
    sa->sin.sin_addr.s_addr = htonl((a << 24) | (b << 16) | (c << 8) | d);
    sa->sin.sin_port = htons((uint16_t) port);
#if defined(USE_IPV6)
  } else if (sscanf(str, "[%49[^]]]:%d%n", buf, &port, &len) == 2 &&
             inet_pton(AF_INET6, buf, &sa->sin6.sin6_addr)) {
    // IPv6 address, e.g. [3ffe:2a00:100:7031::1]:8080
    sa->sin6.sin6_family = AF_INET6;
    sa->sin6.sin6_port = htons((uint16_t) port);
#endif
  } else if (sscanf(str, "%u%n", &port, &len) == 1) {
    // If only port is specified, bind to IPv4, INADDR_ANY
    sa->sin.sin_port = htons((uint16_t) port);
  } else {
    port = 0;   // Parsing failure. Make port invalid.

  return port > 0 && port < 0xffff && str[len] == '\0';
const char *mg_set_option(struct mg_server *server, const char *name,
                          const char *value) {
  int ind = get_option_index(name);
  const char *error_msg = NULL;

  if (ind < 0) {
    error_msg = "No such option";
  } else {
    if (server->config_options[ind] != NULL) {
      free(server->config_options[ind]);
    }
    server->config_options[ind] = mg_strdup(value);
    DBG(("%s => %s", name, value));

    if (ind == LISTENING_PORT) {
      if (server->listening_sock != INVALID_SOCKET) {
        closesocket(server->listening_sock);
      }
      parse_port_string(server->config_options[LISTENING_PORT], &server->lsa);
      server->listening_sock = open_listening_socket(&server->lsa);
      if (server->listening_sock == INVALID_SOCKET) {
        error_msg = "Cannot bind to port";
      } else {
        set_non_blocking_mode(server->listening_sock);
      }
Sergey Lyubka's avatar
Sergey Lyubka committed
    } else if (ind == RUN_AS_USER) {
#ifndef _WIN32
      struct passwd *pw;
      if ((pw = getpwnam(value)) == NULL) {
        error_msg = "Unknown user";
      } else if (setgid(pw->pw_gid) != 0) {
        error_msg = "setgid() failed";
      } else if (setuid(pw->pw_uid) != 0) {
        error_msg = "setuid() failed";
      }
#endif
  return error_msg;
}

const char *mg_get_option(const struct mg_server *server, const char *name) {
  const char **opts = (const char **) server->config_options;
  int i = get_option_index(name);
  return i == -1 ? NULL : opts[i] == NULL ? "" : opts[i];
}

struct mg_server *mg_create_server(void *server_data) {
  struct mg_server *server = (struct mg_server *) calloc(1, sizeof(*server));

#ifdef _WIN32
  WSADATA data;
  WSAStartup(MAKEWORD(2, 2), &data);
#else
  // Ignore SIGPIPE signal, so if browser cancels the request, it
  // won't kill the whole process.
  signal(SIGPIPE, SIG_IGN);
#endif

  LINKED_LIST_INIT(&server->active_connections);
  LINKED_LIST_INIT(&server->uri_handlers);
  mg_socketpair(server->ctl);
  server->server_data = server_data;
  server->listening_sock = INVALID_SOCKET;
  set_default_option_values(server->config_options);
  return server;