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; }
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; }
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; }
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 (¤t_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 (¤t_time); engine_stats = server_api_get_engine_stats (¤t_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); }
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; }