@Override
	public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
		if (CollectionUtils.isEmpty(trafficStainers)) {
			return chain.filter(exchange);
		}

		// 1. get stained labels from request
		Map<String, String> stainedLabels = getStainedLabels(exchange);

		if (CollectionUtils.isEmpty(stainedLabels)) {
			return chain.filter(exchange);
		}

		// 2. put stained labels to metadata context
		ServerHttpRequest request = exchange.getRequest().mutate().headers((httpHeaders) -> {
			MetadataContext metadataContext = exchange.getAttribute(MetadataConstant.HeaderName.METADATA_CONTEXT);
			if (metadataContext == null) {
				metadataContext = MetadataContextHolder.get();
			}

			Map<String, String> oldTransitiveMetadata = metadataContext.getTransitiveMetadata();

			// append new transitive metadata
			Map<String, String> newTransitiveMetadata = new HashMap<>(oldTransitiveMetadata);
			newTransitiveMetadata.putAll(stainedLabels);

			metadataContext.setTransitiveMetadata(newTransitiveMetadata);
		}).build();

		return chain.filter(exchange.mutate().request(request).build());
	}
@Test
	public void testWithTrafficStainers() {
		MetadataContext metadataContext = new MetadataContext();
		MetadataContextHolder.set(metadataContext);

		RuleStainingProperties ruleStainingProperties = new RuleStainingProperties();
		ruleStainingProperties.setNamespace(testNamespace);
		ruleStainingProperties.setGroup(testGroup);
		ruleStainingProperties.setFileName(testFileName);

		ConfigFile configFile = Mockito.mock(ConfigFile.class);
		when(configFile.getContent()).thenReturn("{\n"
				+ "    \"rules\":[\n"
				+ "        {\n"
				+ "            \"conditions\":[\n"
				+ "                {\n"
				+ "                    \"key\":\"${http.query.uid}\",\n"
				+ "                    \"values\":[\"1000\"],\n"
				+ "                    \"operation\":\"EQUALS\"\n"
				+ "                }\n"
				+ "            ],\n"
				+ "            \"labels\":[\n"
				+ "                {\n"
				+ "                    \"key\":\"env\",\n"
				+ "                    \"value\":\"blue\"\n"
				+ "                }\n"
				+ "            ]\n"
				+ "        }\n"
				+ "    ]\n"
				+ "}");
		when(configFileService.getConfigFile(testNamespace, testGroup, testFileName)).thenReturn(configFile);

		StainingRuleManager stainingRuleManager = new StainingRuleManager(ruleStainingProperties, configFileService);
		RuleStainingExecutor ruleStainingExecutor = new RuleStainingExecutor();
		RuleTrafficStainer ruleTrafficStainer = new RuleTrafficStainer(stainingRuleManager, ruleStainingExecutor);

		TrafficStainingGatewayFilter filter = new TrafficStainingGatewayFilter(Collections.singletonList(ruleTrafficStainer));

		MockServerHttpRequest request = MockServerHttpRequest.get("/users")
				.queryParam("uid", "1000").build();
		MockServerWebExchange exchange = new MockServerWebExchange.Builder(request).build();

		filter.filter(exchange, chain);
		Map<String, String> map = metadataContext.getTransitiveMetadata();
		assertThat(map).isNotNull();
		assertThat(map.size()).isEqualTo(1);
		assertThat(map.get("env")).isEqualTo("blue");
	}