Пример #1
0
/**
 * soup_websocket_client_prepare_handshake:
 * @msg: a #SoupMessage
 * @origin: (allow-none): the "Origin" header to set
 * @protocols: (allow-none) (array zero-terminated=1): list of
 *   protocols to offer
 *
 * Adds the necessary headers to @msg to request a WebSocket
 * handshake. The message body and non-WebSocket-related headers are
 * not modified.
 *
 * This is a low-level function; if you use
 * soup_session_websocket_connect_async() to create a WebSocket
 * connection, it will call this for you.
 *
 * Since: 2.50
 */
void
soup_websocket_client_prepare_handshake (SoupMessage  *msg,
					 const char   *origin,
					 char        **protocols)
{
	guint32 raw[4];
	char *key;

	soup_message_headers_replace (msg->request_headers, "Upgrade", "websocket");
	soup_message_headers_append (msg->request_headers, "Connection", "Upgrade");

	raw[0] = g_random_int ();
	raw[1] = g_random_int ();
	raw[2] = g_random_int ();
	raw[3] = g_random_int ();
	key = g_base64_encode ((const guchar *)raw, sizeof (raw));
	soup_message_headers_replace (msg->request_headers, "Sec-WebSocket-Key", key);
	g_free (key);

	soup_message_headers_replace (msg->request_headers, "Sec-WebSocket-Version", "13");

	if (origin)
		soup_message_headers_replace (msg->request_headers, "Origin", origin);

	if (protocols) {
		char *protocols_str;

		protocols_str = g_strjoinv (", ", protocols);
		soup_message_headers_replace (msg->request_headers,
					      "Sec-WebSocket-Protocol", protocols_str);
		g_free (protocols_str);
	}
}
Пример #2
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;
}
void
gss_adaptive_get_resource (GssTransaction * t, GssAdaptive * adaptive,
    const char *path)
{
  gboolean failed;

  g_return_if_fail (t != NULL);
  g_return_if_fail (adaptive != NULL);
  g_return_if_fail (path != NULL);

  soup_message_headers_replace (t->msg->response_headers,
      "Access-Control-Allow-Origin", "*");
  soup_message_headers_replace (t->msg->response_headers,
      "Access-Control-Allow-Headers", "origin,range");
  soup_message_headers_replace (t->msg->response_headers,
      "Access-Control-Expose-Headers", "Server,range");
  soup_message_headers_replace (t->msg->response_headers,
      "Access-Control-Allow-Methods", "GET, HEAD, OPTIONS");

  failed = FALSE;
  switch (adaptive->stream_type) {
    case GSS_ADAPTIVE_STREAM_ISM:
      if (strcmp (path, "Manifest") == 0) {
        gss_adaptive_resource_get_manifest (t, adaptive);
      } else if (strcmp (path, "content") == 0) {
        gss_adaptive_resource_get_content (t, adaptive);
      } else {
        failed = TRUE;
      }
      break;
    case GSS_ADAPTIVE_STREAM_ISOFF_LIVE:
      if (strcmp (path, "manifest.mpd") == 0) {
        gss_adaptive_resource_get_dash_live_mpd (t, adaptive);
      } else if (strcmp (path, "content") == 0) {
        gss_adaptive_resource_get_content (t, adaptive);
      } else {
        failed = TRUE;
      }
      break;
    case GSS_ADAPTIVE_STREAM_ISOFF_ONDEMAND:
      if (strcmp (path, "manifest.mpd") == 0) {
        gss_adaptive_resource_get_dash_range_mpd (t, adaptive);
      } else if (strncmp (path, "content/", 8) == 0) {
        gss_adaptive_resource_get_dash_range_fragment (t, adaptive, path);
      } else {
        failed = TRUE;
      }
      break;
    default:
      failed = TRUE;
  }

  if (failed) {
    gss_transaction_error_not_found (t, "invalid path for stream type");
  }
}
Пример #4
0
static void
do_message_has_authorization_header_test (void)
{
	SoupSession *session;
	SoupMessage *msg;
	SoupAuthManager *manager;
	SoupAuth *auth = NULL;
	char *token;
	guint auth_id;
	char *uri;

	g_test_bug ("775882");

	SOUP_TEST_SKIP_IF_NO_APACHE;

	session = soup_test_session_new (SOUP_TYPE_SESSION, NULL);
	uri = g_strconcat (base_uri, "Digest/realm1/", NULL);

	msg = soup_message_new ("GET", uri);
	auth_id = g_signal_connect (session, "authenticate",
			  G_CALLBACK (has_authorization_header_authenticate), &auth);
	soup_session_send_message (session, msg);
	soup_test_assert_message_status (msg, SOUP_STATUS_OK);
	soup_test_assert (SOUP_IS_AUTH (auth), "Expected a SoupAuth");
	token = soup_auth_get_authorization (auth, msg);
	g_object_unref (auth);
	g_object_unref (msg);
	g_signal_handler_disconnect (session, auth_id);

	manager = SOUP_AUTH_MANAGER (soup_session_get_feature (session, SOUP_TYPE_AUTH_MANAGER));
	soup_auth_manager_clear_cached_credentials (manager);

	msg = soup_message_new ("GET", uri);
	soup_message_headers_replace (msg->request_headers, "Authorization", token);
	auth_id = g_signal_connect (session, "authenticate",
				    G_CALLBACK (has_authorization_header_authenticate_assert),
				    NULL);
	soup_session_send_message (session, msg);
	soup_test_assert_message_status (msg, SOUP_STATUS_OK);
	g_object_unref (msg);

	/* Check that we can also provide our own Authorization header when not using credentials cache. */
	soup_auth_manager_clear_cached_credentials (manager);
	msg = soup_message_new ("GET", uri);
	soup_message_headers_replace (msg->request_headers, "Authorization", token);
	soup_message_set_flags (msg, soup_message_get_flags (msg) | SOUP_MESSAGE_DO_NOT_USE_AUTH_CACHE);
	soup_session_send_message (session, msg);
	soup_test_assert_message_status (msg, SOUP_STATUS_OK);
	g_object_unref (msg);
	g_free (token);
	g_signal_handler_disconnect (session, auth_id);

	g_free (uri);
	soup_test_session_abort_unref (session);
}
Пример #5
0
static void
_picasa_web_service_add_headers (PicasaWebService *self,
				 SoupMessage      *msg)
{
	if (self->priv->access_token != NULL) {
		char *value;

		value = g_strconcat ("Bearer ", self->priv->access_token, NULL);
		soup_message_headers_replace (msg->request_headers, "Authorization", value);
		g_free (value);
	}

	soup_message_headers_replace (msg->request_headers, "GData-Version", "2");
}
Пример #6
0
/**
 * Callback for web pages send-request signal.
 */
static gboolean on_web_page_send_request(WebKitWebPage *webpage, WebKitURIRequest *request,
        WebKitURIResponse *response, gpointer extension)
{
    char *name, *value;
    SoupMessageHeaders *headers;
    GHashTableIter iter;

    if (!ext.headers) {
        return FALSE;
    }

    /* Change request headers according to the users preferences. */
    headers = webkit_uri_request_get_http_headers(request);
    if (!headers) {
        return FALSE;
    }

    g_hash_table_iter_init(&iter, ext.headers);
    while (g_hash_table_iter_next(&iter, (gpointer*)&name, (gpointer*)&value)) {
        /* Null value is used to indicate that the header should be
         * removed completely. */
        if (value == NULL) {
            soup_message_headers_remove(headers, name);
        } else {
            soup_message_headers_replace(headers, name, value);
        }
    }

    return FALSE;
}
/**
 * soup_message_headers_set_ranges:
 * @hdrs: a #SoupMessageHeaders
 * @ranges: an array of #SoupRange
 * @length: the length of @range
 *
 * Sets @hdrs's Range header to request the indicated ranges. (If you
 * only want to request a single range, you can use
 * soup_message_headers_set_range().)
 *
 * Since: 2.26
 **/
void
soup_message_headers_set_ranges (SoupMessageHeaders  *hdrs,
				 SoupRange           *ranges,
				 int                  length)
{
	GString *header;
	int i;

	header = g_string_new ("bytes=");
	for (i = 0; i < length; i++) {
		if (i > 0)
			g_string_append_c (header, ',');
		if (ranges[i].end >= 0) {
			g_string_append_printf (header, "%" G_GINT64_FORMAT "-%" G_GINT64_FORMAT,
						ranges[i].start, ranges[i].end);
		} else if (ranges[i].start >= 0) {
			g_string_append_printf (header,"%" G_GINT64_FORMAT "-",
						ranges[i].start);
		} else {
			g_string_append_printf (header, "%" G_GINT64_FORMAT,
						ranges[i].start);
		}
	}

	soup_message_headers_replace (hdrs, "Range", header->str);
	g_string_free (header, TRUE);
}
/**
 * soup_message_headers_set_encoding:
 * @hdrs: a #SoupMessageHeaders
 * @encoding: a #SoupEncoding
 *
 * Sets the message body encoding that @hdrs will declare. In particular,
 * you should use this if you are going to send a request or response in
 * chunked encoding.
 **/
void
soup_message_headers_set_encoding (SoupMessageHeaders *hdrs,
				   SoupEncoding        encoding)
{
	if (encoding == hdrs->encoding)
		return;

	switch (encoding) {
	case SOUP_ENCODING_NONE:
	case SOUP_ENCODING_EOF:
		soup_message_headers_remove (hdrs, "Transfer-Encoding");
		soup_message_headers_remove (hdrs, "Content-Length");
		break;

	case SOUP_ENCODING_CONTENT_LENGTH:
		soup_message_headers_remove (hdrs, "Transfer-Encoding");
		break;

	case SOUP_ENCODING_CHUNKED:
		soup_message_headers_remove (hdrs, "Content-Length");
		soup_message_headers_replace (hdrs, "Transfer-Encoding", "chunked");
		break;

	default:
		g_return_if_reached ();
	}

	hdrs->encoding = encoding;
}
/**
 * soup_message_headers_set_expectations:
 * @hdrs: a #SoupMessageHeaders
 * @expectations: the expectations to set
 *
 * Sets @hdrs's "Expect" header according to @expectations.
 *
 * Currently %SOUP_EXPECTATION_CONTINUE is the only known expectation
 * value. You should set this value on a request if you are sending a
 * large message body (eg, via POST or PUT), and want to give the
 * server a chance to reject the request after seeing just the headers
 * (eg, because it will require authentication before allowing you to
 * post, or because you're POSTing to a URL that doesn't exist). This
 * saves you from having to transmit the large request body when the
 * server is just going to ignore it anyway.
 **/
void
soup_message_headers_set_expectations (SoupMessageHeaders *hdrs,
				       SoupExpectation     expectations)
{
	g_return_if_fail ((expectations & ~SOUP_EXPECTATION_CONTINUE) == 0);

	if (expectations & SOUP_EXPECTATION_CONTINUE)
		soup_message_headers_replace (hdrs, "Expect", "100-continue");
	else
		soup_message_headers_remove (hdrs, "Expect");
}
/**
 * soup_message_headers_set_content_length:
 * @hdrs: a #SoupMessageHeaders
 * @content_length: the message body length
 *
 * Sets the message body length that @hdrs will declare, and sets
 * @hdrs's encoding to %SOUP_ENCODING_CONTENT_LENGTH.
 *
 * You do not normally need to call this; if @hdrs is set to use
 * Content-Length encoding, libsoup will automatically set its
 * Content-Length header for you immediately before sending the
 * headers. One situation in which this method is useful is when
 * generating the response to a HEAD request; Calling
 * soup_message_headers_set_content_length() allows you to put the
 * correct content length into the response without needing to waste
 * memory by filling in a response body which won't actually be sent.
 **/
void
soup_message_headers_set_content_length (SoupMessageHeaders *hdrs,
					 goffset             content_length)
{
	char length[128];

	snprintf (length, sizeof (length), "%" G_GUINT64_FORMAT,
		  content_length);
	soup_message_headers_remove (hdrs, "Transfer-Encoding");
	soup_message_headers_replace (hdrs, "Content-Length", length);
}
Пример #11
0
void serverHandleJournal(SoupServer *server,
                         SoupMessage *msg,
                         const char *path,
                         GHashTable *query,
                         SoupClientContext *client,
                         gpointer user_data)
{
    bool ok = false;
    if(strcmp(path, "/journal/journal.html") == 0) {

        GDBusProxy *journal = g_dbus_proxy_new_for_bus_sync(
            G_BUS_TYPE_SESSION, G_DBUS_PROXY_FLAGS_NONE, NULL,
            "org.laptop.sugar.DataStore",
            "/org/laptop/sugar/DataStore",
            "org.laptop.sugar.DataStore",
            NULL, NULL);
        GVariant *params = g_variant_new("(a{sv}as)", NULL, NULL);
        GError *error = NULL;
        GVariant *result = g_dbus_proxy_call_sync(
            journal, "find", params, G_DBUS_CALL_FLAGS_NONE, -1, NULL, &error);
        if(error != NULL) {fprintf(stderr, "error: %d, %d, %s\n", error->domain, error->code, error->message);}
        if(result != NULL) {

            soup_message_headers_replace(msg->response_headers, "Content-Type", "text/html");
            append(msg, "<html><body>");

            GVariant *results = NULL;
            guint32 count = -1;
            g_variant_get(result, "(@aa{sv}u)", &results, &count);
            GVariant *dictionary = NULL;
            GVariantIter results_iter;
            g_variant_iter_init(&results_iter, results);
            while (g_variant_iter_loop(&results_iter, "@a{sv}", &dictionary)) {
                append(msg, "<p><a href='");
                append(msg, lookup(dictionary, "uid", "invalid object id"));
                append(msg, "'>");
                append(msg, lookup(dictionary, "title", "invalid title"));
                append(msg, "</a></p>");
            }

            append(msg, "</body></html>");
            soup_message_set_status(msg, 200);
            fprintf(stderr, "GET: %s\n", path);
            ok = true;
        }
    
    }
    if(!ok) {
        soup_message_set_status(msg, 404);
        fprintf(stderr, "ERROR: %s\n", path);
    }
}
Пример #12
0
static void
server_callback (SoupServer *server, SoupMessage *msg,
		 const char *path, GHashTable *query,
		 SoupClientContext *context, gpointer data)
{
	if (g_str_equal (path, "/index.html")) {
		soup_message_headers_replace (msg->response_headers,
					      "Set-Cookie",
					      "foo=bar");
	} else if (g_str_equal (path, "/foo.jpg")) {
		soup_message_headers_replace (msg->response_headers,
					      "Set-Cookie",
					      "baz=qux");
	} else if (soup_message_headers_get_one (msg->request_headers,
						 "Echo-Set-Cookie")) {
		soup_message_headers_replace (msg->response_headers,
					      "Set-Cookie",
					      soup_message_headers_get_one (msg->request_headers,
									    "Echo-Set-Cookie"));
	}

	soup_message_set_status (msg, SOUP_STATUS_OK);
}
Пример #13
0
static void
md5_post_callback (SoupServer *server, SoupMessage *msg,
                   const char *path, GHashTable *query,
                   SoupClientContext *context, gpointer data)
{
    const char *content_type;
    GHashTable *params;
    const char *fmt;
    char *filename, *md5sum, *redirect_uri;
    SoupBuffer *file;
    SoupURI *uri;

    content_type = soup_message_headers_get_content_type (msg->request_headers, NULL);
    if (!content_type || strcmp (content_type, "multipart/form-data") != 0) {
        soup_message_set_status (msg, SOUP_STATUS_BAD_REQUEST);
        return;
    }

    params = soup_form_decode_multipart (msg, "file",
                                         &filename, NULL, &file);
    if (!params) {
        soup_message_set_status (msg, SOUP_STATUS_BAD_REQUEST);
        return;
    }
    fmt = g_hash_table_lookup (params, "fmt");

    md5sum = g_compute_checksum_for_data (G_CHECKSUM_MD5,
                                          (gpointer)file->data,
                                          file->length);
    soup_buffer_free (file);

    uri = soup_uri_copy (soup_message_get_uri (msg));
    soup_uri_set_query_from_fields (uri,
                                    "file", filename ? filename : "",
                                    "md5sum", md5sum,
                                    "fmt", fmt ? fmt : "html",
                                    NULL);
    redirect_uri = soup_uri_to_string (uri, FALSE);

    soup_message_set_status (msg, SOUP_STATUS_SEE_OTHER);
    soup_message_headers_replace (msg->response_headers, "Location",
                                  redirect_uri);

    g_free (redirect_uri);
    soup_uri_free (uri);
    g_free (md5sum);
    g_free (filename);
    g_hash_table_destroy (params);
}
Пример #14
0
void
video_server (SoupServer *server, SoupMessage *msg, const char *path,
		GHashTable *query, SoupClientContext *client, gpointer user_data)
{
	const gchar *contents = "dummy_video_content";
	gsize length = strlen (contents);
	gchar *mime_type = NULL;

	if (!g_strcmp0 (path, "/video/dummy_mp4"))
		mime_type = "video/mp4";
	else if (!g_strcmp0 (path, "/video/dummy_mov"))
		mime_type = "video/mov";

	if (mime_type) {
		soup_message_set_status (msg, SOUP_STATUS_OK);
		if (g_strcmp0 (msg->method, "HEAD"))
			soup_message_set_response (msg, 
				mime_type, 
				SOUP_MEMORY_COPY, 
				contents, 
				length);
		return;
	}

	if (!g_strcmp0 (path, "/video/youtube")) {
		gchar *page_contents;
		gsize length;
		if (!g_file_get_contents (BASEFILEPATH "/watch?v=SiYurfwzyuY", &page_contents, &length, NULL)) {
			g_error ("Couldn't serve video file");
		}
		soup_message_set_status (msg, SOUP_STATUS_OK);
		soup_message_set_response (msg, "text/html", SOUP_MEMORY_COPY, page_contents, length);
		return;
	}

	if (!g_strcmp0 (path, "/video/file_with_slash")) {
		gchar *page_contents = "dummy";
		gsize length = g_utf8_strlen ("dummy", -1);
		soup_message_set_status (msg, SOUP_STATUS_OK);
		soup_message_set_response (msg, "video/none", SOUP_MEMORY_COPY, page_contents, length);
		soup_message_headers_replace (msg->response_headers, "Content-Disposition", "attachment; filename=\"dir/file.vid\";");
		return;
	}
	soup_message_set_status (msg, SOUP_STATUS_NOT_FOUND);
}
/**
 * soup_message_headers_set_content_range:
 * @hdrs: a #SoupMessageHeaders
 * @start: the start of the range
 * @end: the end of the range
 * @total_length: the total length of the resource, or -1 if unknown
 *
 * Sets @hdrs's Content-Range header according to the given values.
 * (Note that @total_length is the total length of the entire resource
 * that this is a range of, not simply @end - @start + 1.)
 *
 * Since: 2.26
 **/
void
soup_message_headers_set_content_range (SoupMessageHeaders  *hdrs,
					goffset              start,
					goffset              end,
					goffset              total_length)
{
	char *header;

	if (total_length >= 0) {
		header = g_strdup_printf ("bytes %" G_GINT64_FORMAT "-%"
					  G_GINT64_FORMAT "/%" G_GINT64_FORMAT,
					  start, end, total_length);
	} else {
		header = g_strdup_printf ("bytes %" G_GINT64_FORMAT "-%"
					  G_GINT64_FORMAT "/*", start, end);
	}
	soup_message_headers_replace (hdrs, "Content-Range", header);
	g_free (header);
}
static void
set_content_foo (SoupMessageHeaders *hdrs, const char *header_name,
		 const char *foo, GHashTable *params)
{
	GString *str;
	GHashTableIter iter;
	gpointer key, value;

	str = g_string_new (foo);
	if (params) {
		g_hash_table_iter_init (&iter, params);
		while (g_hash_table_iter_next (&iter, &key, &value)) {
			g_string_append (str, "; ");
			soup_header_g_string_append_param (str, key, value);
		}
	}

	soup_message_headers_replace (hdrs, header_name, str->str);
	g_string_free (str, TRUE);
}
Пример #17
0
/* Host header handling: client must be able to override the default
 * value, server must be able to recognize different Host values.
 * #539803.
 */
static void
do_host_test (void)
{
	SoupSession *session;
	SoupMessage *one, *two;

	debug_printf (1, "Host handling\n");

	session = soup_test_session_new (SOUP_TYPE_SESSION_SYNC, NULL);

	one = soup_message_new_from_uri ("GET", base_uri);
	two = soup_message_new_from_uri ("GET", base_uri);
	soup_message_headers_replace (two->request_headers, "Host", "foo");

	soup_session_send_message (session, one);
	soup_session_send_message (session, two);

	soup_test_session_abort_unref (session);

	if (!SOUP_STATUS_IS_SUCCESSFUL (one->status_code)) {
		debug_printf (1, "  Message 1 failed: %d %s\n",
			      one->status_code, one->reason_phrase);
		errors++;
	} else if (strcmp (one->response_body->data, "index") != 0) {
		debug_printf (1, "  Unexpected response to message 1: '%s'\n",
			      one->response_body->data);
		errors++;
	}
	g_object_unref (one);

	if (!SOUP_STATUS_IS_SUCCESSFUL (two->status_code)) {
		debug_printf (1, "  Message 2 failed: %d %s\n",
			      two->status_code, two->reason_phrase);
		errors++;
	} else if (strcmp (two->response_body->data, "foo-index") != 0) {
		debug_printf (1, "  Unexpected response to message 2: '%s'\n",
			      two->response_body->data);
		errors++;
	}
	g_object_unref (two);
}
Пример #18
0
static void
server_callback (SoupServer *server, SoupMessage *msg,
		 const char *path, GHashTable *query,
		 SoupClientContext *context, gpointer data)
{
	SoupMessageBody *md5_body;
	char *md5;

	if (g_str_has_prefix (path, "/redirect")) {
		soup_message_set_status (msg, SOUP_STATUS_FOUND);
		soup_message_headers_replace (msg->response_headers,
					      "Location", "/");
		return;
	}

	if (msg->method == SOUP_METHOD_GET) {
		soup_message_set_response (msg, "text/plain",
					   SOUP_MEMORY_STATIC,
					   "three\r\ntwo\r\none\r\n",
					   strlen ("three\r\ntwo\r\none\r\n"));
		soup_buffer_free (soup_message_body_flatten (msg->response_body));
		md5_body = msg->response_body;
		soup_message_set_status (msg, SOUP_STATUS_OK);
	} else if (msg->method == SOUP_METHOD_PUT) {
		soup_message_set_status (msg, SOUP_STATUS_CREATED);
		md5_body = msg->request_body;
	} else {
		soup_message_set_status (msg, SOUP_STATUS_METHOD_NOT_ALLOWED);
		return;
	}

	md5 = g_compute_checksum_for_data (G_CHECKSUM_MD5,
					   (guchar *)md5_body->data,
					   md5_body->length);
	soup_message_headers_append (msg->response_headers,
				     "Content-MD5", md5);
	g_free (md5);
}
Пример #19
0
void
soup_session_send_queue_item (SoupSession *session,
			      SoupMessageQueueItem *item,
			      SoupConnection *conn)
{
	SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);

	if (item->conn) {
		if (item->conn != conn) {
			g_object_unref (item->conn);
			item->conn = g_object_ref (conn);
		}
	} else
		item->conn = g_object_ref (conn);

	if (priv->user_agent) {
		soup_message_headers_replace (item->msg->request_headers,
					      "User-Agent", priv->user_agent);
	}

	g_signal_emit (session, signals[REQUEST_STARTED], 0,
		       item->msg, soup_connection_get_socket (conn));
	soup_connection_send_request (conn, item->msg);
}
Пример #20
0
/**
 * soup_websocket_server_process_handshake:
 * @msg: #SoupMessage containing the client side of a WebSocket handshake
 * @expected_origin: (allow-none): expected Origin header
 * @protocols: (allow-none) (array zero-terminated=1): allowed WebSocket
 *   protocols.
 *
 * Examines the method and request headers in @msg and (assuming @msg
 * contains a valid handshake request), fills in the handshake
 * response.
 *
 * If @expected_origin is non-%NULL, then only requests containing a matching
 * "Origin" header will be accepted. If @protocols is non-%NULL, then
 * only requests containing a compatible "Sec-WebSocket-Protocols"
 * header will be accepted.
 *
 * This is a low-level function; if you use
 * soup_server_add_websocket_handler() to handle accepting WebSocket
 * connections, it will call this for you.
 *
 * Returns: %TRUE if @msg contained a valid WebSocket handshake
 *   request and was updated to contain a handshake response. %FALSE if not.
 *
 * Since: 2.50
 */
gboolean
soup_websocket_server_process_handshake (SoupMessage  *msg,
					 const char   *expected_origin,
					 char        **protocols)
{
	const char *chosen_protocol = NULL;
	const char *key;
	char *accept_key;
	GError *error = NULL;

	if (!soup_websocket_server_check_handshake (msg, expected_origin, protocols, &error)) {
		if (g_error_matches (error,
				     SOUP_WEBSOCKET_ERROR,
				     SOUP_WEBSOCKET_ERROR_BAD_ORIGIN))
			respond_handshake_forbidden (msg);
		else
			respond_handshake_bad (msg, error->message);
		g_error_free (error);
		return FALSE;
	}

	soup_message_set_status (msg, SOUP_STATUS_SWITCHING_PROTOCOLS);
	soup_message_headers_replace (msg->response_headers, "Upgrade", "websocket");
	soup_message_headers_append (msg->response_headers, "Connection", "Upgrade");

	key = soup_message_headers_get_one (msg->request_headers, "Sec-WebSocket-Key");
	accept_key = compute_accept_key (key);
	soup_message_headers_append (msg->response_headers, "Sec-WebSocket-Accept", accept_key);
	g_free (accept_key);

	choose_subprotocol (msg, (const char **) protocols, &chosen_protocol);
	if (chosen_protocol)
		soup_message_headers_append (msg->response_headers, "Sec-WebSocket-Protocol", chosen_protocol);

	return TRUE;
}
Пример #21
0
static void
request_started(SoupSessionFeature *feature, SoupSession *session,
        SoupMessage *msg, SoupSocket *socket)
{
    (void) session;
    (void) socket;
    SoupCookieJar *sj = SOUP_COOKIE_JAR(feature);
    SoupURI *uri = soup_message_get_uri(msg);
    lua_State *L = globalconf.L;

    /* give user a chance to add cookies from other instances into the jar */
    gchar *str = soup_uri_to_string(uri, FALSE);
    lua_pushstring(L, str);
    g_free(str);
    signal_object_emit(L, soupconf.signals, "request-started", 1, 0);

    /* generate cookie header */
    gchar *header = soup_cookie_jar_get_cookies(sj, uri, TRUE);
    if (header) {
        soup_message_headers_replace(msg->request_headers, "Cookie", header);
        g_free(header);
    } else
        soup_message_headers_remove(msg->request_headers, "Cookie");
}
static void
gss_adaptive_resource_get_dash_live_mpd (GssTransaction * t,
    GssAdaptive * adaptive)
{
  GString *s = g_string_new ("");
  int i;
  ManifestQuery mq;

  parse_manifest_query (&mq, t);

  t->s = s;

  soup_message_headers_replace (t->msg->response_headers, "Content-Type",
      "application/octet-stream");

  GSS_P ("<?xml version=\"1.0\" encoding=\"utf-8\"?>\n");
  GSS_A ("<MPD xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n"
      "  xmlns=\"urn:mpeg:dash:schema:mpd:2011\"\n");
  if (adaptive->drm_type == GSS_DRM_PLAYREADY) {
    GSS_A ("  xmlns:mspr=\"urn:microsoft:playready\"\n");
  }
  GSS_P ("  xsi:schemaLocation=\"urn:mpeg:dash:schema:mpd:2011 DASH-MPD.xsd\"\n"
      "  type=\"static\"\n"
      "  mediaPresentationDuration=\"PT%dS\"\n"
      "  minBufferTime=\"PT10S\"\n"
      "  profiles=\"urn:mpeg:dash:profile:isoff-live:2011\">\n",
      (int) (adaptive->duration / GSS_ISM_SECOND));
  GSS_P ("  <Period>\n");

  GSS_A ("    <AdaptationSet " "id=\"1\" "
      "profiles=\"ccff\" "
      "bitstreamSwitching=\"true\" "
      "segmentAlignment=\"true\" "
      "contentType=\"audio\" " "mimeType=\"audio/mp4\" " "lang=\"en\">\n");
  append_content_protection (t, adaptive, mq.auth_token);
  GSS_A ("    <SegmentTemplate timescale=\"10000000\" "
      "media=\"content?stream=audio&amp;bitrate=$Bandwidth$&amp;start_time=$Time$\" "
      "initialization=\"content?stream=audio&amp;bitrate=$Bandwidth$&amp;start_time=init\">\n");
  GSS_A ("      <SegmentTimeline>\n");
  {
    GssAdaptiveLevel *level = &adaptive->audio_levels[0];

    for (i = 0; i < level->n_fragments; i++) {
      GssIsomFragment *fragment;
      fragment = gss_isom_track_get_fragment (level->track, i);
      GSS_P ("        <S d=\"%" G_GUINT64_FORMAT "\" />\n",
          (guint64) fragment->duration);
    }
  }
  GSS_A ("      </SegmentTimeline>\n");
  GSS_A ("    </SegmentTemplate>\n");
  for (i = 0; i < adaptive->n_audio_levels; i++) {
    GssAdaptiveLevel *level = &adaptive->audio_levels[i];

    GSS_P ("      <Representation id=\"a%d\" codecs=\"%s\" "
        "bandwidth=\"%d\" audioSamplingRate=\"%d\"/>\n",
        i, level->codec, level->bitrate, level->audio_rate);
  }
  GSS_A ("    </AdaptationSet>\n");

  GSS_P ("    <AdaptationSet " "id=\"2\" "
      "profiles=\"ccff\" "
      "bitstreamSwitching=\"true\" "
      "segmentAlignment=\"true\" "
      "contentType=\"video\" "
      "mimeType=\"video/mp4\" "
      "maxWidth=\"1920\" " "maxHeight=\"1080\" " "startWithSAP=\"1\">\n");
  append_content_protection (t, adaptive, mq.auth_token);

  GSS_A ("    <SegmentTemplate timescale=\"10000000\" "
      "media=\"content?stream=video&amp;bitrate=$Bandwidth$&amp;start_time=$Time$\" "
      "initialization=\"content?stream=video&amp;bitrate=$Bandwidth$&amp;start_time=init\">\n");
  GSS_A ("      <SegmentTimeline>\n");
  {
    GssAdaptiveLevel *level = &adaptive->video_levels[0];

    for (i = 0; i < level->n_fragments; i++) {
      GssIsomFragment *fragment;
      fragment = gss_isom_track_get_fragment (level->track, i);
      GSS_P ("        <S d=\"%" G_GUINT64_FORMAT "\" />\n",
          (guint64) fragment->duration);
    }
  }
  GSS_A ("      </SegmentTimeline>\n");
  GSS_A ("    </SegmentTemplate>\n");
  for (i = 0; i < adaptive->n_video_levels; i++) {
    GssAdaptiveLevel *level = &adaptive->video_levels[i];

    if (manifest_query_check_video (&mq, level)) {
      GSS_P ("      <Representation id=\"v%d\" bandwidth=\"%d\" "
          "codecs=\"%s\" width=\"%d\" height=\"%d\"/>\n",
          i, level->bitrate, level->codec,
          level->video_width, level->video_height);
    }
  }
  GSS_A ("    </AdaptationSet>\n");

  GSS_A ("  </Period>\n");
  GSS_A ("</MPD>\n");
  GSS_A ("\n");

}
Пример #23
0
static void
got_headers (SoupMessage *req, SoupClientContext *client)
{
	SoupServer *server = client->server;
	SoupServerPrivate *priv = SOUP_SERVER_GET_PRIVATE (server);
	SoupURI *uri;
	SoupDate *date;
	char *date_string;
	SoupAuthDomain *domain;
	GSList *iter;
	gboolean rejected = FALSE;
	char *auth_user;

	if (!priv->raw_paths) {
		char *decoded_path;

		uri = soup_message_get_uri (req);
		decoded_path = soup_uri_decode (uri->path);
		soup_uri_set_path (uri, decoded_path);
		g_free (decoded_path);
	}

	/* Add required response headers */
	date = soup_date_new_from_now (0);
	date_string = soup_date_to_string (date, SOUP_DATE_HTTP);
	soup_message_headers_replace (req->response_headers, "Date",
				      date_string);
	g_free (date_string);
	soup_date_free (date);
	
	/* Now handle authentication. (We do this here so that if
	 * the request uses "Expect: 100-continue", we can reject it
	 * immediately rather than waiting for the request body to
	 * be sent.
	 */
	for (iter = priv->auth_domains; iter; iter = iter->next) {
		domain = iter->data;

		if (soup_auth_domain_covers (domain, req)) {
			auth_user = soup_auth_domain_accepts (domain, req);
			if (auth_user) {
				client->auth_domain = g_object_ref (domain);
				client->auth_user = auth_user;
				return;
			}

			rejected = TRUE;
		}
	}

	/* If no auth domain rejected it, then it's ok. */
	if (!rejected)
		return;

	for (iter = priv->auth_domains; iter; iter = iter->next) {
		domain = iter->data;

		if (soup_auth_domain_covers (domain, req))
			soup_auth_domain_challenge (domain, req);
	}
}
Пример #24
0
/**
 * youtube_proxy_upload_async:
 * @self: a #YoutubeProxy
 * @filename: filename
 * @fields: fields
 * @incomplete: incomplete
 * @callback: (scope async): callback to invoke upon completion
 * @weak_object: an object instance used to tie the life cycle of the proxy to 
 * @user_data: user data to pass to the callback
 * @error: a #GError pointer, or %NULL
 *
 * Upload a file.
 *
 * Returns: %TRUE, or %FALSE if the file could not be opened
 */
gboolean
youtube_proxy_upload_async (YoutubeProxy              *self,
                            const gchar               *filename,
                            GHashTable                *fields,
                            gboolean                   incomplete,
                            YoutubeProxyUploadCallback callback,
                            GObject                   *weak_object,
                            gpointer                   user_data,
                            GError                   **error)
{
  SoupMultipart *mp;
  SoupMessage *message;
  SoupMessageHeaders *part_headers;
  SoupBuffer *sb;
  gchar *content_type;
  gchar *atom_xml;
  GMappedFile *map;
  YoutubeProxyUploadClosure *closure;

  map = g_mapped_file_new (filename, FALSE, error);
  if (*error != NULL) {
    g_warning ("Error opening file %s: %s", filename, (*error)->message);
    return FALSE;
  }

  mp = soup_multipart_new ("multipart/related");

  atom_xml = _construct_upload_atom_xml (fields, incomplete);

  sb = soup_buffer_new_with_owner (atom_xml,
                                   strlen(atom_xml),
                                   atom_xml,
                                   (GDestroyNotify) g_free);

  part_headers = soup_message_headers_new (SOUP_MESSAGE_HEADERS_MULTIPART);

  soup_message_headers_append (part_headers, "Content-Type",
                               "application/atom+xml; charset=UTF-8");


  soup_multipart_append_part (mp, part_headers, sb);

  soup_buffer_free (sb);

  content_type = g_content_type_guess (
      filename,
      (const guchar*) g_mapped_file_get_contents (map),
      g_mapped_file_get_length (map),
      NULL);

  sb = soup_buffer_new_with_owner (g_mapped_file_get_contents (map),
                                   g_mapped_file_get_length (map),
                                   map,
                                   (GDestroyNotify) g_mapped_file_unref);

  soup_message_headers_replace (part_headers, "Content-Type", content_type);

  soup_multipart_append_part (mp, part_headers, sb);

  soup_buffer_free (sb);

  soup_message_headers_free (part_headers);

  message = soup_form_request_new_from_multipart (UPLOAD_URL, mp);

  soup_multipart_free (mp);

  _set_upload_headers (self, message->request_headers, filename);

  closure = _upload_async_closure_new (self, callback, message, weak_object,
                                       user_data);

  g_signal_connect (message,
                    "wrote-body-data",
                    (GCallback) _message_wrote_data_cb,
                    closure);


  _rest_proxy_queue_message (REST_PROXY (self), message, _upload_completed_cb,
                             closure);

  return TRUE;
}
Пример #25
0
static void
gss_server_resource_callback (SoupServer * soupserver, SoupMessage * msg,
    const char *path, GHashTable * query, SoupClientContext * client,
    gpointer user_data)
{
  GssServer *server = (GssServer *) user_data;
  GssTransaction *t;
  GssSession *session;

  t = gss_transaction_new (server, soupserver, msg, path, query, client);

  t->resource = gss_server_lookup_resource (server, path);

  if (!t->resource) {
    gss_transaction_error_not_found (t, "resource not found");
    return;
  }

  if (t->resource->flags & GSS_RESOURCE_UI) {
    if (!server->enable_public_interface && soupserver == server->server) {
      gss_transaction_error_not_found (t, "public interface disabled");
      return;
    }
  }

  if (t->resource->flags & GSS_RESOURCE_HTTPS_ONLY) {
    if (soupserver != server->ssl_server) {
      gss_transaction_error_not_found (t, "resource https only");
      return;
    }
  }

  session = gss_session_get_session (query);
  if (session && soupserver != server->ssl_server) {
    gss_session_invalidate (session);
    session = NULL;
  }

  if (t->resource->flags & GSS_RESOURCE_USER) {
    if (session == NULL) {
      gss_transaction_error_not_found (t, "resource requires login");
      return;
    }
  }
  t->session = session;

  if (t->resource->flags & GSS_RESOURCE_ADMIN) {
    if (session == NULL || !session->is_admin ||
        !gss_addr_range_list_check_address (server->admin_arl,
            soup_client_context_get_address (client))) {
      gss_html_error_401 (server, msg);
      return;
    }
  }

  if (t->resource->content_type) {
    soup_message_headers_replace (msg->response_headers, "Content-Type",
        t->resource->content_type);
  }

  if (t->resource->etag) {
    const char *inm;

    soup_message_headers_append (msg->response_headers, "Cache-Control",
        "max-age=86400");
    inm = soup_message_headers_get_one (msg->request_headers, "If-None-Match");
    if (inm && !strcmp (inm, t->resource->etag)) {
      soup_message_set_status (msg, SOUP_STATUS_NOT_MODIFIED);
      return;
    }
  } else {
    soup_message_headers_append (msg->response_headers, "Cache-Control",
        "must-revalidate");
  }


  if (t->resource->flags & GSS_RESOURCE_HTTP_ONLY) {
    if (soupserver != server->server) {
      gss_resource_onetime_redirect (t);
      return;
    }
  }

  soup_message_set_status (msg, SOUP_STATUS_OK);

  if ((msg->method == SOUP_METHOD_GET || msg->method == SOUP_METHOD_HEAD)
      && t->resource->get_callback) {
    t->resource->get_callback (t);
  } else if (msg->method == SOUP_METHOD_PUT && t->resource->put_callback) {
    t->resource->put_callback (t);
  } else if (msg->method == SOUP_METHOD_POST && t->resource->post_callback) {
    t->resource->post_callback (t);
  } else if (msg->method == SOUP_METHOD_SOURCE && t->resource->put_callback) {
    t->resource->put_callback (t);
  } else if (msg->method == SOUP_METHOD_OPTIONS) {
    soup_message_headers_replace (msg->response_headers,
        "Access-Control-Allow-Origin", "*");
    soup_message_headers_replace (msg->response_headers,
        "Access-Control-Allow-Headers", "origin,range,content-type");
    soup_message_headers_replace (msg->response_headers,
        "Access-Control-Expose-Headers", "Server,range");
    soup_message_headers_replace (msg->response_headers,
        "Access-Control-Allow-Methods", "GET, HEAD, OPTIONS");
  } else {
    gss_html_error_405 (server, msg);
  }

  if (t->s) {
    int len;
    gchar *content;

    len = t->s->len;
    content = g_string_free (t->s, FALSE);
    soup_message_body_append (msg->response_body, SOUP_MEMORY_TAKE,
        content, len);
  }
}
Пример #26
0
/* TODO: use .part extentions and continue even when using GRITS_ONCE */
gchar *grits_http_fetch(GritsHttp *http, const gchar *uri, const char *local,
		GritsCacheType mode, GritsChunkCallback callback, gpointer user_data)
{
	g_debug("GritsHttp: fetch - %s mode=%d", local, mode);
	gchar *path = _get_cache_path(http, local);

	/* Unlink the file if we're refreshing it */
	if (mode == GRITS_REFRESH)
		g_remove(path);

	/* Do the cache if necessasairy */
	if (!(mode == GRITS_ONCE && g_file_test(path, G_FILE_TEST_EXISTS)) &&
			mode != GRITS_LOCAL) {
		g_debug("GritsHttp: fetch - Caching file %s", local);

		/* Open the file for writting */
		gchar *part = path;
		if (!g_file_test(path, G_FILE_TEST_EXISTS))
			part = g_strdup_printf("%s.part", path);
		FILE *fp = fopen_p(part, "ab");
		if (!fp) {
			g_warning("GritsHttp: fetch - error opening %s", path);
			return NULL;
		}
		fseek(fp, 0, SEEK_END); // "a" is broken on Windows, twice

		/* Make temp data */
		struct _CacheInfo info = {
			.fp        = fp,
			.path      = path,
			.callback  = callback,
			.user_data = user_data,
		};

		/* Download the file */
		SoupMessage *message = soup_message_new("GET", uri);
		if (message == NULL)
			g_error("message is null, cannot parse uri");
		g_signal_connect(message, "got-chunk", G_CALLBACK(_chunk_cb), &info);
		//if (ftell(fp) > 0)
			soup_message_headers_set_range(message->request_headers, ftell(fp), -1);
		if (mode == GRITS_REFRESH)
			soup_message_headers_replace(message->request_headers,
					"Cache-Control", "max-age=0");
		soup_session_send_message(http->soup, message);

		/* Close file */
		fclose(fp);
		if (path != part) {
			if (SOUP_STATUS_IS_SUCCESSFUL(message->status_code))
				g_rename(part, path);
			g_free(part);
		}

		/* Finished */
		guint status = message->status_code;
		g_object_unref(message);
		if (status == SOUP_STATUS_CANCELLED) {
			return NULL;
		} else if (status == SOUP_STATUS_REQUESTED_RANGE_NOT_SATISFIABLE) {
			/* Range unsatisfiable, file already complete */
		} else if (!SOUP_STATUS_IS_SUCCESSFUL(status)) {
			g_warning("GritsHttp: done_cb - error copying file, status=%d\n"
					"\tsrc=%s\n"
					"\tdst=%s",
					status, uri, path);
			return NULL;
		}
	}
Пример #27
0
static void
server_callback (SoupServer *server, SoupMessage *msg,
		 const char *path, GHashTable *query,
		 SoupClientContext *context, gpointer data)
{
	const char *accept_encoding, *options;
	GSList *codings;
	char *file = NULL, *contents;
	gsize length;

	options = soup_message_headers_get_one (msg->request_headers,
						"X-Test-Options");
	if (!options)
		options = "";

	accept_encoding = soup_message_headers_get_list (msg->request_headers,
							 "Accept-Encoding");
	if (accept_encoding && !soup_header_contains (options, "force-encode"))
		codings = soup_header_parse_quality_list (accept_encoding, NULL);
	else
		codings = NULL;

	if (codings) {
		gboolean claim_deflate, claim_gzip;
		const char *file_path = NULL, *encoding = NULL;

		claim_deflate = g_slist_find_custom (codings, "deflate", (GCompareFunc)g_ascii_strcasecmp) != NULL;
		claim_gzip = g_slist_find_custom (codings, "gzip", (GCompareFunc)g_ascii_strcasecmp) != NULL;

		if (claim_gzip && (!claim_deflate ||
				   (!soup_header_contains (options, "prefer-deflate-zlib") &&
				    !soup_header_contains (options, "prefer-deflate-raw")))) {
			file_path = SRCDIR "/resources%s.gz";
			encoding = "gzip";
		} else if (claim_deflate) {
			if (soup_header_contains (options, "prefer-deflate-raw")) {
				file_path = SRCDIR "/resources%s.raw";
				encoding = "deflate";
			} else {
				file_path = SRCDIR "/resources%s.zlib";
				encoding = "deflate";
			}
		}
		if (file_path && encoding) {
			file = g_strdup_printf (file_path, path);
			if (g_file_test (file, G_FILE_TEST_EXISTS)) {
				soup_message_headers_append (msg->response_headers,
							     "Content-Encoding",
							     encoding);
			} else {
				g_free (file);
				file = NULL;
			}
		}
	}

	soup_header_free_list (codings);

	if (!file)
		file = g_strdup_printf (SRCDIR "/resources%s", path);
	if (!g_file_get_contents (file, &contents, &length, NULL)) {
		/* If path.gz exists but can't be read, we'll send back
		 * the error with "Content-Encoding: gzip" but there's
		 * no body, so, eh.
		 */
		g_free (file);
		soup_message_set_status (msg, SOUP_STATUS_NOT_FOUND);
		return;
	}
	g_free (file);

	if (soup_header_contains (options, "force-encode")) {
		const gchar *encoding = "gzip";

		if (soup_header_contains (options, "prefer-deflate-zlib") ||
		    soup_header_contains (options, "prefer-deflate-raw"))
			encoding = "deflate";

		soup_message_headers_replace (msg->response_headers,
					      "Content-Encoding",
					      encoding);
	}

	/* Content-Type matches the "real" format, not the sent format */
	if (g_str_has_suffix (path, ".gz")) {
		soup_message_headers_append (msg->response_headers,
					     "Content-Type",
					     "application/gzip");
	} else {
		soup_message_headers_append (msg->response_headers,
					     "Content-Type",
					     "text/plain");
	}

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

	if (!soup_header_contains (options, "empty")) {
		soup_message_body_append (msg->response_body,
					  SOUP_MEMORY_TAKE, contents, length);
	} else
		g_free (contents);

	if (soup_header_contains (options, "trailing-junk")) {
		soup_message_body_append (msg->response_body, SOUP_MEMORY_COPY,
					  options, strlen (options));
	}
	soup_message_body_complete (msg->response_body);
}
static void
gss_adaptive_resource_get_content (GssTransaction * t, GssAdaptive * adaptive)
{
  const char *stream;
  const char *start_time_str;
  const char *bitrate_str;
  guint64 start_time;
  guint64 bitrate;
  gboolean is_init;
  GssAdaptiveLevel *level;
  GssIsomFragment *fragment;
  gboolean ret;

  //GST_ERROR ("content request");

  if (t->query == NULL) {
    gss_transaction_error_not_found (t, "no smooth streaming query");
    return;
  }

  stream = g_hash_table_lookup (t->query, "stream");
  if (stream == NULL) {
    gss_transaction_error_not_found (t, "missing stream parameterr");
    return;
  }
  start_time_str = g_hash_table_lookup (t->query, "start_time");
  if (start_time_str == NULL) {
    gss_transaction_error_not_found (t, "missing start_time parameter");
    return;
  }
  bitrate_str = g_hash_table_lookup (t->query, "bitrate");
  if (bitrate_str == NULL) {
    gss_transaction_error_not_found (t, "missing bitrate parameter");
    return;
  }

  ret = parse_guint64 (bitrate_str, &bitrate);
  if (!ret) {
    gss_transaction_error_not_found (t, "bitrate is not a number");
    return;
  }

  if (strcmp (start_time_str, "init") == 0) {
    is_init = TRUE;
    start_time = 0;
  } else {
    is_init = FALSE;
    ret = parse_guint64 (start_time_str, &start_time);
    if (!ret) {
      gss_transaction_error_not_found (t,
          "start_time is not a number or \"init\"");
      return;
    }
  }

  if (strcmp (stream, "audio") != 0 && strcmp (stream, "video") != 0) {
    gss_transaction_error_not_found (t, "stream is not \"audio\" or \"video\"");
    return;
  }

  level = gss_adaptive_get_level (adaptive, (stream[0] == 'v'), bitrate);
  if (level == NULL) {
    gss_transaction_error_not_found (t,
        "level not found for stream and bitrate");
    return;
  }

  soup_message_headers_replace (t->msg->response_headers, "Content-Type",
      (stream[0] == 'v') ? "video/mp4" : "audio/mp4");

  if (is_init) {
    soup_message_body_append (t->msg->response_body, SOUP_MEMORY_COPY,
        level->track->ccff_header_data, level->track->ccff_header_size);
  } else {
    GssAdaptiveQuery *query;

    fragment = gss_isom_track_get_fragment_by_timestamp (level->track,
        start_time);
    if (fragment == NULL) {
      gss_transaction_error_not_found (t, "fragment not found for start_time");
      return;
    }
    //GST_ERROR ("frag %s %" G_GUINT64_FORMAT " %" G_GUINT64_FORMAT,
    //    level->filename, fragment->offset, fragment->size);

    soup_server_pause_message (t->soupserver, t->msg);

    query = g_malloc0 (sizeof (GssAdaptiveQuery));
    query->adaptive = adaptive;
    query->level = level;
    query->fragment = fragment;

    gss_transaction_process_async (t, gss_adaptive_async_assemble_chunk,
        gss_adaptive_async_assemble_chunk_finish, query);
  }
}
static void
gss_adaptive_resource_get_dash_range_fragment (GssTransaction * t,
    GssAdaptive * adaptive, const char *path)
{
  gboolean have_range;
  SoupRange *ranges;
  int n_ranges;
  int index;
  GssAdaptiveLevel *level;
  gsize start, end;

  /* skip over content/ */
  path += 8;

  if (path[0] != 'a' && path[0] != 'v') {
    GST_ERROR ("bad path: %s", path);
    return;
  }
  index = strtoul (path + 1, NULL, 10);

  level = NULL;
  if (path[0] == 'a') {
    if (index < adaptive->n_audio_levels) {
      level = &adaptive->audio_levels[index];
    }
  } else {
    if (index < adaptive->n_video_levels) {
      level = &adaptive->video_levels[index];
    }
  }

  if (level == NULL) {
    GST_ERROR ("bad level: %c%d from path %s", path[0], index, path);
    return;
  }

  if (t->msg->method == SOUP_METHOD_HEAD) {
    GST_DEBUG ("%s: HEAD", path);
    soup_message_headers_set_content_length (t->msg->response_headers,
        level->track->dash_size);
    return;
  }

  have_range = soup_message_headers_get_ranges (t->msg->request_headers,
      level->track->dash_size, &ranges, &n_ranges);

  if (have_range) {
    if (n_ranges != 1) {
      GST_ERROR ("too many ranges");
    }
    start = ranges[0].start;
    end = ranges[0].end + 1;
  } else {
    start = 0;
    end = level->track->dash_size;
  }
  GST_DEBUG ("%s: range: %ld-%ld", path, start, end);
  t->start = start;
  t->end = end;

  if (have_range) {
    soup_message_headers_set_content_range (t->msg->response_headers,
        ranges[0].start, ranges[0].end, level->track->dash_size);

    soup_message_set_status (t->msg, SOUP_STATUS_PARTIAL_CONTENT);

    soup_message_headers_free_ranges (t->msg->response_headers, ranges);
  } else {
    soup_message_set_status (t->msg, SOUP_STATUS_OK);
  }

  soup_message_headers_replace (t->msg->response_headers, "Content-Type",
      (path[0] == 'v') ? "video/mp4" : "audio/mp4");

  {
    GssAdaptiveQuery *query;

    soup_server_pause_message (t->soupserver, t->msg);

    query = g_malloc0 (sizeof (GssAdaptiveQuery));
    query->adaptive = adaptive;
    query->level = level;

    gss_transaction_process_async (t, gss_adaptive_dash_range_async,
        gss_adaptive_dash_range_async_finish, query);
  }
}
static void
gss_adaptive_resource_get_dash_range_mpd (GssTransaction * t,
    GssAdaptive * adaptive)
{
  GString *s = g_string_new ("");
  int i;
  ManifestQuery mq;

  parse_manifest_query (&mq, t);
  t->s = s;

  soup_message_headers_replace (t->msg->response_headers, "Content-Type",
      "application/octet-stream");

  GSS_A ("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
  GSS_A ("<MPD xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n"
      "  xmlns=\"urn:mpeg:dash:schema:mpd:2011\"\n");
  if (adaptive->drm_type == GSS_DRM_PLAYREADY) {
    GSS_A ("  xmlns:mspr=\"urn:microsoft:playready\"\n");
  }
  GSS_P ("  xsi:schemaLocation=\"urn:mpeg:dash:schema:mpd:2011 DASH-MPD.xsd\"\n"
      "  type=\"static\"\n"
      "  mediaPresentationDuration=\"PT%dS\"\n"
      "  minBufferTime=\"PT10S\"\n"
      "  profiles=\"urn:mpeg:dash:profile:isoff-on-demand:2011\">\n",
      (int) (adaptive->duration / GSS_ISM_SECOND));
  GSS_P ("  <Period>\n");

  GSS_A ("    <AdaptationSet mimeType=\"audio/mp4\" "
      "lang=\"en\" "
      "segmentAlignment=\"true\" "
      "subsegmentAlignment=\"true\" " "subsegmentStartsWithSAP=\"1\">\n");
  append_content_protection (t, adaptive, mq.auth_token);
  for (i = 0; i < adaptive->n_audio_levels; i++) {
    GssAdaptiveLevel *level = &adaptive->audio_levels[i];
    GssIsomTrack *track = level->track;

    GSS_P ("      <Representation id=\"a%d\" codecs=\"%s\" bandwidth=\"%d\">\n",
        i, level->codec, level->bitrate);
    GSS_P ("        <BaseURL>content/a%d</BaseURL>\n", i);
    GSS_P ("        <SegmentBase indexRange=\"%" G_GSIZE_FORMAT "-%"
        G_GSIZE_FORMAT "\">" "<Initialization range=\"%" G_GSIZE_FORMAT "-%"
        G_GSIZE_FORMAT "\" /></SegmentBase>\n", track->dash_header_size,
        track->dash_header_and_sidx_size - 1, (gsize) 0,
        track->dash_header_size - 1);
    GSS_A ("      </Representation>\n");
    break;
  }
  GSS_A ("    </AdaptationSet>\n");

  GSS_A ("    <AdaptationSet mimeType=\"video/mp4\" "
      "segmentAlignment=\"true\" "
      "subsegmentAlignment=\"true\" " "subsegmentStartsWithSAP=\"1\">\n");
  append_content_protection (t, adaptive, mq.auth_token);
  for (i = 0; i < adaptive->n_video_levels; i++) {
    GssAdaptiveLevel *level = &adaptive->video_levels[i];
    GssIsomTrack *track = level->track;

    if (manifest_query_check_video (&mq, level)) {
      GSS_P ("      <Representation id=\"v%d\" bandwidth=\"%d\" "
          "codecs=\"%s\" width=\"%d\" height=\"%d\">\n",
          i, level->bitrate, level->codec,
          level->video_width, level->video_height);
      GSS_P ("        <BaseURL>content/v%d</BaseURL>\n", i);
      GSS_P ("        <SegmentBase indexRange=\"%" G_GSIZE_FORMAT "-%"
          G_GSIZE_FORMAT "\">" "<Initialization range=\"%" G_GSIZE_FORMAT "-%"
          G_GSIZE_FORMAT "\" /></SegmentBase>\n", track->dash_header_size,
          track->dash_header_and_sidx_size - 1, (gsize) 0,
          track->dash_header_size - 1);
      GSS_A ("      </Representation>\n");
    }
  }
  GSS_A ("    </AdaptationSet>\n");

  GSS_A ("  </Period>\n");
  GSS_A ("</MPD>\n");
  GSS_A ("\n");

}