Пример #1
0
static guint
parse_response_headers (SoupMessage *req,
			char *headers, guint headers_len,
			SoupEncoding *encoding,
			gpointer user_data)
{
	SoupMessagePrivate *priv = SOUP_MESSAGE_GET_PRIVATE (req);
	SoupHTTPVersion version;

	g_free(req->reason_phrase);
	req->reason_phrase = NULL;
	if (!soup_headers_parse_response (headers, headers_len,
					  req->response_headers,
					  &version,
					  &req->status_code,
					  &req->reason_phrase))
		return SOUP_STATUS_MALFORMED;

	g_object_notify (G_OBJECT (req), SOUP_MESSAGE_STATUS_CODE);
	g_object_notify (G_OBJECT (req), SOUP_MESSAGE_REASON_PHRASE);

	if (version < priv->http_version) {
		priv->http_version = version;
		g_object_notify (G_OBJECT (req), SOUP_MESSAGE_HTTP_VERSION);
	}

	if ((req->method == SOUP_METHOD_HEAD ||
	     req->status_code  == SOUP_STATUS_NO_CONTENT ||
	     req->status_code  == SOUP_STATUS_NOT_MODIFIED ||
	     SOUP_STATUS_IS_INFORMATIONAL (req->status_code)) ||
	    (req->method == SOUP_METHOD_CONNECT &&
	     SOUP_STATUS_IS_SUCCESSFUL (req->status_code)))
		*encoding = SOUP_ENCODING_NONE;
	else
		*encoding = soup_message_headers_get_encoding (req->response_headers);

	if (*encoding == SOUP_ENCODING_UNRECOGNIZED)
		return SOUP_STATUS_MALFORMED;

	return SOUP_STATUS_OK;
}
Пример #2
0
static gboolean
send_request (const gchar  *method,
              const gchar  *path,
              const gchar  *content,
              const gchar  *macaroon,
              gchar       **discharges,
              guint        *status_code,
              gchar       **reason_phrase,
              gchar       **response_type,
              gchar       **response,
              gsize        *response_length,
              GCancellable *cancellable,
              GError      **error)
{
    g_autoptr (GSocket) socket = NULL;
    g_autoptr (GString) request = NULL;
    gssize n_written;
    g_autoptr (GByteArray) buffer = NULL;
    gsize data_length = 0, header_length;
    gchar *body = NULL;
    g_autoptr (SoupMessageHeaders) headers = NULL;
    gsize chunk_length = 0, n_required;
    guint8 *chunk_start = NULL;
    guint code;

    // NOTE: Would love to use libsoup but it doesn't support unix sockets
    // https://bugzilla.gnome.org/show_bug.cgi?id=727563

    socket = open_snapd_socket (cancellable, error);
    if (socket == NULL)
        return FALSE;

    request = g_string_new ("");
    g_string_append_printf (request, "%s %s HTTP/1.1\r\n", method, path);
    g_string_append (request, "Host:\r\n");
    if (macaroon != NULL) {
        gint i;

        g_string_append_printf (request, "Authorization: Macaroon root=\"%s\"", macaroon);
        for (i = 0; discharges[i] != NULL; i++)
            g_string_append_printf (request, ",discharge=\"%s\"", discharges[i]);
        g_string_append (request, "\r\n");
    }
    if (content)
        g_string_append_printf (request, "Content-Length: %zu\r\n", strlen (content));
    g_string_append (request, "\r\n");
    if (content)
        g_string_append (request, content);

    g_debug ("begin snapd request: %s", request->str);

    /* send HTTP request */
    n_written = g_socket_send (socket, request->str, request->len, cancellable, error);
    if (n_written < 0)
        return FALSE;

    /* read HTTP headers */
    buffer = g_byte_array_new ();
    while (body == NULL) {
        if (!read_from_snapd (socket,
                              buffer,
                              &data_length,
                              1024,
                              cancellable,
                              error))
            return FALSE;
        body = strstr (buffer->data, "\r\n\r\n");
    }
    if (body == NULL) {
        g_set_error_literal (error,
                             GS_PLUGIN_ERROR,
                             GS_PLUGIN_ERROR_INVALID_FORMAT,
                             "Unable to find header separator in snapd response");
        return FALSE;
    }

    /* body starts after header divider */
    body += 4;
    header_length = (gsize) ((guint8 *) body - buffer->data);

    /* parse headers */
    headers = soup_message_headers_new (SOUP_MESSAGE_HEADERS_RESPONSE);
    if (!soup_headers_parse_response (buffer->data, (gint) header_length, headers,
                                      NULL, &code, reason_phrase)) {
        g_set_error_literal (error,
                             GS_PLUGIN_ERROR,
                             GS_PLUGIN_ERROR_INVALID_FORMAT,
                             "snapd response HTTP headers not parseable");
        return FALSE;
    }

    if (status_code != NULL)
        *status_code = code;

    /* read content */
    switch (soup_message_headers_get_encoding (headers)) {
    case SOUP_ENCODING_EOF:
        while (TRUE) {
            gsize n_read = data_length;
            if (!read_from_snapd (socket,
                                  buffer,
                                  &data_length,
                                  1024,
                                  cancellable,
                                  error))
                return FALSE;
            if (n_read == data_length)
                break;
            chunk_length += data_length - n_read;
        }
        break;
    case SOUP_ENCODING_CHUNKED:
        // FIXME: support multiple chunks
        while (TRUE) {
            chunk_start = strstr (body, "\r\n");
            if (chunk_start)
                break;
            if (!read_from_snapd (socket,
                                  buffer,
                                  &data_length,
                                  1024,
                                  cancellable,
                                  error))
                return FALSE;
        }
        if (!chunk_start) {
            g_set_error_literal (error,
                                 GS_PLUGIN_ERROR,
                                 GS_PLUGIN_ERROR_INVALID_FORMAT,
                                 "Unable to find chunk header in "
                                 "snapd response");
            return FALSE;
        }
        chunk_length = strtoul (body, NULL, 16);
        chunk_start += 2;

        /* check if enough space to read chunk */
        n_required = (chunk_start - buffer->data) + chunk_length;
        while (data_length < n_required)
            if (!read_from_snapd (socket,
                                  buffer,
                                  &data_length,
                                  n_required - data_length,
                                  cancellable,
                                  error))
                return FALSE;
        break;
    case SOUP_ENCODING_CONTENT_LENGTH:
        chunk_length = soup_message_headers_get_content_length (headers);
        chunk_start = body;
        n_required = header_length + chunk_length;
        while (data_length < n_required) {
            if (!read_from_snapd (socket,
                                  buffer,
                                  &data_length,
                                  n_required - data_length,
                                  cancellable,
                                  error))
                return FALSE;
        }
        break;
    default:
        g_set_error_literal (error,
                             GS_PLUGIN_ERROR,
                             GS_PLUGIN_ERROR_INVALID_FORMAT,
                             "Unable to determine content "
                             "length of snapd response");
        return FALSE;
    }

    if (response_type)
        *response_type = g_strdup (soup_message_headers_get_content_type (headers, NULL));
    if (response != NULL && chunk_start != NULL) {
        *response = g_malloc (chunk_length + 2);
        memcpy (*response, chunk_start, chunk_length + 1);
        (*response)[chunk_length + 1] = '\0';
        g_debug ("snapd status %u: %s", code, *response);
    }
    if (response_length)
        *response_length = chunk_length;

    return TRUE;
}