This is an automated email from the ASF dual-hosted git repository.

liubao pushed a commit to branch 2.8.x
in repository https://gitbox.apache.org/repos/asf/servicecomb-java-chassis.git


The following commit(s) were added to refs/heads/2.8.x by this push:
     new 221fb379b [#4405]header parameter support array type (#4408)
221fb379b is described below

commit 221fb379b2582d82308fa9fc4e85a03545efac5b
Author: liubao68 <[email protected]>
AuthorDate: Tue Jul 9 09:37:12 2024 +0800

    [#4405]header parameter support array type (#4408)
---
 .../common/rest/codec/header/HeaderCodec.java      |  45 +++++++++
 .../common/rest/codec/header/HeaderCodecCsv.java   |  28 ++++++
 .../common/rest/codec/header/HeaderCodecMulti.java |  66 +++++++++++++
 .../common/rest/codec/header/HeaderCodecPipes.java |  30 ++++++
 .../rest/codec/header/HeaderCodecSimple.java       |  52 ++++++++++
 .../common/rest/codec/header/HeaderCodecSsv.java   |  28 ++++++
 .../common/rest/codec/header/HeaderCodecTsv.java   |  27 ++++++
 .../codec/header/HeaderCodecWithDelimiter.java     |  84 ++++++++++++++++
 .../rest/codec/header/HeaderCodecsUtils.java       |  49 ++++++++++
 .../rest/codec/param/HeaderProcessorCreator.java   |  44 +++------
 .../rest/codec/param/TestHeaderProcessor.java      |   1 +
 .../codec/param/TestRestClientRequestImpl.java     |  63 +++++-------
 .../demo/api/IHeaderParamWithListSchema.java       |  64 ++++++++++++
 .../client/ConsumerHeaderParamWithListSchema.java  |  61 ++++++++++++
 .../src/main/resources/application.yml             |  10 ++
 .../server/HeaderParamWithListSchema.java          |  55 +++++++++++
 .../tests/HeaderParamWithListSchemaIT.java         | 108 +++++++++++++++++++++
 .../demo/zeroconfig/tests/ServerTest.java          |   3 +
 18 files changed, 746 insertions(+), 72 deletions(-)

diff --git 
a/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/header/HeaderCodec.java
 
b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/header/HeaderCodec.java
new file mode 100644
index 000000000..546605d6b
--- /dev/null
+++ 
b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/header/HeaderCodec.java
@@ -0,0 +1,45 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.servicecomb.common.rest.codec.header;
+
+import java.io.UnsupportedEncodingException;
+import java.net.URLEncoder;
+import java.nio.charset.StandardCharsets;
+
+import org.apache.servicecomb.common.rest.codec.RestClientRequest;
+import org.apache.servicecomb.common.rest.codec.RestObjectMapperFactory;
+import 
org.apache.servicecomb.common.rest.codec.param.HeaderProcessorCreator.HeaderProcessor;
+
+import javax.servlet.http.HttpServletRequest;
+
+public interface HeaderCodec {
+  static String encodeValue(Object value) throws UnsupportedEncodingException {
+    return URLEncoder.encode(value.toString(), StandardCharsets.UTF_8.name());
+  }
+
+  // can not be replaced by value.toString() because of date serialize
+  static String convertToString(Object value) throws Exception {
+    return 
RestObjectMapperFactory.getRestObjectMapper().convertToString(value);
+  }
+
+  String getCodecName();
+
+  void encode(RestClientRequest clientRequest, String name, Object value) 
throws Exception;
+
+  Object decode(HeaderProcessor processor, HttpServletRequest request);
+}
diff --git 
a/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/header/HeaderCodecCsv.java
 
b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/header/HeaderCodecCsv.java
new file mode 100644
index 000000000..c2db1f12f
--- /dev/null
+++ 
b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/header/HeaderCodecCsv.java
@@ -0,0 +1,28 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.servicecomb.common.rest.codec.header;
+
+public class HeaderCodecCsv extends HeaderCodecWithDelimiter {
+  public static final String CODEC_NAME = "csv";
+
+  public static final String DELIMITER = ",";
+
+  public HeaderCodecCsv() {
+    super(CODEC_NAME, DELIMITER, DELIMITER);
+  }
+}
diff --git 
a/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/header/HeaderCodecMulti.java
 
b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/header/HeaderCodecMulti.java
new file mode 100644
index 000000000..e53e09ca0
--- /dev/null
+++ 
b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/header/HeaderCodecMulti.java
@@ -0,0 +1,66 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.servicecomb.common.rest.codec.header;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Enumeration;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.ws.rs.core.Response.Status;
+
+import org.apache.servicecomb.common.rest.codec.RestClientRequest;
+import org.apache.servicecomb.common.rest.codec.RestObjectMapperFactory;
+import 
org.apache.servicecomb.common.rest.codec.param.HeaderProcessorCreator.HeaderProcessor;
+import org.apache.servicecomb.swagger.invocation.exception.CommonExceptionData;
+import org.apache.servicecomb.swagger.invocation.exception.InvocationException;
+
+public class HeaderCodecMulti implements HeaderCodec {
+  public static final String NAME = "multi";
+
+  @Override
+  public String getCodecName() {
+    return NAME;
+  }
+
+  @Override
+  public void encode(RestClientRequest clientRequest, String name, Object 
value) throws Exception {
+    if (null == value) {
+      // if value is empty, header should not be set to clientRequest to avoid 
NullPointerException in Netty.
+      return;
+    }
+    if (!(value instanceof Collection<?>)) {
+      throw new InvocationException(Status.BAD_REQUEST,
+          new CommonExceptionData("Array type of header should be 
Collection"));
+    }
+    for (Object item : ((Collection<?>) value)) {
+      clientRequest.getHeaders().add(name,
+          
RestObjectMapperFactory.getConsumerWriterMapper().convertToString(item));
+    }
+  }
+
+  @Override
+  public Object decode(HeaderProcessor processor, HttpServletRequest request) {
+    Enumeration<String> headerValues = 
request.getHeaders(processor.getParameterPath());
+    if (headerValues == null) {
+      //Even if the paramPath does not exist, headerValues won't be null at now
+      return null;
+    }
+    return processor.convertValue(Collections.list(headerValues), 
processor.getTargetType());
+  }
+}
diff --git 
a/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/header/HeaderCodecPipes.java
 
b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/header/HeaderCodecPipes.java
new file mode 100644
index 000000000..4a96aaebe
--- /dev/null
+++ 
b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/header/HeaderCodecPipes.java
@@ -0,0 +1,30 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.servicecomb.common.rest.codec.header;
+
+public class HeaderCodecPipes extends HeaderCodecWithDelimiter {
+  public static final String CODEC_NAME = "pipes";
+
+  public static final String JOIN_DELIMITER = "|";
+
+  public static final String SPLIT_DELIMITER = "\\|";
+
+  public HeaderCodecPipes() {
+    super(CODEC_NAME, JOIN_DELIMITER, SPLIT_DELIMITER);
+  }
+}
diff --git 
a/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/header/HeaderCodecSimple.java
 
b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/header/HeaderCodecSimple.java
new file mode 100644
index 000000000..3c37e56e2
--- /dev/null
+++ 
b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/header/HeaderCodecSimple.java
@@ -0,0 +1,52 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.servicecomb.common.rest.codec.header;
+
+import javax.servlet.http.HttpServletRequest;
+
+import org.apache.servicecomb.common.rest.codec.RestClientRequest;
+import org.apache.servicecomb.common.rest.codec.RestObjectMapperFactory;
+import 
org.apache.servicecomb.common.rest.codec.param.HeaderProcessorCreator.HeaderProcessor;
+
+public class HeaderCodecSimple implements HeaderCodec {
+  public static final String NAME = "simple";
+
+  @Override
+  public String getCodecName() {
+    return NAME;
+  }
+
+  @Override
+  public void encode(RestClientRequest clientRequest, String name, Object 
value) throws Exception {
+    if (null == value) {
+      // if value is empty, header should not be set to clientRequest to avoid 
NullPointerException in Netty.
+      return;
+    }
+    clientRequest.putHeader(name,
+        
RestObjectMapperFactory.getConsumerWriterMapper().convertToString(value));
+  }
+
+  @Override
+  public Object decode(HeaderProcessor processor, HttpServletRequest request) {
+    Object value = request.getHeader(processor.getParameterPath());
+    if (value == null) {
+      value = processor.checkRequiredAndDefaultValue();
+    }
+    return processor.convertValue(value, processor.getTargetType());
+  }
+}
diff --git 
a/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/header/HeaderCodecSsv.java
 
b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/header/HeaderCodecSsv.java
new file mode 100644
index 000000000..9de25de2e
--- /dev/null
+++ 
b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/header/HeaderCodecSsv.java
@@ -0,0 +1,28 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.servicecomb.common.rest.codec.header;
+
+public class HeaderCodecSsv extends HeaderCodecWithDelimiter {
+  public static final String CODEC_NAME = "ssv";
+
+  public static final String DELIMITER = " ";
+
+  public HeaderCodecSsv() {
+    super(CODEC_NAME, DELIMITER, DELIMITER);
+  }
+}
diff --git 
a/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/header/HeaderCodecTsv.java
 
b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/header/HeaderCodecTsv.java
new file mode 100644
index 000000000..51dda805c
--- /dev/null
+++ 
b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/header/HeaderCodecTsv.java
@@ -0,0 +1,27 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.servicecomb.common.rest.codec.header;
+
+public class HeaderCodecTsv extends HeaderCodecWithDelimiter {
+  public static final String CODEC_NAME = "tsv";
+
+  public static final String DELIMITER = "\t";
+
+  public HeaderCodecTsv() {
+    super(CODEC_NAME, DELIMITER, DELIMITER);
+  }
+}
diff --git 
a/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/header/HeaderCodecWithDelimiter.java
 
b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/header/HeaderCodecWithDelimiter.java
new file mode 100644
index 000000000..75481c8ce
--- /dev/null
+++ 
b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/header/HeaderCodecWithDelimiter.java
@@ -0,0 +1,84 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.servicecomb.common.rest.codec.header;
+
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.StringJoiner;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.ws.rs.core.Response.Status;
+
+import org.apache.servicecomb.common.rest.codec.RestClientRequest;
+import org.apache.servicecomb.common.rest.codec.RestObjectMapperFactory;
+import 
org.apache.servicecomb.common.rest.codec.param.HeaderProcessorCreator.HeaderProcessor;
+import org.apache.servicecomb.swagger.invocation.exception.CommonExceptionData;
+import org.apache.servicecomb.swagger.invocation.exception.InvocationException;
+
+public abstract class HeaderCodecWithDelimiter implements HeaderCodec {
+  private final String name;
+
+  private final String joinDelimiter;
+
+  private final String splitDelimiter;
+
+  public HeaderCodecWithDelimiter(String name, String joinDelimiter, String 
splitDelimiter) {
+    this.name = name;
+    this.joinDelimiter = joinDelimiter;
+    this.splitDelimiter = splitDelimiter;
+  }
+
+
+  @Override
+  public String getCodecName() {
+    return name;
+  }
+
+  @Override
+  public void encode(RestClientRequest clientRequest, String name, Object 
value) throws Exception {
+    if (null == value) {
+      // if value is empty, header should not be set to clientRequest to avoid 
NullPointerException in Netty.
+      return;
+    }
+    if (!(value instanceof Collection<?>)) {
+      throw new InvocationException(Status.BAD_REQUEST,
+          new CommonExceptionData("Array type of header should be 
Collection"));
+    }
+    clientRequest.putHeader(name, join((Collection<?>) value));
+  }
+
+  protected String join(Collection<?> values) throws Exception {
+    StringJoiner joiner = new StringJoiner(joinDelimiter);
+    for (Object value : values) {
+      String strValue = 
RestObjectMapperFactory.getConsumerWriterMapper().convertToString(value);
+      joiner.add(strValue);
+    }
+
+    return joiner.toString();
+  }
+
+  @Override
+  public Object decode(HeaderProcessor processor, HttpServletRequest request) {
+    String headerValues = request.getHeader(processor.getParameterPath());
+    if (headerValues == null) {
+      headerValues = (String) processor.checkRequiredAndDefaultValue();
+    }
+
+    return 
processor.convertValue(Arrays.asList(headerValues.split(splitDelimiter)), 
processor.getTargetType());
+  }
+}
diff --git 
a/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/header/HeaderCodecsUtils.java
 
b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/header/HeaderCodecsUtils.java
new file mode 100644
index 000000000..cbb968a31
--- /dev/null
+++ 
b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/header/HeaderCodecsUtils.java
@@ -0,0 +1,49 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.servicecomb.common.rest.codec.header;
+
+import java.util.HashMap;
+import java.util.Map;
+
+public class HeaderCodecsUtils {
+  private static final Map<String, HeaderCodec> CODECS;
+
+  static {
+    CODECS = new HashMap<>();
+    CODECS.put(HeaderCodecSimple.NAME, new HeaderCodecSimple());
+    CODECS.put(HeaderCodecMulti.NAME, new HeaderCodecMulti());
+    CODECS.put(HeaderCodecCsv.CODEC_NAME, new HeaderCodecCsv());
+    CODECS.put(HeaderCodecTsv.CODEC_NAME, new HeaderCodecTsv());
+    CODECS.put(HeaderCodecPipes.CODEC_NAME, new HeaderCodecPipes());
+    CODECS.put(HeaderCodecSsv.CODEC_NAME, new HeaderCodecSsv());
+  }
+
+  private HeaderCodecsUtils() {
+  }
+
+  public static HeaderCodec find(String collectionFormat) {
+    return CODECS.get(formatName(collectionFormat));
+  }
+
+  private static String formatName(String collectionFormat) {
+    if (collectionFormat == null) {
+      return HeaderCodecSimple.NAME;
+    }
+    return collectionFormat;
+  }
+}
diff --git 
a/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/param/HeaderProcessorCreator.java
 
b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/param/HeaderProcessorCreator.java
index cb40fdf64..30846a27c 100644
--- 
a/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/param/HeaderProcessorCreator.java
+++ 
b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/param/HeaderProcessorCreator.java
@@ -18,17 +18,15 @@
 package org.apache.servicecomb.common.rest.codec.param;
 
 import java.lang.reflect.Type;
-import java.util.Collections;
-import java.util.Enumeration;
 
 import javax.servlet.http.HttpServletRequest;
 import javax.ws.rs.core.Response.Status;
 
 import org.apache.servicecomb.common.rest.codec.RestClientRequest;
-import org.apache.servicecomb.common.rest.codec.RestObjectMapperFactory;
+import org.apache.servicecomb.common.rest.codec.header.HeaderCodec;
+import org.apache.servicecomb.common.rest.codec.header.HeaderCodecMulti;
+import org.apache.servicecomb.common.rest.codec.header.HeaderCodecsUtils;
 import org.apache.servicecomb.swagger.invocation.exception.InvocationException;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
 
 import com.fasterxml.jackson.databind.JavaType;
 import com.fasterxml.jackson.databind.type.TypeFactory;
@@ -39,8 +37,6 @@ import io.swagger.models.parameters.Parameter;
 import io.swagger.models.properties.ArrayProperty;
 
 public class HeaderProcessorCreator implements ParamValueProcessorCreator {
-  private static final Logger LOGGER = 
LoggerFactory.getLogger(HeaderProcessorCreator.class);
-
   public static final String PARAMTYPE = "header";
 
   public static class HeaderProcessor extends AbstractParamProcessor {
@@ -48,33 +44,25 @@ public class HeaderProcessorCreator implements 
ParamValueProcessorCreator {
     private final boolean ignoreRequiredCheck = 
DynamicPropertyFactory.getInstance()
         
.getBooleanProperty("servicecomb.rest.parameter.header.ignoreRequiredCheck", 
false).get();
 
-    private final boolean repeatedType;
+    private final HeaderCodec headerCodec;
 
     public HeaderProcessor(HeaderParameter headerParameter, JavaType 
targetType) {
       super(headerParameter.getName(), targetType, 
headerParameter.getDefaultValue(), headerParameter.getRequired());
 
-      this.repeatedType = ArrayProperty.isType(headerParameter.getType());
+      if ((ArrayProperty.isType(headerParameter.getType())) && 
headerParameter.getCollectionFormat() == null) {
+        // compatible to default settings
+        this.headerCodec = HeaderCodecsUtils.find(HeaderCodecMulti.NAME);
+      } else {
+        this.headerCodec = 
HeaderCodecsUtils.find(headerParameter.getCollectionFormat());
+      }
     }
 
     @Override
     public Object getValue(HttpServletRequest request) {
-      if (repeatedType) {
-        Enumeration<String> headerValues = request.getHeaders(paramPath);
-        if (headerValues == null) {
-          //Even if the paramPath does not exist, headerValues won't be null 
at now
-          return null;
-        }
-        return convertValue(Collections.list(headerValues), targetType);
-      }
-
-      Object value = request.getHeader(paramPath);
-      if (value == null) {
-        value = checkRequiredAndDefaultValue();
-      }
-      return convertValue(value, targetType);
+      return headerCodec.decode(this, request);
     }
 
-    private Object checkRequiredAndDefaultValue() {
+    public Object checkRequiredAndDefaultValue() {
       if (!ignoreRequiredCheck && isRequired()) {
         throw new InvocationException(Status.BAD_REQUEST, "Parameter is 
required.");
       }
@@ -83,13 +71,7 @@ public class HeaderProcessorCreator implements 
ParamValueProcessorCreator {
 
     @Override
     public void setValue(RestClientRequest clientRequest, Object arg) throws 
Exception {
-      if (null == arg) {
-        // null header should not be set to clientRequest to avoid 
NullPointerException in Netty.
-        LOGGER.debug("Header arg is null, will not be set into clientRequest. 
paramPath = [{}]", paramPath);
-        return;
-      }
-      clientRequest.putHeader(paramPath,
-          
RestObjectMapperFactory.getConsumerWriterMapper().convertToString(arg));
+      headerCodec.encode(clientRequest, paramPath, arg);
     }
 
     @Override
diff --git 
a/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/codec/param/TestHeaderProcessor.java
 
b/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/codec/param/TestHeaderProcessor.java
index c3f07c582..4058f045e 100644
--- 
a/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/codec/param/TestHeaderProcessor.java
+++ 
b/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/codec/param/TestHeaderProcessor.java
@@ -65,6 +65,7 @@ public class TestHeaderProcessor {
 
     if (javaType.isContainerType()) {
       headerParameter.type(ArrayProperty.TYPE);
+      headerParameter.setCollectionFormat("multi");
     }
     return new HeaderProcessor(headerParameter, javaType);
   }
diff --git 
a/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/codec/param/TestRestClientRequestImpl.java
 
b/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/codec/param/TestRestClientRequestImpl.java
index be86969fd..70e3f4bdd 100644
--- 
a/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/codec/param/TestRestClientRequestImpl.java
+++ 
b/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/codec/param/TestRestClientRequestImpl.java
@@ -16,9 +16,9 @@
  */
 package org.apache.servicecomb.common.rest.codec.param;
 
+import static org.assertj.core.api.Assertions.assertThat;
+
 import java.util.Arrays;
-import java.util.HashMap;
-import java.util.Map;
 import java.util.UUID;
 
 import javax.servlet.http.Part;
@@ -31,9 +31,6 @@ import org.junit.jupiter.api.AfterEach;
 import org.junit.jupiter.api.Assertions;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
-import org.junit.jupiter.api.condition.EnabledForJreRange;
-import org.junit.jupiter.api.condition.EnabledOnJre;
-import org.junit.jupiter.api.condition.JRE;
 import org.mockito.MockedStatic;
 import org.mockito.Mockito;
 
@@ -43,7 +40,7 @@ import io.vertx.core.Context;
 import io.vertx.core.MultiMap;
 import io.vertx.core.buffer.Buffer;
 import io.vertx.core.http.HttpClientRequest;
-import static org.assertj.core.api.Assertions.assertThat;
+import io.vertx.core.http.impl.headers.HeadersMultiMap;
 
 public class TestRestClientRequestImpl {
   private HttpClientRequest request;
@@ -74,13 +71,15 @@ public class TestRestClientRequestImpl {
   public void testCookie() throws Exception {
     final MultiMap map = MultiMap.caseInsensitiveMultiMap();
     Mockito.doAnswer(invocation -> {
-      map.add(io.vertx.core.http.HttpHeaders.COOKIE, 
"sessionid=abcdefghijklmnopqrstuvwxyz; region=china-north; ");
-      return null;
-    }).when(request).putHeader(io.vertx.core.http.HttpHeaders.COOKIE, 
"sessionid=abcdefghijklmnopqrstuvwxyz; region=china-north; ");
+          map.add(io.vertx.core.http.HttpHeaders.COOKIE, 
"sessionid=abcdefghijklmnopqrstuvwxyz; region=china-north; ");
+          return null;
+        }).when(request)
+        .putHeader(io.vertx.core.http.HttpHeaders.COOKIE, 
"sessionid=abcdefghijklmnopqrstuvwxyz; region=china-north; ");
     Mockito.doAnswer(invocation -> {
-      map.add(io.vertx.core.http.HttpHeaders.COOKIE, 
"sessionid=abcdefghijklmnopqrstuvwxyz; region=china-north; ");
-      return null;
-    }).when(request).putHeader(io.vertx.core.http.HttpHeaders.COOKIE, 
"region=china-north; sessionid=abcdefghijklmnopqrstuvwxyz; ");
+          map.add(io.vertx.core.http.HttpHeaders.COOKIE, 
"sessionid=abcdefghijklmnopqrstuvwxyz; region=china-north; ");
+          return null;
+        }).when(request)
+        .putHeader(io.vertx.core.http.HttpHeaders.COOKIE, "region=china-north; 
sessionid=abcdefghijklmnopqrstuvwxyz; ");
     Mockito.when(request.headers()).thenReturn(map);
 
     RestClientRequestImpl restClientRequest = new 
RestClientRequestImpl(request, null, null);
@@ -149,44 +148,26 @@ public class TestRestClientRequestImpl {
   }
 
   @Test
-  @EnabledOnJre(JRE.JAVA_8)
-  public void doEndWithUploadForJre8() {
-    Map<String, String> headers = new HashMap<>();
-    Mockito.doAnswer(invocation -> {
-      headers.put(HttpHeaders.CONTENT_TYPE, "multipart/form-data; 
charset=UTF-8; boundary=boundarynull-null-null-null-null");
-      return null;
-    }).when(request).putHeader(HttpHeaders.CONTENT_TYPE, "multipart/form-data; 
charset=UTF-8; boundary=boundarynull-null-null-null-null");
-
-    UUID uuid = new UUID(0, 0);
-    try (MockedStatic<UUID> mockedStatic = Mockito.mockStatic(UUID.class)) {
-      mockedStatic.when(UUID::randomUUID).thenReturn(uuid);
-      RestClientRequestImpl restClientRequest = new 
RestClientRequestImpl(request, context, null);
-      restClientRequest.attach("file", null);
-      restClientRequest.doEndWithUpload();
-
-      Assertions.assertEquals("multipart/form-data; charset=UTF-8; 
boundary=boundarynull-null-null-null-null",
-              headers.get(HttpHeaders.CONTENT_TYPE));
-    }
-  }
-
-  @Test
-  @EnabledForJreRange(min = JRE.JAVA_9)
-  public void doEndWithUploadAfterJre8() {
-    Map<String, String> headers = new HashMap<>();
+  public void testDoEndWithUpload() {
+    MultiMap headers = HeadersMultiMap.headers();
     Mockito.doAnswer(invocation -> {
-      headers.put(HttpHeaders.CONTENT_TYPE, "multipart/form-data; 
charset=UTF-8; boundary=boundary00000000-0000-0000-0000-000000000000");
+      headers.add(HttpHeaders.CONTENT_TYPE,
+          "multipart/form-data; charset=UTF-8; 
boundary=boundary00000000-0000-0000-0000-000000000000");
       return null;
-    }).when(request).putHeader(HttpHeaders.CONTENT_TYPE, "multipart/form-data; 
charset=UTF-8; boundary=boundary00000000-0000-0000-0000-000000000000");
+    }).when(request).putHeader(HttpHeaders.CONTENT_TYPE,
+        "multipart/form-data; charset=UTF-8; 
boundary=boundary00000000-0000-0000-0000-000000000000");
 
-    UUID uuid = new UUID(0, 0);
+    UUID uuid = Mockito.mock(UUID.class);
+    
Mockito.when(uuid.toString()).thenReturn("00000000-0000-0000-0000-000000000000");
     try (MockedStatic<UUID> mockedStatic = Mockito.mockStatic(UUID.class)) {
       mockedStatic.when(UUID::randomUUID).thenReturn(uuid);
       RestClientRequestImpl restClientRequest = new 
RestClientRequestImpl(request, context, null);
       restClientRequest.attach("file", null);
       restClientRequest.doEndWithUpload();
 
-      Assertions.assertEquals("multipart/form-data; charset=UTF-8; 
boundary=boundary00000000-0000-0000-0000-000000000000",
-              headers.get(HttpHeaders.CONTENT_TYPE));
+      Assertions.assertEquals(
+          "multipart/form-data; charset=UTF-8; 
boundary=boundary00000000-0000-0000-0000-000000000000",
+          headers.get(HttpHeaders.CONTENT_TYPE));
     }
   }
 }
diff --git 
a/demo/demo-schema/src/main/java/org/apache/servicecomb/demo/api/IHeaderParamWithListSchema.java
 
b/demo/demo-schema/src/main/java/org/apache/servicecomb/demo/api/IHeaderParamWithListSchema.java
new file mode 100644
index 000000000..0f0b5b5a9
--- /dev/null
+++ 
b/demo/demo-schema/src/main/java/org/apache/servicecomb/demo/api/IHeaderParamWithListSchema.java
@@ -0,0 +1,64 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.servicecomb.demo.api;
+
+import java.util.List;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.HeaderParam;
+import javax.ws.rs.Path;
+
+import io.swagger.annotations.ApiParam;
+
+@Path("/headerList")
+public interface IHeaderParamWithListSchema {
+  @Path("headerListDefault")
+  @GET
+  String headerListDefault(
+      @HeaderParam("headerList") List<String> headerList);
+
+  @Path("headerListCSV")
+  @GET
+  String headerListCSV(
+      @ApiParam(collectionFormat = "csv") @HeaderParam("headerList")
+          List<String> headerList);
+
+  @Path("headerListMULTI")
+  @GET
+  String headerListMULTI(
+      @ApiParam(collectionFormat = "multi") @HeaderParam("headerList")
+          List<String> headerList);
+
+  @Path("headerListSSV")
+  @GET
+  String headerListSSV(
+      @ApiParam(collectionFormat = "ssv") @HeaderParam("headerList")
+          List<String> headerList);
+
+  @Path("headerListPIPES")
+  @GET
+  String headerListPIPES(
+      @ApiParam(collectionFormat = "pipes") @HeaderParam("headerList")
+          List<String> headerList);
+
+  @Path("headerListTSV")
+  @GET
+  String headerListTSV(
+      @ApiParam(collectionFormat = "tsv") @HeaderParam("headerList")
+          List<String> headerList);
+}
diff --git 
a/demo/demo-zeroconfig-schemadiscovery-registry/demo-zeroconfig-schemadiscovery-registry-client/src/main/java/org/apache/servicecomb/demo/zeroconfig/client/ConsumerHeaderParamWithListSchema.java
 
b/demo/demo-zeroconfig-schemadiscovery-registry/demo-zeroconfig-schemadiscovery-registry-client/src/main/java/org/apache/servicecomb/demo/zeroconfig/client/ConsumerHeaderParamWithListSchema.java
new file mode 100644
index 000000000..4b759246e
--- /dev/null
+++ 
b/demo/demo-zeroconfig-schemadiscovery-registry/demo-zeroconfig-schemadiscovery-registry-client/src/main/java/org/apache/servicecomb/demo/zeroconfig/client/ConsumerHeaderParamWithListSchema.java
@@ -0,0 +1,61 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.servicecomb.demo.zeroconfig.client;
+
+import java.util.List;
+
+import org.apache.servicecomb.demo.api.IHeaderParamWithListSchema;
+import org.apache.servicecomb.provider.pojo.RpcReference;
+import org.apache.servicecomb.provider.rest.common.RestSchema;
+
+@RestSchema(schemaId = "ConsumerHeaderParamWithListSchema", schemaInterface = 
IHeaderParamWithListSchema.class)
+public class ConsumerHeaderParamWithListSchema implements 
IHeaderParamWithListSchema {
+  @RpcReference(microserviceName = 
"demo-zeroconfig-schemadiscovery-registry-server",
+      schemaId = "HeaderParamWithListSchema")
+  private IHeaderParamWithListSchema provider;
+
+  @Override
+  public String headerListDefault(List<String> headerList) {
+    return provider.headerListDefault(headerList);
+  }
+
+  @Override
+  public String headerListCSV(List<String> headerList) {
+    return provider.headerListCSV(headerList);
+  }
+
+  @Override
+  public String headerListMULTI(List<String> headerList) {
+    return provider.headerListMULTI(headerList);
+  }
+
+  @Override
+  public String headerListSSV(List<String> headerList) {
+    return provider.headerListSSV(headerList);
+  }
+
+  @Override
+  public String headerListPIPES(List<String> headerList) {
+    return provider.headerListPIPES(headerList);
+  }
+
+  @Override
+  public String headerListTSV(List<String> headerList) {
+    return provider.headerListTSV(headerList);
+  }
+}
diff --git 
a/demo/demo-zeroconfig-schemadiscovery-registry/demo-zeroconfig-schemadiscovery-registry-edge/src/main/resources/application.yml
 
b/demo/demo-zeroconfig-schemadiscovery-registry/demo-zeroconfig-schemadiscovery-registry-edge/src/main/resources/application.yml
index 685fd5937..8f327cbef 100644
--- 
a/demo/demo-zeroconfig-schemadiscovery-registry/demo-zeroconfig-schemadiscovery-registry-edge/src/main/resources/application.yml
+++ 
b/demo/demo-zeroconfig-schemadiscovery-registry/demo-zeroconfig-schemadiscovery-registry-edge/src/main/resources/application.yml
@@ -45,6 +45,16 @@ servicecomb:
               path: "/register/url/prefix/.*"
               microserviceName: demo-zeroconfig-schemadiscovery-registry-client
               versionRule: 0+
+            client-2:
+              prefixSegmentCount: 0
+              path: "/headerList/.*"
+              microserviceName: demo-zeroconfig-schemadiscovery-registry-client
+              versionRule: 0+
+
+  references:
+    transport:
+      
demo-zeroconfig-schemadiscovery-registry-client.ClientServerEndpoint.contextMapper:
 rest
+
   context:
     headerContextMapper: |
       gatewayHeader: context-gateway-header
diff --git 
a/demo/demo-zeroconfig-schemadiscovery-registry/demo-zeroconfig-schemadiscovery-registry-server/src/main/java/org/apache/servicecomb/demo/zeroconfig/server/HeaderParamWithListSchema.java
 
b/demo/demo-zeroconfig-schemadiscovery-registry/demo-zeroconfig-schemadiscovery-registry-server/src/main/java/org/apache/servicecomb/demo/zeroconfig/server/HeaderParamWithListSchema.java
new file mode 100644
index 000000000..f51852fb8
--- /dev/null
+++ 
b/demo/demo-zeroconfig-schemadiscovery-registry/demo-zeroconfig-schemadiscovery-registry-server/src/main/java/org/apache/servicecomb/demo/zeroconfig/server/HeaderParamWithListSchema.java
@@ -0,0 +1,55 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.servicecomb.demo.zeroconfig.server;
+
+import java.util.List;
+
+import org.apache.servicecomb.demo.api.IHeaderParamWithListSchema;
+import org.apache.servicecomb.provider.rest.common.RestSchema;
+
+@RestSchema(schemaId = "HeaderParamWithListSchema", schemaInterface = 
IHeaderParamWithListSchema.class)
+public class HeaderParamWithListSchema implements IHeaderParamWithListSchema {
+  @Override
+  public String headerListDefault(List<String> headerList) {
+    return headerList == null ? "null" : headerList.size() + ":" + headerList;
+  }
+
+  @Override
+  public String headerListCSV(List<String> headerList) {
+    return headerList == null ? "null" : headerList.size() + ":" + headerList;
+  }
+
+  @Override
+  public String headerListMULTI(List<String> headerList) {
+    return headerList == null ? "null" : headerList.size() + ":" + headerList;
+  }
+
+  @Override
+  public String headerListSSV(List<String> headerList) {
+    return headerList == null ? "null" : headerList.size() + ":" + headerList;
+  }
+
+  @Override
+  public String headerListPIPES(List<String> headerList) {
+    return headerList == null ? "null" : headerList.size() + ":" + headerList;
+  }
+
+  @Override
+  public String headerListTSV(List<String> headerList) {
+    return headerList == null ? "null" : headerList.size() + ":" + headerList;
+  }
+}
diff --git 
a/demo/demo-zeroconfig-schemadiscovery-registry/demo-zeroconfig-schemadiscovery-registry-tests/src/main/java/org/apache/servicecomb/demo/zeroconfig/tests/HeaderParamWithListSchemaIT.java
 
b/demo/demo-zeroconfig-schemadiscovery-registry/demo-zeroconfig-schemadiscovery-registry-tests/src/main/java/org/apache/servicecomb/demo/zeroconfig/tests/HeaderParamWithListSchemaIT.java
new file mode 100644
index 000000000..37bab8df0
--- /dev/null
+++ 
b/demo/demo-zeroconfig-schemadiscovery-registry/demo-zeroconfig-schemadiscovery-registry-tests/src/main/java/org/apache/servicecomb/demo/zeroconfig/tests/HeaderParamWithListSchemaIT.java
@@ -0,0 +1,108 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.servicecomb.demo.zeroconfig.tests;
+
+import org.apache.servicecomb.demo.CategorizedTestCase;
+import org.apache.servicecomb.demo.TestMgr;
+import org.springframework.http.HttpEntity;
+import org.springframework.http.HttpHeaders;
+import org.springframework.http.HttpMethod;
+import org.springframework.stereotype.Component;
+import org.springframework.util.MultiValueMap;
+import org.springframework.web.client.RestOperations;
+import org.springframework.web.client.RestTemplate;
+
+@Component
+public class HeaderParamWithListSchemaIT implements CategorizedTestCase {
+  private static final String GATEWAY_URL = "http://localhost:8888";;
+
+  RestOperations template = new RestTemplate();
+
+  @Override
+  public void testRestTransport() throws Exception {
+    testHeaderListDefault();
+    testHeaderListMulti();
+    testHeaderListCSV();
+    testHeaderListSSV();
+    testHeaderListTSV();
+    testHeaderListPipes();
+  }
+
+  // default to csv, this is different from Open API 3.0 which default to none.
+  private void testHeaderListDefault() {
+    MultiValueMap<String, String> headers = new HttpHeaders();
+    headers.add("headerList", "a,b,c");
+    HttpEntity<Void> entity = new HttpEntity<>(headers);
+    String result = template
+        .exchange(GATEWAY_URL + "/headerList/headerListDefault", 
HttpMethod.GET, entity, String.class).getBody();
+    TestMgr.check("\"3:[a, b, c]\"", result);
+  }
+
+  private void testHeaderListPipes() {
+    MultiValueMap<String, String> headers = new HttpHeaders();
+    headers.add("headerList", "a|b|c");
+    HttpEntity<Void> entity = new HttpEntity<>(headers);
+    String result = template
+        .exchange(GATEWAY_URL + "/headerList/headerListPIPES", HttpMethod.GET, 
entity, String.class).getBody();
+    TestMgr.check("\"3:[a, b, c]\"", result);
+  }
+
+  private void testHeaderListTSV() {
+    MultiValueMap<String, String> headers = new HttpHeaders();
+    headers.add("headerList", "a\tb\tc");
+    HttpEntity<Void> entity = new HttpEntity<>(headers);
+    String result = template
+        .exchange(GATEWAY_URL + "/headerList/headerListTSV", HttpMethod.GET, 
entity, String.class).getBody();
+    TestMgr.check("\"3:[a, b, c]\"", result);
+  }
+
+  private void testHeaderListSSV() {
+    MultiValueMap<String, String> headers = new HttpHeaders();
+    headers.add("headerList", "a b c");
+    HttpEntity<Void> entity = new HttpEntity<>(headers);
+    String result = template
+        .exchange(GATEWAY_URL + "/headerList/headerListSSV", HttpMethod.GET, 
entity, String.class).getBody();
+    TestMgr.check("\"3:[a, b, c]\"", result);
+  }
+
+  private void testHeaderListCSV() {
+    MultiValueMap<String, String> headers = new HttpHeaders();
+    headers.add("headerList", "a,b,c");
+    HttpEntity<Void> entity = new HttpEntity<>(headers);
+    String result = template
+        .exchange(GATEWAY_URL + "/headerList/headerListCSV", HttpMethod.GET, 
entity, String.class).getBody();
+    TestMgr.check("\"3:[a, b, c]\"", result);
+
+    headers.add("headerList", "a, b, c");
+    entity = new HttpEntity<>(headers);
+    result = template
+        .exchange(GATEWAY_URL + "/headerList/headerListCSV", HttpMethod.GET, 
entity, String.class).getBody();
+    TestMgr.check("\"3:[a, b, c]\"", result);
+  }
+
+  private void testHeaderListMulti() {
+    MultiValueMap<String, String> headers = new HttpHeaders();
+    headers.add("headerList", "a");
+    headers.add("headerList", "b");
+    headers.add("headerList", "c");
+    HttpEntity<Void> entity = new HttpEntity<>(headers);
+    String result = template
+        .exchange(GATEWAY_URL + "/headerList/headerListMULTI", HttpMethod.GET, 
entity, String.class).getBody();
+    TestMgr.check("\"3:[a, b, c]\"", result);
+  }
+}
diff --git 
a/demo/demo-zeroconfig-schemadiscovery-registry/demo-zeroconfig-schemadiscovery-registry-tests/src/main/java/org/apache/servicecomb/demo/zeroconfig/tests/ServerTest.java
 
b/demo/demo-zeroconfig-schemadiscovery-registry/demo-zeroconfig-schemadiscovery-registry-tests/src/main/java/org/apache/servicecomb/demo/zeroconfig/tests/ServerTest.java
index aaf8233df..363496a05 100644
--- 
a/demo/demo-zeroconfig-schemadiscovery-registry/demo-zeroconfig-schemadiscovery-registry-tests/src/main/java/org/apache/servicecomb/demo/zeroconfig/tests/ServerTest.java
+++ 
b/demo/demo-zeroconfig-schemadiscovery-registry/demo-zeroconfig-schemadiscovery-registry-tests/src/main/java/org/apache/servicecomb/demo/zeroconfig/tests/ServerTest.java
@@ -59,8 +59,11 @@ public class ServerTest implements CategorizedTestCase {
     RequestEntity<Void> requestEntity = new RequestEntity<>(headers, 
HttpMethod.GET,
         new 
URI("cse://demo-zeroconfig-schemadiscovery-registry-edge/register/url/prefix/contextMapper?clientQuery=v3&"
             + "gatewayQuery=v4"));
+    // test two times to check different transport(only use rest)
     ResponseEntity<String> response = template.exchange(requestEntity, 
String.class);
     TestMgr.check(response.getBody(), "success");
+    response = template.exchange(requestEntity, String.class);
+    TestMgr.check(response.getBody(), "success");
   }
 
   @SuppressWarnings("unchecked")


Reply via email to