MetadataValidationPolicy.java

// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

package com.azure.storage.common.policy;

import com.azure.core.http.HttpPipelineCallContext;
import com.azure.core.http.HttpPipelineNextPolicy;
import com.azure.core.http.HttpResponse;
import com.azure.core.http.policy.HttpPipelinePolicy;
import com.azure.core.util.FluxUtil;
import com.azure.core.util.logging.ClientLogger;
import com.azure.storage.common.implementation.Constants;
import reactor.core.publisher.Mono;

import java.util.Locale;

/**
 * This is a request policy in an {@link com.azure.core.http.HttpPipeline} to validate that metadata does not contain
 * leading or trailing whitespace characters.
 * We do this since the service trims whitespace for the string to sign, but the client does not, resulting in an auth
 * failure.
 */
public class MetadataValidationPolicy implements HttpPipelinePolicy {

    private final ClientLogger logger = new ClientLogger(MetadataValidationPolicy.class);

    @Override
    public Mono<HttpResponse> process(HttpPipelineCallContext context, HttpPipelineNextPolicy next) {
        try {
            context.getHttpRequest().getHeaders().stream()
                .filter(header -> header.getName().toLowerCase(Locale.ROOT)
                    .startsWith(Constants.HeaderConstants.X_MS_META))
                .forEach(header -> {
                    boolean foundWhitespace = Character.isWhitespace(header.getName()
                        .charAt(Constants.HeaderConstants.X_MS_META.length()))
                        || Character.isWhitespace(header.getName().charAt(header.getName().length() - 1))
                        || Character.isWhitespace(header.getValue().charAt(0))
                        || Character.isWhitespace(header.getValue().charAt(header.getValue().length() - 1));
                    if (foundWhitespace) {
                        throw logger.logExceptionAsError(new IllegalArgumentException("Metadata keys and values "
                            + "can not contain leading or trailing whitespace. Please remove or encode them."));
                    }
                });
        } catch (IllegalArgumentException ex) {
            return FluxUtil.monoError(logger, ex);
        }

        return next.process();
    }
}