Example #1
0
/**
 * @brief  Create an AWS V3 Signature canonical headers string.
 *
 * This function constructs a canonical string containing all of the headers
 * in the given request.
 *
 * @note   \p request will typically not include a `Host` header at this stage,
 *         however Qt will add an appropriate `Host` header when the request is
 *         performed.  So, if \p request does not include a `Host` header yet,
 *         this function will include a derived `Host` header in the canonical
 *         headers to allow for it.
 *
 * @note   This function is only applicable to the `AWS3` format, not `AWS3-HTTPS`.
 *
 * @param[in]  request        The network request to fetch the canonical headers from.
 * @param[out] signedHeaders  A semi-colon separated list of the names of all headers
 *                            included in the result.
 *
 * @return  An AWS V3 Signature canonical headers string.
 *
 * @see    http://docs.aws.amazon.com/general/latest/gr/sigV3-create-canonical-request.html
 * @see    canonicalHeader
 */
QByteArray AwsSignatureV3Private::canonicalHeaders(const QNetworkRequest &request, QByteArray * const signedHeaders) const
{
    Q_CHECK_PTR(signedHeaders);
    signedHeaders->clear();

    /* Note, Amazon says we should combine duplicate headers with comma separators...
     * conveniently for us, QNetworkRequest requires that to have been done already.
     * See note in QNetworkRequest::setRawHeader.
     */

    // Convert the raw headers list to a map to sort on (lowercased) header names only.
    QMap<QByteArray,QByteArray> headers;
    foreach (const QByteArray &rawHeader, request.rawHeaderList()) {
        headers.insert(rawHeader.toLower(), request.rawHeader(rawHeader));
    }
    // The "host" header is not included in QNetworkRequest::rawHeaderList, but will be sent by Qt.
    headers.insert("host", request.url().host().toUtf8());

    // Convert the headers map to a canonical string, keeping track of which headers we've included too.
    QByteArray canonicalHeaders;
    for (QMap<QByteArray,QByteArray>::const_iterator iter = headers.constBegin(); iter != headers.constEnd(); ++iter) {
        // Only include "host" and "x-amz-*" headers. Note, Amazon documentation states that latter as
        // "x-amz-" (ie with the trailing '-'), yet the official Amazon Java SDK tests for a "x-amz"
        // prefix. Thus the Java SDK would include headers with keys like "x-amzfoo", but here we do not
        // since that would disagree with the official documentation.
        if ((iter.key() == "host") || (iter.key().startsWith("x-amz-"))) {
            canonicalHeaders += canonicalHeader(iter.key(), iter.value()) + '\n';
            if (!signedHeaders->isEmpty()) *signedHeaders += ';';
            *signedHeaders += iter.key();
        }
    }
    return canonicalHeaders;
}
/**
 * @brief  Create an AWS V4 Signature canonical headers string.
 *
 * This function constructs a canonical string containing all of the headers
 * in the given request.
 *
 * @note   \p request will typically not include a `Host` header at this stage,
 *         however Qt will add an appropriate `Host` header when the request is
 *         performed.  So, if \p request does not include a `Host` header yet,
 *         this function will include a derived `Host` header in the canonical
 *         headers to allow for it.
 *
 * @param[in]  request        The network request to fetch the canonical headers from.
 * @param[out] signedHeaders  A semi-colon separated list of the names of all headers
 *                            included in the result.
 *
 * @return  An AWS V4 Signature canonical headers string.
 *
 * @see    http://docs.aws.amazon.com/general/latest/gr/sigv4-create-canonical-request.html
 * @see    canonicalHeader
 */
QByteArray AwsSignatureV4Private::canonicalHeaders(const QNetworkRequest &request, QByteArray * const signedHeaders) const
{
    Q_CHECK_PTR(signedHeaders);
    signedHeaders->clear();

    /* Note, Amazon says we should combine duplicate headers with comma separators...
     * conveniently for us, QNetworkRequest requires that to have been done already.
     * See note in QNetworkRequest::setRawHeader.
     */

    // Convert the raw headers list to a map to sort on (lowercased) header names only.
    QMap<QByteArray,QByteArray> headers;
    foreach (const QByteArray &rawHeader, request.rawHeaderList()) {
        headers.insert(rawHeader.toLower(), request.rawHeader(rawHeader));
    }
    // The "host" header is not included in QNetworkRequest::rawHeaderList, but will be sent by Qt.
    headers.insert("host", request.url().host().toUtf8());

    // Convert the headers map to a canonical string, keeping track of which headers we've included too.
    QByteArray canonicalHeaders;
    for (QMap<QByteArray,QByteArray>::const_iterator iter = headers.constBegin(); iter != headers.constEnd(); ++iter) {
        canonicalHeaders += canonicalHeader(iter.key(), iter.value()) + '\n';
        if (!signedHeaders->isEmpty()) *signedHeaders += ';';
        *signedHeaders += iter.key();
    }
    return canonicalHeaders;
}