pplx::task<std::vector<cloud_queue_message>> cloud_queue::peek_messages_async(size_t message_count, const queue_request_options& options, operation_context context) const
    {
        queue_request_options modified_options = get_modified_options(options);
        storage_uri uri = protocol::generate_queue_message_uri(service_client(), *this);

        std::shared_ptr<core::storage_command<std::vector<cloud_queue_message>>> command = std::make_shared<core::storage_command<std::vector<cloud_queue_message>>>(uri);
        command->set_build_request(std::bind(protocol::get_messages, message_count, std::chrono::seconds(0LL), /* is_peek */ true, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));
        command->set_authentication_handler(service_client().authentication_handler());
        command->set_preprocess_response(std::bind(protocol::preprocess_response<std::vector<cloud_queue_message>>, std::vector<cloud_queue_message>(), std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));
        command->set_postprocess_response([] (const web::http::http_response& response, const request_result&, const core::ostream_descriptor&, operation_context context) -> pplx::task<std::vector<cloud_queue_message>>
        {
            UNREFERENCED_PARAMETER(context);
            protocol::message_reader reader(response.body());
            std::vector<protocol::cloud_message_list_item> queue_items = reader.move_items();

            std::vector<cloud_queue_message> results;
            results.reserve(queue_items.size());

            for (std::vector<protocol::cloud_message_list_item>::iterator it = queue_items.begin(); it != queue_items.end(); ++it)
            {
                cloud_queue_message message(it->move_content(), it->move_id(), it->move_pop_receipt(), it->insertion_time(), it->expiration_time(), it->next_visible_time(), it->dequeue_count());
                results.push_back(message);
            }

            return pplx::task_from_result(results);
        });
        return core::executor<std::vector<cloud_queue_message>>::execute_async(command, modified_options, context);
    }
    pplx::task<void> cloud_queue::add_message_async(cloud_queue_message& message, std::chrono::seconds time_to_live, std::chrono::seconds initial_visibility_timeout, queue_request_options& options, operation_context context)
    {
        if (time_to_live.count() <= 0LL)
        {
            throw std::invalid_argument(protocol::error_non_positive_time_to_live);
        }

        if (time_to_live.count() > 604800LL)
        {
            throw std::invalid_argument(protocol::error_large_time_to_live);
        }

        if (initial_visibility_timeout.count() < 0LL)
        {
            throw std::invalid_argument(protocol::error_negative_initial_visibility_timeout);
        }

        if (initial_visibility_timeout.count() > 604800LL)
        {
            throw std::invalid_argument(protocol::error_large_initial_visibility_timeout);
        }

        queue_request_options modified_options = get_modified_options(options);
        storage_uri uri = protocol::generate_queue_message_uri(service_client(), *this);

        std::shared_ptr<core::storage_command<void>> command = std::make_shared<core::storage_command<void>>(uri);
        command->set_build_request(std::bind(protocol::add_message, message, time_to_live, initial_visibility_timeout, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));
        command->set_authentication_handler(service_client().authentication_handler());
        command->set_preprocess_response(std::bind(protocol::preprocess_response_void, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));
        return core::executor<void>::execute_async(command, modified_options, context);
    }
    pplx::task<cloud_queue_message> cloud_queue::peek_message_async(const queue_request_options& options, operation_context context) const
    {
        queue_request_options modified_options = get_modified_options(options);
        storage_uri uri = protocol::generate_queue_message_uri(service_client(), *this);

        std::shared_ptr<core::storage_command<cloud_queue_message>> command = std::make_shared<core::storage_command<cloud_queue_message>>(uri);
        command->set_build_request(std::bind(protocol::get_messages, 1U, std::chrono::seconds(0LL), /* is_peek */ true, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));
        command->set_authentication_handler(service_client().authentication_handler());
        command->set_preprocess_response(std::bind(protocol::preprocess_response<cloud_queue_message>, cloud_queue_message(), std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));
        command->set_postprocess_response([] (const web::http::http_response& response, const request_result&, const core::ostream_descriptor&, operation_context context) -> pplx::task<cloud_queue_message>
        {
            UNREFERENCED_PARAMETER(context);
            protocol::message_reader reader(response.body());
            std::vector<protocol::cloud_message_list_item> queue_items = reader.move_items();

            if (!queue_items.empty())
            {
                protocol::cloud_message_list_item& item = queue_items.front();
                cloud_queue_message message(item.move_content(), item.move_id(), item.move_pop_receipt(), item.insertion_time(), item.expiration_time(), item.next_visible_time(), item.dequeue_count());
                return pplx::task_from_result(message);
            }

            return pplx::task_from_result(cloud_queue_message());
        });
        return core::executor<cloud_queue_message>::execute_async(command, modified_options, context);
    }
    pplx::task<std::vector<table_result>> cloud_table::execute_batch_async(const table_batch_operation& operation, const table_request_options& options, operation_context context) const
    {
        table_request_options modified_options = get_modified_options(options);
        storage_uri uri = protocol::generate_table_uri(service_client(), *this, operation);

        std::vector<table_operation> operations = operation.operations();
        if (operations.size() == 0U)
        {
            throw std::invalid_argument(protocol::error_empty_batch_operation);
        }

        utility::string_t partition_key = operations.front().entity().partition_key();
        for (std::vector<table_operation>::const_iterator itr = operations.cbegin(); itr != operations.cend(); ++itr)
        {
            if (partition_key.compare(itr->entity().partition_key()) != 0)
            {
                throw std::invalid_argument(protocol::error_batch_operation_partition_key_mismatch);
            }
        }

        bool is_query = false;
        for (std::vector<table_operation>::const_iterator itr = operations.cbegin(); itr != operations.cend(); ++itr)
        {
            if (itr->operation_type() == table_operation_type::retrieve_operation)
            {
                if (is_query)
                {
                    throw std::invalid_argument(protocol::error_batch_operation_retrieve_count);
                }

                is_query = true;
            }
        }

        if (is_query && operations.size() != 1)
        {
            throw std::invalid_argument(protocol::error_batch_operation_retrieve_mix);
        }

        // TODO: Pre-create a stream for the response to pass to response handler in other functions too so the response doesn't need to be copied
        Concurrency::streams::stringstreambuf response_buffer;

        std::shared_ptr<core::storage_command<std::vector<table_result>>> command = std::make_shared<core::storage_command<std::vector<table_result>>>(uri);
        command->set_build_request(std::bind(protocol::execute_batch_operation, response_buffer, *this, operation, options.payload_format(), is_query, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));
        command->set_authentication_handler(service_client().authentication_handler());
        command->set_location_mode(is_query ? core::command_location_mode::primary_or_secondary : core::command_location_mode::primary_only);
        command->set_preprocess_response(std::bind(protocol::preprocess_response<std::vector<table_result>>, std::vector<table_result>(), std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));
        command->set_postprocess_response([response_buffer, operations, is_query] (const web::http::http_response& response, const request_result&, const core::ostream_descriptor&, operation_context context) mutable -> pplx::task<std::vector<table_result>>
        {
            UNREFERENCED_PARAMETER(context);
            return response.content_ready().then([response_buffer, operations, is_query](const web::http::http_response& response) mutable -> pplx::task<std::vector<table_result>>
            {
                std::vector<table_result> batch_result = protocol::table_response_parsers::parse_batch_results(response, response_buffer, is_query, operations.size());
                return pplx::task_from_result(batch_result);
            });
        });
        return core::executor<std::vector<table_result>>::execute_async(command, modified_options, context);
    }
    pplx::task<void> cloud_queue::upload_metadata_async(const queue_request_options& options, operation_context context)
    {
        queue_request_options modified_options = get_modified_options(options);
        storage_uri uri = protocol::generate_queue_uri(service_client(), *this);

        std::shared_ptr<core::storage_command<void>> command = std::make_shared<core::storage_command<void>>(uri);
        command->set_build_request(std::bind(protocol::upload_queue_metadata, metadata(), std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));
        command->set_authentication_handler(service_client().authentication_handler());
        command->set_preprocess_response(std::bind(protocol::preprocess_response_void, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));
        return core::executor<void>::execute_async(command, modified_options, context);
    }
    pplx::task<table_result> cloud_table::execute_async(const table_operation& operation, const table_request_options& options, operation_context context) const
    {
        table_request_options modified_options = get_modified_options(options);
        storage_uri uri = protocol::generate_table_uri(service_client(), *this, operation);

        // Do not throw an exception when the retrieve fails because the entity does not exist
        bool allow_not_found = operation.operation_type() == table_operation_type::retrieve_operation;

        std::shared_ptr<core::storage_command<table_result>> command = std::make_shared<core::storage_command<table_result>>(uri);
        command->set_build_request(std::bind(protocol::execute_operation, operation, options.payload_format(), std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));
        command->set_authentication_handler(service_client().authentication_handler());
        command->set_location_mode(operation.operation_type() == azure::storage::table_operation_type::retrieve_operation ? core::command_location_mode::primary_or_secondary : core::command_location_mode::primary_only);
        command->set_preprocess_response([allow_not_found] (const web::http::http_response& response, const request_result& result, operation_context context) -> table_result
        {
            if (!allow_not_found || response.status_code() != web::http::status_codes::NotFound)
            {
                protocol::preprocess_response_void(response, result, context);
            }
            return table_result();
        });
        command->set_postprocess_response([] (const web::http::http_response& response, const request_result&, const core::ostream_descriptor&, operation_context context) -> pplx::task<table_result>
        {
            UNREFERENCED_PARAMETER(context);
            int status_code = response.status_code();
            utility::string_t etag = protocol::table_response_parsers::parse_etag(response);

            if (status_code == web::http::status_codes::NoContent)
            {
                table_result result;
                result.set_http_status_code(status_code);
                result.set_etag(std::move(etag));
                return pplx::task_from_result(result);
            }
            else
            {
                return response.extract_json().then([status_code, etag] (const web::json::value& obj) -> table_result
                {
                    table_entity entity = protocol::parse_table_entity(obj);
                    if (entity.etag().empty())
                    {
                        entity.set_etag(etag);
                    }

                    table_result result;
                    result.set_http_status_code(status_code);
                    result.set_etag(std::move(etag));
                    result.set_entity(std::move(entity));
                    return result;
                });
            }
        });
        return core::executor<table_result>::execute_async(command, modified_options, context);
    }
    pplx::task<void> cloud_queue::download_attributes_async(const queue_request_options& options, operation_context context)
    {
        queue_request_options modified_options = get_modified_options(options);
        storage_uri uri = protocol::generate_queue_uri(service_client(), *this);

        std::shared_ptr<core::storage_command<void>> command = std::make_shared<core::storage_command<void>>(uri);
        command->set_build_request(std::bind(protocol::download_queue_metadata, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));
        command->set_authentication_handler(service_client().authentication_handler());
        command->set_location_mode(core::command_location_mode::primary_or_secondary);
        command->set_preprocess_response([this](const web::http::http_response& response, const request_result& result, operation_context context)
        {
            protocol::preprocess_response_void(response, result, context);
            m_metadata = protocol::parse_metadata(response);
            m_approximate_message_count = protocol::parse_approximate_messages_count(response);
        });
        return core::executor<void>::execute_async(command, modified_options, context);
    }
    pplx::task<void> cloud_queue::upload_permissions_async(const queue_permissions& permissions, const queue_request_options& options, operation_context context)
    {
        queue_request_options modified_options = get_modified_options(options);
        storage_uri uri = protocol::generate_queue_uri(service_client(), *this);

        protocol::access_policy_writer<queue_shared_access_policy> writer;
        concurrency::streams::istream stream(concurrency::streams::bytestream::open_istream(writer.write(permissions.policies())));

        std::shared_ptr<core::storage_command<void>> command = std::make_shared<core::storage_command<void>>(uri);
        command->set_build_request(std::bind(protocol::set_queue_acl, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));
        command->set_authentication_handler(service_client().authentication_handler());
        command->set_preprocess_response(std::bind(protocol::preprocess_response_void, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));
        return core::istream_descriptor::create(stream).then([command, context, modified_options](core::istream_descriptor request_body) -> pplx::task<void>
        {
            command->set_request_body(request_body);
            return core::executor<void>::execute_async(command, modified_options, context);
        });
    }
    pplx::task<bool> cloud_queue::delete_async_impl(const queue_request_options& options, operation_context context, bool allow_not_found)
    {
        queue_request_options modified_options = get_modified_options(options);
        storage_uri uri = protocol::generate_queue_uri(service_client(), *this);

        std::shared_ptr<core::storage_command<bool>> command = std::make_shared<core::storage_command<bool>>(uri);
        command->set_build_request(std::bind(protocol::delete_queue, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));
        command->set_authentication_handler(service_client().authentication_handler());
        command->set_preprocess_response([allow_not_found] (const web::http::http_response& response, const request_result& result, operation_context context) -> bool
        {
            if (allow_not_found && response.status_code() == web::http::status_codes::NotFound)
            {
                return false;
            }

            protocol::preprocess_response_void(response, result, context);
            return true;
        });
        return core::executor<bool>::execute_async(command, modified_options, context);
    }
    pplx::task<queue_permissions> cloud_queue::download_permissions_async(const queue_request_options& options, operation_context context) const
    {
        queue_request_options modified_options = get_modified_options(options);
        storage_uri uri = protocol::generate_queue_uri(service_client(), *this);

        std::shared_ptr<core::storage_command<queue_permissions>> command = std::make_shared<core::storage_command<queue_permissions>>(uri);
        command->set_build_request(std::bind(protocol::get_queue_acl, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));
        command->set_authentication_handler(service_client().authentication_handler());
        command->set_location_mode(core::command_location_mode::primary_or_secondary);
        command->set_preprocess_response(std::bind(protocol::preprocess_response<queue_permissions>, queue_permissions(), std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));
        command->set_postprocess_response([] (const web::http::http_response& response, const request_result&, const core::ostream_descriptor&, operation_context context) -> pplx::task<queue_permissions>
        {
            UNREFERENCED_PARAMETER(context);
            queue_permissions permissions;
            protocol::access_policy_reader<queue_shared_access_policy> reader(response.body());
            permissions.set_policies(reader.move_policies());
            return pplx::task_from_result<queue_permissions>(permissions);
        });
        return core::executor<queue_permissions>::execute_async(command, modified_options, context);
    }
    pplx::task<bool> cloud_queue::exists_async_impl(const queue_request_options& options, operation_context context, bool allow_secondary) const
    {
        queue_request_options modified_options = get_modified_options(options);
        storage_uri uri = protocol::generate_queue_uri(service_client(), *this);

        std::shared_ptr<core::storage_command<bool>> command = std::make_shared<core::storage_command<bool>>(uri);
        command->set_build_request(std::bind(protocol::download_queue_metadata, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));
        command->set_authentication_handler(service_client().authentication_handler());
        command->set_location_mode(allow_secondary ? core::command_location_mode::primary_or_secondary : core::command_location_mode::primary_only);
        command->set_preprocess_response([] (const web::http::http_response& response, const request_result& result, operation_context context) -> bool
        {
            if (response.status_code() != web::http::status_codes::NotFound)
            {
                protocol::preprocess_response_void(response, result, context);
                return true;
            }

            return false;
        });
        return core::executor<bool>::execute_async(command, modified_options, context);
    }
    pplx::task<table_query_segment> cloud_table::execute_query_segmented_async(const table_query& query, const continuation_token& token, const table_request_options& options, operation_context context) const
    {
        table_request_options modified_options = get_modified_options(options);
        storage_uri uri = protocol::generate_table_uri(service_client(), *this, query, token);

        std::shared_ptr<core::storage_command<table_query_segment>> command = std::make_shared<core::storage_command<table_query_segment>>(uri);
        command->set_build_request(std::bind(protocol::execute_query, options.payload_format(), std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));
        command->set_authentication_handler(service_client().authentication_handler());
        command->set_location_mode(core::command_location_mode::primary_or_secondary, token.target_location());
        command->set_preprocess_response(std::bind(protocol::preprocess_response<table_query_segment>, table_query_segment(), std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));
        command->set_postprocess_response([] (const web::http::http_response& response, const request_result& result, const core::ostream_descriptor&, operation_context context) -> pplx::task<table_query_segment>
        {
            UNREFERENCED_PARAMETER(context);
            continuation_token next_token = protocol::table_response_parsers::parse_continuation_token(response, result);

            return response.extract_json().then([next_token] (const web::json::value& obj) -> table_query_segment
            {
                table_query_segment query_segment(protocol::table_response_parsers::parse_query_results(obj), std::move(next_token));
                return query_segment;
            });
        });
        return core::executor<table_query_segment>::execute_async(command, modified_options, context);
    }
    pplx::task<void> cloud_queue::update_message_async(cloud_queue_message& message, std::chrono::seconds visibility_timeout, bool update_content, queue_request_options& options, operation_context context)
    {
        if (message.id().empty())
        {
            throw std::invalid_argument(protocol::error_empty_message_id);
        }

        if (message.pop_receipt().empty())
        {
            throw std::invalid_argument(protocol::error_empty_message_pop_receipt);
        }

        if (visibility_timeout.count() < 0LL)
        {
            throw std::invalid_argument(protocol::error_negative_visibility_timeout);
        }

        if (visibility_timeout.count() > 604800LL)
        {
            throw std::invalid_argument(protocol::error_large_visibility_timeout);
        }

        queue_request_options modified_options = get_modified_options(options);
        storage_uri uri = protocol::generate_queue_message_uri(service_client(), *this, message);

        std::shared_ptr<core::storage_command<void>> command = std::make_shared<core::storage_command<void>>(uri);
        command->set_build_request(std::bind(protocol::update_message, message, visibility_timeout, update_content, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));
        command->set_authentication_handler(service_client().authentication_handler());
        command->set_preprocess_response([&message] (const web::http::http_response& response, const request_result& result, operation_context context) mutable
        {
            protocol::preprocess_response_void(response, result, context);
            message.set_pop_receipt(protocol::parse_pop_receipt(response));
            message.set_next_visible_time(protocol::parse_next_visible_time(response));
        });
        return core::executor<void>::execute_async(command, modified_options, context);
    }