/** * g_mapped_file_get_bytes: * @file: a #GMappedFile * * Creates a new #GBytes which references the data mapped from @file. * The mapped contents of the file must not be modified after creating this * bytes object, because a #GBytes should be immutable. * * Returns: (transfer full): A newly allocated #GBytes referencing data * from @file * * Since: 2.34 **/ GBytes * g_mapped_file_get_bytes (GMappedFile *file) { g_return_val_if_fail (file != NULL, NULL); return g_bytes_new_with_free_func (file->contents, file->length, (GDestroyNotify) g_mapped_file_unref, g_mapped_file_ref (file)); }
static void prv_soup_server_cb(SoupServer *server, SoupMessage *msg, const char *path, GHashTable *query, SoupClientContext *client, gpointer user_data) { rsu_host_file_t *hf; rsu_host_server_t *hs = user_data; const gchar *file_name; SoupMessageHeaders *hdrs; if (msg->method != SOUP_METHOD_GET) { soup_message_set_status(msg, SOUP_STATUS_NOT_IMPLEMENTED); goto on_error; } hf = prv_host_server_find_file(hs, path, &file_name); if (!hf) { soup_message_set_status(msg, SOUP_STATUS_NOT_FOUND); goto on_error; } if (hf->mapped_file) { g_mapped_file_ref(hf->mapped_file); ++hf->mapped_count; } else { hf->mapped_file = g_mapped_file_new(file_name, FALSE, NULL); if (!hf->mapped_file) { soup_message_set_status(msg, SOUP_STATUS_NOT_FOUND); goto on_error; } hf->mapped_count = 1; } g_signal_connect(msg, "finished", G_CALLBACK(prv_soup_message_finished_cb), hf); g_object_get(msg, "response-headers", &hdrs, NULL); /* TODO: Need to add the relevant DLNA headers */ /* soup_message_headers_append(hdrs, "contentFeatures.dlna.org", "DLNA.ORG_PN=PNG_LRG;DLNA.ORG_OP=01;"DLNA.ORG_FLAGS=00f00000000000000000000000000000"); soup_message_headers_append(hdrs, "Connection", "close"); */ soup_message_set_status(msg, SOUP_STATUS_OK); soup_message_set_response(msg, hf->mime_type, SOUP_MEMORY_STATIC, g_mapped_file_get_contents(hf->mapped_file), g_mapped_file_get_length(hf->mapped_file)); on_error: return; }
TestHttp* test_http_new( GMappedFile* resp_file, const char* resp_content_type, int resp_status) { TestHttp* http = g_new0(TestHttp, 1); http->ref_count = 1; if (resp_file) { http->resp_file = g_mapped_file_ref(resp_file); http->resp_content_type = g_strdup(resp_content_type); } http->resp_status = resp_status; http->server = g_object_new(SOUP_TYPE_SERVER, NULL); MMS_DEBUG("Listening on port %hu", soup_server_get_port(http->server)); soup_server_add_handler(http->server, NULL, test_http_callback, http, NULL); soup_server_run_async(http->server); return http; }
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 prv_soup_server_cb(SoupServer *server, SoupMessage *msg, const char *path, GHashTable *query, SoupClientContext *client, gpointer user_data) { dlr_host_file_t *hf; dlr_host_server_t *hs = user_data; const gchar *file_name; const char *hdr; if ((msg->method != SOUP_METHOD_GET) && (msg->method != SOUP_METHOD_HEAD)) { soup_message_set_status(msg, SOUP_STATUS_NOT_IMPLEMENTED); goto on_error; } hf = prv_host_server_find_file(hs, path, &file_name); if (!hf) { soup_message_set_status(msg, SOUP_STATUS_NOT_FOUND); goto on_error; } hdr = soup_message_headers_get_one(msg->request_headers, "getContentFeatures.dlna.org"); if (hdr) { if (strcmp(hdr, "1") != 0) { soup_message_set_status(msg, SOUP_STATUS_BAD_REQUEST); goto on_error; } if ((hf->dlna_header) && strlen(hf->dlna_header) > 0) soup_message_headers_append(msg->response_headers, "contentFeatures.dlna.org", hf->dlna_header); } if (hf->mapped_file) { g_mapped_file_ref(hf->mapped_file); ++hf->mapped_count; } else { hf->mapped_file = g_mapped_file_new(file_name, FALSE, NULL); if (!hf->mapped_file) { soup_message_set_status(msg, SOUP_STATUS_NOT_FOUND); goto on_error; } hf->mapped_count = 1; } if (msg->method == SOUP_METHOD_GET) { g_signal_connect(msg, "finished", G_CALLBACK(prv_soup_message_finished_cb), hf); soup_message_set_response( msg, hf->mime_type, SOUP_MEMORY_STATIC, g_mapped_file_get_contents(hf->mapped_file), g_mapped_file_get_length(hf->mapped_file)); } else { soup_message_headers_set_content_type(msg->response_headers, hf->mime_type, NULL); soup_message_headers_set_content_length( msg->response_headers, g_mapped_file_get_length(hf->mapped_file)); } soup_message_set_status(msg, SOUP_STATUS_OK); on_error: 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; }