support to try to use request-id as trace-id when trace context absent (#104)

diff --git a/lib/skywalking/tracer.lua b/lib/skywalking/tracer.lua
index cc08e6f..4becc49 100644
--- a/lib/skywalking/tracer.lua
+++ b/lib/skywalking/tracer.lua
@@ -43,7 +43,7 @@
     end
 
     local includeHostInEntrySpan = metadata_shdict:get('includeHostInEntrySpan')
-    local tracingContext = TC.new(serviceName, serviceInstanceName)
+    local tracingContext = TC.new(serviceName, serviceInstanceName, ngx.var.http_request_id)
     -- Constant pre-defined in SkyWalking main repo
     -- 6000 represents Nginx
     local contextCarrier = Util.tablepool_fetch("sw_contextCarrier")
diff --git a/lib/skywalking/tracing_context.lua b/lib/skywalking/tracing_context.lua
index 6d16209..c1b0f25 100644
--- a/lib/skywalking/tracing_context.lua
+++ b/lib/skywalking/tracing_context.lua
@@ -109,14 +109,21 @@
     return {is_noop = true}
 end
 
-function _M.new(serviceName, serviceInstanceName)
+function _M.new(serviceName, serviceInstanceName, requestId)
     if serviceInstanceName == nil or serviceName == nil then
         return _M.newNoOP()
     end
 
+    local segment_id = Util.newID()
+    -- use request_id as trace_id if it is present
+    local trace_id = requestId
+    if trace_id == nil then
+        trace_id = segment_id
+    end
+
     local tracing_context = Util.tablepool_fetch()
-    tracing_context.trace_id = Util.newID()
-    tracing_context.segment_id = tracing_context.trace_id
+    tracing_context.trace_id = trace_id
+    tracing_context.segment_id = segment_id
     tracing_context.service = serviceName
     tracing_context.service_instance = serviceInstanceName
     tracing_context.internal = Internal.new()
diff --git a/test/e2e/docker-compose.yml b/test/e2e/docker-compose.yml
index c6e0e20..156197d 100644
--- a/test/e2e/docker-compose.yml
+++ b/test/e2e/docker-compose.yml
@@ -52,7 +52,7 @@
 
   provider:
     build:
-      context: ./
+      context: ./e2e-service
       args:
         E2E_SERVICE_NAME: e2e-service-provider
         E2E_SERVICE_VERSION: ${E2E_SERVICE_VERSION}
@@ -77,7 +77,7 @@
 
   consumer:
     build:
-      context: ./
+      context: ./e2e-service
       args:
         E2E_SERVICE_NAME: e2e-service-consumer
         E2E_SERVICE_VERSION: ${E2E_SERVICE_VERSION}
@@ -109,12 +109,11 @@
     build:
       context: ./user
     volumes:
-      - ./user/expectedData.yaml:/expectedData.yaml
+      - ./user/expected:/expected
+      - ./user/config.yaml:/config.yaml
     environment:
       MAX_RETRY_TIMES: 3
-      SUFFIX_ENTRY: http://nginx:8080/suffix
-      SERVICE_ENTRY: http://consumer:9092/info
-      VALIDATION_ENTRY: http://skywalking-collector:12800/dataValidate
+      COLLECTOR: skywalking-collector:12800
     networks:
       e2e:
         ipv4_address: 172.16.238.14
diff --git a/test/e2e/Dockerfile b/test/e2e/e2e-service/Dockerfile
similarity index 100%
rename from test/e2e/Dockerfile
rename to test/e2e/e2e-service/Dockerfile
diff --git a/test/e2e/user/Dockerfile b/test/e2e/user/Dockerfile
index 91b7dec..f8af53e 100644
--- a/test/e2e/user/Dockerfile
+++ b/test/e2e/user/Dockerfile
@@ -14,9 +14,11 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-FROM alpine
+FROM eclipse-temurin:8-jre-alpine
 
 RUN apk add --no-cache curl
+RUN wget https://github.com/mikefarah/yq/releases/download/v4.24.5/yq_linux_amd64 -O /usr/bin/yq \
+    && chmod +x /usr/bin/yq
 
 COPY execute_and_validate.sh /execute_and_validate.sh
 
diff --git a/test/e2e/user/config.yaml b/test/e2e/user/config.yaml
new file mode 100644
index 0000000..67aae4c
--- /dev/null
+++ b/test/e2e/user/config.yaml
@@ -0,0 +1,29 @@
+# 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.
+scenarios:
+  - name: basic
+    expected: /expected/basic.yaml
+    entries:
+      - url: http://consumer:9092/info
+        method: POST
+      - url: http://nginx:8080/suffix
+  - name: request_id
+    expected: /expected/requestId.yaml
+    entries:
+      - url: http://nginx:8080/info
+        method: POST
+        headers:
+          - Request-Id: skywalking-e2e-request-id
diff --git a/test/e2e/user/execute_and_validate.sh b/test/e2e/user/execute_and_validate.sh
index 401dc91..4aab838 100644
--- a/test/e2e/user/execute_and_validate.sh
+++ b/test/e2e/user/execute_and_validate.sh
@@ -15,35 +15,72 @@
 # 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.
+trigger() {
+  url=$(pos=$1 yq '.entries.[env(pos)].url' ./scenario)
+  method=$(pos=$1 yq '.entries.[env(pos)].method // "GET"' ./scenario)
 
-rst=$(curl -X POST -Is ${SERVICE_ENTRY} | head -1 | grep "HTTP/1.1 200")
-if [[ -z "$rst" ]]; then
-  echo "failed to access ${SERVICE_ENTRY}"
-  exit 1
-fi
-echo "access ${SERVICE_ENTRY} success"
+  pos=$1 yq '.entries.[env(pos)].headers.[]' ./scenario > /req_header
 
-rst=$(curl -X GET -Is ${SUFFIX_ENTRY} | head -1 | grep "HTTP/1.1 200")
-if [[ -z "$rst" ]]; then
-  echo "failed to access ${SUFFIX_ENTRY}"
-  exit 1
-fi
-echo "access ${SUFFIX_ENTRY} success"
-
-sleep 5 # Wait Agent reported TraceSegment.
-
-times=0
-while [ $times -lt $MAX_RETRY_TIMES ]; do
-  curl -X POST --data-raw "$(cat /expectedData.yaml)" --dump-header ./header -o /response -s ${VALIDATION_ENTRY}
-  rst=$(head -1 /header | grep "HTTP/1.1 200")
-  if [[ -n "$rst" ]]; then
-    echo "Verification successful"
-    exit 0
+  rst=$(curl -Is -X $method -H @/req_header $url | head -1 | grep "HTTP/1.1 200")
+  if [[ -z "$rst" ]]; then
+    echo "failed to access $2"
+    exit 1
   fi
+  echo "access $2 successful"
+}
 
-  sleep 3
-  times=$((times+1))
+clear() {
+  rst=$(curl -Is -X GET http://${COLLECTOR}/receiveData/clear | head -1 | grep "HTTP/1.1 200")
+  if [[ -z "$rst" ]]; then
+    echo "failed to clear collector segments"
+    exit 1
+  fi
+  echo "sweep collector segments successful"
+}
+
+validate() {
+  name=$1
+  expectedData=$2
+
+  times=0
+  while [ $times -lt $MAX_RETRY_TIMES ]; do
+    curl -X POST --data-raw "$(cat $expectedData)" --dump-header ./header -o /response -s ${COLLECTOR}/dataValidate
+    rst=$(head -1 /header | grep "HTTP/1.1 200")
+    if [[ -n "$rst" ]]; then
+      echo "scenario $name verification successful"
+      return 0
+    fi
+
+    sleep 3
+    times=$((times+1))
+  done
+
+  cat /response
+  echo "scenario $name verification failed"
+  exit 1
+}
+
+scenarios=$(yq e '.scenarios | length' /config.yaml)
+echo "total scenarios number: $scenarios"
+
+scenario=0
+while [ $scenario -lt $scenarios ]; do
+  clear
+
+  pos=$scenario yq -P '.scenarios.[env(pos)]' /config.yaml > ./scenario
+  name=$(yq '.name' ./scenario)
+  entries=$(yq '.entries | length' ./scenario)
+  expectedData=$(yq '.expected' ./scenario)
+
+  entry=0
+  while [ $entry -lt $entries ]; do
+    trigger $entry
+
+    entry=$((entry+1))
+  done
+
+  sleep 5 # wait for agent report trace segments.
+  validate $name $expectedData
+
+  scenario=$((scenario+1))
 done
-
-cat /response
-exit 1
diff --git a/test/e2e/user/expectedData.yaml b/test/e2e/user/expected/basic.yaml
similarity index 98%
rename from test/e2e/user/expectedData.yaml
rename to test/e2e/user/expected/basic.yaml
index 7d3c505..cc0c75a 100644
--- a/test/e2e/user/expectedData.yaml
+++ b/test/e2e/user/expected/basic.yaml
@@ -129,7 +129,7 @@
       - key: http.status
         value: '200'
 - serviceName: e2e-service-provider
-  segmentSize: gt 1
+  segmentSize: ge 1
   segments:
   - segmentId: not nul
     spans:
diff --git a/test/e2e/user/expected/requestId.yaml b/test/e2e/user/expected/requestId.yaml
new file mode 100644
index 0000000..ee80852
--- /dev/null
+++ b/test/e2e/user/expected/requestId.yaml
@@ -0,0 +1,81 @@
+# 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.
+segmentItems:
+- serviceName: skywalking-nginx
+  segmentSize: ge 1
+  segments:
+  - segmentId: not null
+    spans:
+    - operationName: /info
+      parentSpanId: 0
+      spanId: 1
+      spanLayer: Http
+      startTime: gt 0
+      endTime: gt 0
+      componentId: 6000
+      isError: false
+      spanType: Exit
+      peer: 'e2e-test-with-mock-collector:upstream_ip:port'
+      skipAnalysis: false
+      tags:
+      - key: http.status
+        value: '200'
+    - operationName: /info
+      parentSpanId: -1
+      spanId: 0
+      spanLayer: Http
+      startTime: gt 0
+      endTime: gt 0
+      componentId: 6000
+      isError: false
+      spanType: Entry
+      peer: ''
+      skipAnalysis: false
+      tags:
+      - key: http.method
+        value: POST
+      - key: http.params
+        value: 'http://nginx/info'
+      - key: http.status
+        value: '200'
+- serviceName: e2e-service-provider
+  segmentSize: ge 1
+  segments:
+  - segmentId: not nul
+    spans:
+    - operationName: POST:/info
+      parentSpanId: -1
+      spanId: 0
+      spanLayer: Http
+      startTime: gt 0
+      endTime: gt 0
+      componentId: 1
+      isError: false
+      spanType: Entry
+      peer: ''
+      skipAnalysis: false
+      tags:
+      - {key: url, value: 'http://provider:9090/info'}
+      - {key: http.method, value: POST}
+      refs:
+      - parentEndpoint: /info
+        networkAddress: 'e2e-test-with-mock-collector:upstream_ip:port'
+        refType: CrossProcess
+        parentSpanId: 1
+        parentTraceSegmentId: not null
+        parentServiceInstance: e2e
+        parentService: skywalking-nginx
+        traceId: 'skywalking-e2e-request-id'