Beispiel #1
0
SnraServerClient *
snra_server_client_new (SoupServer * soup, SoupMessage * msg,
                        SoupClientContext * context)
{
    SnraServerClient *client = g_object_new (SNRA_TYPE_SERVER_CLIENT, NULL);
    const gchar *accept_challenge;
    gchar *accept_reply;

    client->soup = soup;
    client->event_pipe = msg;
    client->host = g_strdup (soup_client_context_get_host (context));

    client->net_event_sig = g_signal_connect (msg, "network-event",
                            G_CALLBACK (snra_server_client_network_event), client);
    client->disco_sig = g_signal_connect (msg, "finished",
                                          G_CALLBACK (snra_server_client_disconnect), client);

    if (!is_websocket_client (client)) {
        client->type = SNRA_SERVER_CLIENT_CHUNKED;
        client->need_body_complete = TRUE;

        soup_message_headers_set_encoding (msg->response_headers,
                                           SOUP_ENCODING_CHUNKED);
        soup_message_set_status (msg, SOUP_STATUS_OK);
        return client;
    }

    /* Otherwise, it's a websocket client */
    client->type = SNRA_SERVER_CLIENT_WEBSOCKET;
    client->need_body_complete = FALSE;

    client->socket = soup_client_context_get_socket (context);
    client->in_bufptr = client->in_buf = g_new0 (gchar, 1024);
    client->in_bufsize = 1024;
    client->in_bufavail = 0;

    accept_challenge =
        soup_message_headers_get_one (msg->request_headers, "Sec-WebSocket-Key");
    accept_reply = calc_websocket_challenge_reply (accept_challenge);

    soup_message_headers_set_encoding (msg->response_headers, SOUP_ENCODING_EOF);

    soup_message_set_status (msg, SOUP_STATUS_SWITCHING_PROTOCOLS);
    soup_message_headers_replace (msg->response_headers, "Upgrade", "websocket");
    soup_message_headers_replace (msg->response_headers, "Connection", "Upgrade");
    soup_message_headers_replace (msg->response_headers, "Sec-WebSocket-Accept",
                                  accept_reply);
    soup_message_headers_replace (msg->response_headers, "Sec-WebSocket-Protocol",
                                  "aurena");

    g_free (accept_reply);

    client->wrote_info_sig = g_signal_connect (msg, "wrote-informational",
                             G_CALLBACK (snra_server_client_wrote_headers), client);

    return client;
}
Beispiel #2
0
static void
timeout_request_started (SoupServer *server, SoupMessage *msg,
			 SoupClientContext *client, gpointer user_data)
{
	SoupSocket *sock;
	GMainContext *context = soup_server_get_async_context (server);
	guint readable;

	sock = soup_client_context_get_socket (client);
	readable = g_signal_connect (sock, "readable",
				    G_CALLBACK (timeout_socket), NULL);
	while (soup_socket_is_connected (sock))
		g_main_context_iteration (context, TRUE);
	g_signal_handler_disconnect (sock, readable);
	g_signal_handlers_disconnect_by_func (server, timeout_request_started, NULL);
}
static void
do_get (OtTrivialHttpd    *self,
        SoupServer        *server,
        SoupMessage       *msg,
        const char        *path,
        SoupClientContext *context)
{
  char *slash;
  int ret;
  struct stat stbuf;

  httpd_log (self, "serving %s\n", path);

  if (opt_expected_cookies)
    {
      GSList *cookies = soup_cookies_from_request (msg);
      GSList *l;
      int i;

      for (i = 0 ; opt_expected_cookies[i] != NULL; i++)
        {
          gboolean found = FALSE;
          gchar *k = opt_expected_cookies[i];
          gchar *v = strchr (k, '=') + 1;

          for (l = cookies;  l != NULL ; l = g_slist_next (l))
            {
              SoupCookie *c = l->data;

              if (!strncmp (k, soup_cookie_get_name (c), v - k - 1) &&
                  !strcmp (v, soup_cookie_get_value (c)))
                {
                  found = TRUE;
                  break;
                }
            }

          if (!found)
            {
              httpd_log (self, "Expected cookie not found %s\n", k);
              soup_message_set_status (msg, SOUP_STATUS_FORBIDDEN);
              soup_cookies_free (cookies);
              goto out;
            }
        }
      soup_cookies_free (cookies);
    }

  if (opt_expected_headers)
    {
      for (int i = 0 ; opt_expected_headers[i] != NULL; i++)
        {
          const gchar *kv = opt_expected_headers[i];
          const gchar *eq = strchr (kv, '=');

          g_assert (eq);

          {
            g_autofree char *k = g_strndup (kv, eq - kv);
            const gchar *expected_v = eq + 1;
            const gchar *found_v = soup_message_headers_get_one (msg->request_headers, k);

            if (!found_v)
              {
                httpd_log (self, "Expected header not found %s\n", k);
                soup_message_set_status (msg, SOUP_STATUS_FORBIDDEN);
                goto out;
              }
            if (strcmp (found_v, expected_v) != 0)
              {
                httpd_log (self, "Expected header %s: %s but found %s\n", k, expected_v, found_v);
                soup_message_set_status (msg, SOUP_STATUS_FORBIDDEN);
                goto out;
              }
          }
        }
    }

  if (strstr (path, "../") != NULL)
    {
      soup_message_set_status (msg, SOUP_STATUS_FORBIDDEN);
      goto out;
    }

  if (opt_random_500s_percentage > 0 &&
      emitted_random_500s_count < opt_random_500s_max &&
      g_random_int_range (0, 100) < opt_random_500s_percentage)
    {
      emitted_random_500s_count++;
      soup_message_set_status (msg, SOUP_STATUS_INTERNAL_SERVER_ERROR);
      goto out;
    }

  while (path[0] == '/')
    path++;

  do
    ret = fstatat (self->root_dfd, path, &stbuf, 0);
  while (ret == -1 && errno == EINTR);
  if (ret == -1)
    {
      if (errno == EPERM)
        soup_message_set_status (msg, SOUP_STATUS_FORBIDDEN);
      else if (errno == ENOENT)
        soup_message_set_status (msg, SOUP_STATUS_NOT_FOUND);
      else
        soup_message_set_status (msg, SOUP_STATUS_INTERNAL_SERVER_ERROR);
      goto out;
    }

  if (!is_safe_to_access (&stbuf))
    {
      soup_message_set_status (msg, SOUP_STATUS_FORBIDDEN);
      goto out;
    }

  if (S_ISDIR (stbuf.st_mode))
    {
      slash = strrchr (path, '/');
      if (!slash || slash[1])
        {
          g_autofree char *redir_uri = NULL;

          redir_uri = g_strdup_printf ("%s/", soup_message_get_uri (msg)->path);
          soup_message_set_redirect (msg, SOUP_STATUS_MOVED_PERMANENTLY,
                                     redir_uri);
        }
      else
        {
          g_autofree char *index_realpath = g_strconcat (path, "/index.html", NULL);
          if (fstatat (self->root_dfd, index_realpath, &stbuf, 0) != -1)
            {
              g_autofree char *index_path = g_strconcat (path, "/index.html", NULL);
              do_get (self, server, msg, index_path, context);
            }
          else
            {
              GString *listing = get_directory_listing (self->root_dfd, path);
              soup_message_set_response (msg, "text/html",
                                         SOUP_MEMORY_TAKE,
                                         listing->str, listing->len);
              soup_message_set_status (msg, SOUP_STATUS_OK);
              g_string_free (listing, FALSE);
            }
        }
    }
  else 
    {
      if (!S_ISREG (stbuf.st_mode))
        {
          soup_message_set_status (msg, SOUP_STATUS_FORBIDDEN);
          goto out;
        }
      
      if (msg->method == SOUP_METHOD_GET)
        {
          glnx_autofd int fd = -1;
          g_autoptr(GMappedFile) mapping = NULL;
          gsize buffer_length, file_size;
          SoupRange *ranges;
          int ranges_length;
          gboolean have_ranges;

          fd = openat (self->root_dfd, path, O_RDONLY | O_CLOEXEC);
          if (fd < 0)
            {
              soup_message_set_status (msg, SOUP_STATUS_INTERNAL_SERVER_ERROR);
              goto out;
            }

          mapping = g_mapped_file_new_from_fd (fd, FALSE, NULL);
          if (!mapping)
            {
              soup_message_set_status (msg, SOUP_STATUS_INTERNAL_SERVER_ERROR);
              goto out;
            }
          (void) close (fd); fd = -1;

          file_size = g_mapped_file_get_length (mapping);
          have_ranges = soup_message_headers_get_ranges(msg->request_headers, file_size, &ranges, &ranges_length);
          if (opt_force_ranges && !have_ranges && g_strrstr (path, "/objects") != NULL)
            {
              SoupSocket *sock;
              buffer_length = file_size/2;
              soup_message_headers_set_content_length (msg->response_headers, file_size);
              soup_message_headers_append (msg->response_headers,
                                           "Connection", "close");

              /* soup-message-io will wait for us to add
               * another chunk after the first, to fill out
               * the declared Content-Length. Instead, we
               * forcibly close the socket at that point.
               */
              sock = soup_client_context_get_socket (context);
              g_signal_connect (msg, "wrote-chunk", G_CALLBACK (close_socket), sock);
            }
          else
            buffer_length = file_size;

          if (have_ranges)
            {
              if (ranges_length > 0 && ranges[0].start >= file_size)
                {
                  soup_message_set_status (msg, SOUP_STATUS_REQUESTED_RANGE_NOT_SATISFIABLE);
                  soup_message_headers_free_ranges (msg->request_headers, ranges);
                  goto out;
                }
              soup_message_headers_free_ranges (msg->request_headers, ranges);
            }
          if (buffer_length > 0)
            {
              SoupBuffer *buffer;

              buffer = soup_buffer_new_with_owner (g_mapped_file_get_contents (mapping),
                                                   buffer_length,
                                                   g_mapped_file_ref (mapping),
                                                   (GDestroyNotify)g_mapped_file_unref);
              soup_message_body_append_buffer (msg->response_body, buffer);
              soup_buffer_free (buffer);
            }
        }
      else /* msg->method == SOUP_METHOD_HEAD */
        {
          g_autofree char *length = NULL;

          /* We could just use the same code for both GET and
           * HEAD (soup-message-server-io.c will fix things up).
           * But we'll optimize and avoid the extra I/O.
           */
          length = g_strdup_printf ("%lu", (gulong)stbuf.st_size);
          soup_message_headers_append (msg->response_headers,
                                       "Content-Length", length);
        }
      soup_message_set_status (msg, SOUP_STATUS_OK);
    }
 out:
  {
    guint status = 0;
    g_autofree gchar *reason = NULL;

    g_object_get (msg,
                  "status-code", &status,
                  "reason-phrase", &reason,
                  NULL);
    httpd_log (self, "  status: %s (%u)\n", reason, status);
  }
  return;
}
Beispiel #4
0
static void
server_callback (SoupServer *server, SoupMessage *msg,
		 const char *path, GHashTable *query,
		 SoupClientContext *context, gpointer data)
{
	/* The way this gets used in the tests, we don't actually
	 * need to hold it through the whole function, so it's simpler
	 * to just release it right away.
	 */
	g_mutex_lock (&server_mutex);
	g_mutex_unlock (&server_mutex);

	if (msg->method != SOUP_METHOD_GET && msg->method != SOUP_METHOD_POST) {
		soup_message_set_status (msg, SOUP_STATUS_NOT_IMPLEMENTED);
		return;
	}

	if (g_str_has_prefix (path, "/content-length/")) {
		gboolean too_long = strcmp (path, "/content-length/long") == 0;
		gboolean no_close = strcmp (path, "/content-length/noclose") == 0;

		soup_message_set_status (msg, SOUP_STATUS_OK);
		soup_message_set_response (msg, "text/plain",
					   SOUP_MEMORY_STATIC, "foobar", 6);
		if (too_long)
			soup_message_headers_set_content_length (msg->response_headers, 9);
		soup_message_headers_append (msg->response_headers,
					     "Connection", "close");

		if (too_long) {
			SoupSocket *sock;

			/* soup-message-io will wait for us to add
			 * another chunk after the first, to fill out
			 * the declared Content-Length. Instead, we
			 * forcibly close the socket at that point.
			 */
			sock = soup_client_context_get_socket (context);
			g_signal_connect (msg, "wrote-chunk",
					  G_CALLBACK (close_socket), sock);
		} else if (no_close) {
			/* Remove the 'Connection: close' after writing
			 * the headers, so that when we check it after
			 * writing the body, we'll think we aren't
			 * supposed to close it.
			 */
			g_signal_connect (msg, "wrote-headers",
					  G_CALLBACK (forget_close), NULL);
		}
		return;
	}

	if (!strcmp (path, "/timeout-persistent")) {
		SoupSocket *sock;

		sock = soup_client_context_get_socket (context);
		setup_timeout_persistent (server, sock);
	}

	soup_message_set_status (msg, SOUP_STATUS_OK);
	soup_message_set_response (msg, "text/plain",
				   SOUP_MEMORY_STATIC, "index", 5);
	return;
}
Beispiel #5
0
static void
server_callback (SoupServer *server, SoupMessage *msg,
		 const char *path, GHashTable *query,
		 SoupClientContext *client, gpointer data)
{
	GHashTable *connections = data;
	SoupSocket *socket;
	const char *auth;
	NTLMServerState state, required_user = 0;
	gboolean auth_required = FALSE, not_found = FALSE;
	gboolean basic_allowed = FALSE, ntlm_allowed = FALSE;

	if (msg->method != SOUP_METHOD_GET) {
		soup_message_set_status (msg, SOUP_STATUS_NOT_IMPLEMENTED);
		return;
	}

	if (!strncmp (path, "/alice", 6)) {
		auth_required = TRUE;
		ntlm_allowed = TRUE;
		required_user = NTLM_AUTHENTICATED_ALICE;
	} else if (!strncmp (path, "/bob", 4)) {
		auth_required = TRUE;
		ntlm_allowed = TRUE;
		required_user = NTLM_AUTHENTICATED_BOB;
	} else if (!strncmp (path, "/either", 7)) {
		auth_required = TRUE;
		ntlm_allowed = basic_allowed = TRUE;
	} else if (!strncmp (path, "/basic", 6)) {
		auth_required = TRUE;
		basic_allowed = TRUE;
	}

	if (strstr (path, "/404"))
		not_found = TRUE;

	socket = soup_client_context_get_socket (client);
	state = GPOINTER_TO_INT (g_hash_table_lookup (connections, socket));
	auth = soup_message_headers_get_one (msg->request_headers,
					     "Authorization");

	if (auth) {
		if (!strncmp (auth, "NTLM ", 5)) {
			if (!strncmp (auth + 5, NTLM_REQUEST_START,
				      strlen (NTLM_REQUEST_START))) {
				state = NTLM_RECEIVED_REQUEST;
				/* If they start, they must finish */
				auth_required = ntlm_allowed = TRUE;
				basic_allowed = FALSE;
			} else if (state == NTLM_SENT_CHALLENGE &&
				   !strncmp (auth + 5, NTLM_RESPONSE_START,
					     strlen (NTLM_RESPONSE_START))) {
				state = NTLM_RESPONSE_USER (auth + 5);
			} else
				state = NTLM_UNAUTHENTICATED;
		} else if (!strncmp (auth, "Basic ", 6) && basic_allowed) {
			gsize len;
			char *decoded = (char *)g_base64_decode (auth + 6, &len);

			if (!strncmp (decoded, "alice:password", len) ||
			    !strncmp (decoded, "bob:password", len))
				auth_required = FALSE;
			g_free (decoded);
		}
	}

	if (ntlm_allowed && state > NTLM_SENT_CHALLENGE &&
	    (!required_user || required_user == state))
		auth_required = FALSE;

	if (auth_required) {
		soup_message_set_status (msg, SOUP_STATUS_UNAUTHORIZED);

		if (basic_allowed) {
			soup_message_headers_append (msg->response_headers,
						     "WWW-Authenticate",
						     "Basic realm=\"ntlm-test\"");
		}

		if (state == NTLM_RECEIVED_REQUEST) {
			soup_message_headers_append (msg->response_headers,
						     "WWW-Authenticate",
						     "NTLM " NTLM_CHALLENGE);
			state = NTLM_SENT_CHALLENGE;
		} else if (ntlm_allowed) {
			soup_message_headers_append (msg->response_headers,
						     "WWW-Authenticate", "NTLM");
			soup_message_headers_append (msg->response_headers,
						     "Connection", "close");
		}
	} else {
		if (not_found)
			soup_message_set_status (msg, SOUP_STATUS_NOT_FOUND);
		else {
			soup_message_set_response (msg, "text/plain",
						   SOUP_MEMORY_STATIC,
						   "OK\r\n", 4);
			soup_message_set_status (msg, SOUP_STATUS_OK);
		}
	}

	g_hash_table_insert (connections, socket, GINT_TO_POINTER (state));
	g_object_weak_ref (G_OBJECT (socket), clear_state, connections);
}
static void
server_api_status_callback (SoupServer * server, SoupMessage * msg, const char *path, GHashTable * query, SoupClientContext * client, gpointer user_data)
{
  (void) server;
  (void) path;
  (void) query;
  (void) client;
  (void) user_data;
  char *response = NULL;
  GPtrArray *r_array = NULL;
  GPtrArray *unr_array = NULL;
  GPtrArray *connected_sensors = NULL;
  GHashTable *hash;
  GString *st_sensors = NULL;
  GString *st_unrsensors = NULL;
  GString *st_csensors = NULL;
  gchar *per_session_stats = NULL;
  gchar *engine_stats = NULL;
  gchar *server_stats = NULL;
  GTimeVal current_time;
  SoupSocket *sock = NULL;
  SoupAddress *sockaddr = NULL;
  SoupURI *uri = NULL;
  guint i;
  /* Only get methods and no params */
  g_get_current_time (&current_time);
  if (msg->method != SOUP_METHOD_GET || query != NULL)
  {
    soup_message_set_status (msg, SOUP_STATUS_NOT_IMPLEMENTED);
    return;
  }
 /* Verificación anti listos */
  if ((sock = soup_client_context_get_socket (client)) == NULL) 
  {
    soup_message_set_status (msg, SOUP_STATUS_INTERNAL_SERVER_ERROR);
    return;
  }
  /* If host == 127.0.0.1 or host == localhost, verify that the localaddress == 127.0.0.1 */
  if ((uri = soup_message_get_uri (msg)) == NULL)
  {
    soup_message_set_status (msg, SOUP_STATUS_INTERNAL_SERVER_ERROR);
    return;
  }
  if (strcmp (uri->host,"127.0.0.1") == 0 || strcmp (uri->host,"localhost") == 0)
  {
    if ((sockaddr = soup_socket_get_local_address (sock)) == NULL)
    {
      soup_message_set_status (msg, SOUP_STATUS_INTERNAL_SERVER_ERROR);
      return;
    }
    else
    {
      /* Aquí hay un listo */
      if (strcmp ( soup_address_get_physical(sockaddr),"127.0.0.1") != 0)
      {
        soup_message_set_status (msg, SOUP_STATUS_UNAUTHORIZED);
        return;
      }
    }
  }

  if (server_api_get_info_sensors (&r_array, &unr_array, &hash) == FALSE)
  {
    soup_message_set_status (msg, SOUP_STATUS_INTERNAL_SERVER_ERROR);
    return;
  }
/* Load the info of connected sensors */
  if (sim_server_get_sensor_uuids_unique (ossim.server, &connected_sensors) == FALSE)
  {
    soup_message_set_status (msg, SOUP_STATUS_INTERNAL_SERVER_ERROR);
    return;
  }


  if ((st_sensors = g_string_new ("")) == NULL)
  {
    soup_message_set_status (msg, SOUP_STATUS_INTERNAL_SERVER_ERROR);
    return;
  }
  if ((st_unrsensors = g_string_new ("")) == NULL)
  {
    soup_message_set_status (msg, SOUP_STATUS_INTERNAL_SERVER_ERROR);
    return;
  }
  if ((st_csensors = g_string_new ("")) == NULL)
  {
    soup_message_set_status (msg, SOUP_STATUS_INTERNAL_SERVER_ERROR);
    return;
  }


  /* For the register sensors */
  if (r_array->len > 0)
  {

    for (i = 0; i < r_array->len; i++)
    {
      g_string_append_printf (st_sensors, "\"%s\"", (gchar *) g_ptr_array_index (r_array, i));
      if ((i != (r_array->len - 1)))
      {
        g_string_append_c (st_sensors, ',');
      }
    }
    g_string_prepend_c (st_sensors, '[');
    g_string_append_c (st_sensors, ']');
  }
  else
  {
    g_string_printf (st_sensors, "[null]");
  }

  /* For the unregister sensors */
  if (unr_array->len > 0)
  {
    for (i = 0; i < unr_array->len; i++)
    {
      g_string_append_printf (st_unrsensors, "\"%s\"", (gchar *) g_ptr_array_index (unr_array, i));
      if ((i != (unr_array->len - 1)))
      {
        g_string_append_c (st_unrsensors, ',');
      }
    }
    g_string_prepend_c (st_unrsensors, '[');
    g_string_append_c (st_unrsensors, ']');
  }
  else
  {
    g_string_printf (st_unrsensors, "[null]");
  }
  /* The connected sensors */
  if (connected_sensors->len > 0)
  {
    int count = 0;
    for (i = 0; i < connected_sensors->len; i++)
    {
      char *av_uuid;
      if ((av_uuid = g_hash_table_lookup (hash, (gchar *) g_ptr_array_index (connected_sensors, i))) != NULL)
      {
        g_string_append_printf (st_csensors, "\"%s\"", av_uuid);
        count++;
        if ((i != (connected_sensors->len - 1)))
        {
          g_string_append_c (st_csensors, ',');
        }
      }
    }
    if (count > 0)
    {
      g_string_prepend_c (st_csensors, '[');
      g_string_append_c (st_csensors, ']');
    }
    else
    {
      g_string_printf (st_csensors, "[null]");

    }
  }
  else
  {
    g_string_printf (st_csensors, "[null]");
  }




  per_session_stats = sim_server_api_get_per_session_stats ();
  server_stats = server_api_get_server_stats (&current_time);
  engine_stats = server_api_get_engine_stats (&current_time);
  old_time.tv_sec = current_time.tv_sec;

response =
      g_strdup_printf ("{\"result\":{\"request\":\"/server/status\",\n\"timestamp\":\"%lld\",\n\"rsensors\":%s,\n\"unrsensors\":%s,\n\"csensors\":%s,\n\"engine_stats\":[%s],\n\"server_stats\":%s,\n\"per_session_stats\":%s\n},\n\"status\":\"OK\"\n}",
                       (long long) time (NULL),
                       st_sensors->str,
                       st_unrsensors->str,
                       st_csensors->str,
                       engine_stats,
                       server_stats,
                       per_session_stats
        );

  soup_message_set_response (msg, "application/json", SOUP_MEMORY_TAKE, response, strlen (response));
  soup_message_set_status (msg, SOUP_STATUS_OK);
  if (r_array != NULL)
    g_ptr_array_unref (r_array);
  if (unr_array != NULL)
    g_ptr_array_unref (unr_array);
  if (hash != NULL)
    g_hash_table_destroy (hash);
  if (st_sensors != NULL)
  {
    g_string_free (st_sensors, TRUE);
  }
  if (st_unrsensors != NULL)
  {
    g_string_free (st_unrsensors, TRUE);
  }
  if (st_csensors != NULL)
  {
    g_string_free (st_csensors, TRUE);
  }
  if (connected_sensors != NULL)
     g_ptr_array_unref (connected_sensors);
  g_free (per_session_stats);
  g_free (engine_stats);
  g_free (server_stats);
}
Beispiel #7
0
static void
server_callback (SoupServer *server, SoupMessage *msg,
		 const char *path, GHashTable *query,
		 SoupClientContext *context, gpointer data)
{
	SoupURI *uri = soup_message_get_uri (msg);

	soup_message_headers_append (msg->response_headers,
				     "X-Handled-By", "server_callback");

	if (!strcmp (path, "*")) {
		debug_printf (1, "    default server_callback got request for '*'!\n");
		errors++;
		soup_message_set_status (msg, SOUP_STATUS_INTERNAL_SERVER_ERROR);
		return;
	}

	if (msg->method != SOUP_METHOD_GET) {
		soup_message_set_status (msg, SOUP_STATUS_NOT_IMPLEMENTED);
		return;
	}

	if (!strcmp (path, "/redirect")) {
		soup_message_set_status (msg, SOUP_STATUS_FOUND);
		soup_message_headers_append (msg->response_headers,
					     /* Kids: don't try this at home!
					      * RFC2616 says to use an
					      * absolute URI!
					      */
					     "Location", "/");
		return;
	}

	if (g_str_has_prefix (path, "/content-length/")) {
		gboolean too_long = strcmp (path, "/content-length/long") == 0;
		gboolean no_close = strcmp (path, "/content-length/noclose") == 0;

		soup_message_set_status (msg, SOUP_STATUS_OK);
		soup_message_set_response (msg, "text/plain",
					   SOUP_MEMORY_STATIC, "foobar", 6);
		if (too_long)
			soup_message_headers_set_content_length (msg->response_headers, 9);
		soup_message_headers_append (msg->response_headers,
					     "Connection", "close");

		if (too_long) {
			SoupSocket *sock;

			/* soup-message-io will wait for us to add
			 * another chunk after the first, to fill out
			 * the declared Content-Length. Instead, we
			 * forcibly close the socket at that point.
			 */
			sock = soup_client_context_get_socket (context);
			g_signal_connect (msg, "wrote-chunk",
					  G_CALLBACK (close_socket), sock);
		} else if (no_close) {
			/* Remove the 'Connection: close' after writing
			 * the headers, so that when we check it after
			 * writing the body, we'll think we aren't
			 * supposed to close it.
			 */
			g_signal_connect (msg, "wrote-headers",
					  G_CALLBACK (forget_close), NULL);
		}
		return;
	}

	if (!strcmp (path, "/timeout-persistent")) {
		SoupSocket *sock;

		/* We time out the persistent connection as soon as
		 * the client starts writing the next request.
		 */
		sock = soup_client_context_get_socket (context);
		g_signal_connect (sock, "readable",
				  G_CALLBACK (timeout_socket), NULL);
	}

	soup_message_set_status (msg, SOUP_STATUS_OK);
	if (!strcmp (uri->host, "foo")) {
		soup_message_set_response (msg, "text/plain",
					   SOUP_MEMORY_STATIC, "foo-index", 9);
		return;
	} else {
		soup_message_set_response (msg, "text/plain",
					   SOUP_MEMORY_STATIC, "index", 5);
		return;
	}
}
static void
do_get (OtTrivialHttpd    *self,
        SoupServer        *server,
        SoupMessage       *msg,
        const char        *path,
        SoupClientContext *context)
{
  char *slash;
  int ret;
  struct stat stbuf;
  g_autofree char *safepath = NULL;

  httpd_log (self, "serving %s\n", path);
  if (strstr (path, "../") != NULL)
    {
      soup_message_set_status (msg, SOUP_STATUS_FORBIDDEN);
      goto out;
    }

  if (opt_random_500s_percentage > 0 &&
      emitted_random_500s_count < opt_random_500s_max &&
      g_random_int_range (0, 100) < opt_random_500s_percentage)
    {
      emitted_random_500s_count++;
      soup_message_set_status (msg, SOUP_STATUS_INTERNAL_SERVER_ERROR);
      goto out;
    }

  if (path[0] == '/')
    path++;

  safepath = g_build_filename (gs_file_get_path_cached (self->root), path, NULL);

  do
    ret = stat (safepath, &stbuf);
  while (ret == -1 && errno == EINTR);
  if (ret == -1)
    {
      if (errno == EPERM)
        soup_message_set_status (msg, SOUP_STATUS_FORBIDDEN);
      else if (errno == ENOENT)
        soup_message_set_status (msg, SOUP_STATUS_NOT_FOUND);
      else
        soup_message_set_status (msg, SOUP_STATUS_INTERNAL_SERVER_ERROR);
      goto out;
    }

  if (!is_safe_to_access (&stbuf))
    {
      soup_message_set_status (msg, SOUP_STATUS_FORBIDDEN);
      goto out;
    }

  if (S_ISDIR (stbuf.st_mode))
    {
      slash = strrchr (safepath, '/');
      if (!slash || slash[1])
        {
          g_autofree char *redir_uri = NULL;

          redir_uri = g_strdup_printf ("%s/", soup_message_get_uri (msg)->path);
          soup_message_set_redirect (msg, SOUP_STATUS_MOVED_PERMANENTLY,
                                     redir_uri);
        }
      else
        {
          g_autofree char *index_realpath = g_strconcat (safepath, "/index.html", NULL);
          if (stat (index_realpath, &stbuf) != -1)
            {
              g_autofree char *index_path = g_strconcat (path, "/index.html", NULL);
              do_get (self, server, msg, index_path, context);
            }
          else
            {
              GString *listing = get_directory_listing (safepath);
              soup_message_set_response (msg, "text/html",
                                         SOUP_MEMORY_TAKE,
                                         listing->str, listing->len);
              soup_message_set_status (msg, SOUP_STATUS_OK);
              g_string_free (listing, FALSE);
            }
        }
    }
  else 
    {
      if (!S_ISREG (stbuf.st_mode))
        {
          soup_message_set_status (msg, SOUP_STATUS_FORBIDDEN);
          goto out;
        }
      
      if (msg->method == SOUP_METHOD_GET)
        {
          g_autoptr(GMappedFile) mapping = NULL;
          gsize buffer_length, file_size;
          SoupRange *ranges;
          int ranges_length;
          gboolean have_ranges;

          mapping = g_mapped_file_new (safepath, FALSE, NULL);
          if (!mapping)
            {
              soup_message_set_status (msg, SOUP_STATUS_INTERNAL_SERVER_ERROR);
              goto out;
            }

          file_size = g_mapped_file_get_length (mapping);
          have_ranges = soup_message_headers_get_ranges(msg->request_headers, file_size, &ranges, &ranges_length);
          if (opt_force_ranges && !have_ranges && g_strrstr (path, "/objects") != NULL)
            {
              SoupSocket *sock;
              buffer_length = file_size/2;
              soup_message_headers_set_content_length (msg->response_headers, file_size);
              soup_message_headers_append (msg->response_headers,
                                           "Connection", "close");

              /* soup-message-io will wait for us to add
               * another chunk after the first, to fill out
               * the declared Content-Length. Instead, we
               * forcibly close the socket at that point.
               */
              sock = soup_client_context_get_socket (context);
              g_signal_connect (msg, "wrote-chunk", G_CALLBACK (close_socket), sock);
            }
          else
            buffer_length = file_size;

          if (have_ranges)
            {
              if (ranges_length > 0 && ranges[0].start >= file_size)
                {
                  soup_message_set_status (msg, SOUP_STATUS_REQUESTED_RANGE_NOT_SATISFIABLE);
                  soup_message_headers_free_ranges (msg->request_headers, ranges);
                  goto out;
                }
              soup_message_headers_free_ranges (msg->request_headers, ranges);
            }
          if (buffer_length > 0)
            {
              SoupBuffer *buffer;

              buffer = soup_buffer_new_with_owner (g_mapped_file_get_contents (mapping),
                                                   buffer_length,
                                                   g_mapped_file_ref (mapping),
                                                   (GDestroyNotify)g_mapped_file_unref);
              soup_message_body_append_buffer (msg->response_body, buffer);
              soup_buffer_free (buffer);
            }
        }
      else /* msg->method == SOUP_METHOD_HEAD */
        {
          g_autofree char *length = NULL;

          /* We could just use the same code for both GET and
           * HEAD (soup-message-server-io.c will fix things up).
           * But we'll optimize and avoid the extra I/O.
           */
          length = g_strdup_printf ("%lu", (gulong)stbuf.st_size);
          soup_message_headers_append (msg->response_headers,
                                       "Content-Length", length);
        }
      soup_message_set_status (msg, SOUP_STATUS_OK);
    }
 out:
  {
    guint status = 0;
    g_autofree gchar *reason = NULL;

    g_object_get (msg,
                  "status-code", &status,
                  "reason-phrase", &reason,
                  NULL);
    httpd_log (self, "  status: %s (%u)\n", reason, status);
  }
  return;
}