feat: support update the peer before requesting outgoing (#95)

diff --git a/README.md b/README.md
index ab21317..9f30f44 100644
--- a/README.md
+++ b/README.md
@@ -131,7 +131,8 @@
 ## Nginx APIs
 - **startTimer**, `require("skywalking.client"):startBackendTimer("http://127.0.0.1:8080")`. Start the backend timer. This timer register the metadata and report traces to the backend.
 - **destroyBackendTimer**, `require("skywalking.client"):destroyBackendTimer()`. Stop the timer created by `startBackendTimer`, and clean unreported data.
-- **start**, `require("skywalking.tracer"):start("upstream service", correlation)`. Begin the tracing before the upstream begin. The custom data (table type) can be injected as the second parameter, and then they will be propagated to the downstream service.
+- **start**, `require("skywalking.tracer"):start("upstream service", correlation)`. Begin the tracing before the upstream beginning. The custom data (table type) can be injected as the second parameter, and then they will be propagated to the downstream service. If `upstream service` could be determined precisely later, keep it as `nil` and call `inject` method when peer(upstream address) is resolved by load balancer and DNS resolver.
+- **inject**, `require("skywalking.tracer"):inject(exitSpan, peer, correlation)`. Inject an exit span context and correlation context into carrier, and then they will be propagated to the downstream service. (**Since v1.0**, advanced API, called when you update the peer of exit span.)
 - **finish**, `require("skywalking.tracer"):finish()`. Finish the tracing for this HTTP request.
 - **prepareForReport**, `require("skywalking.tracer"):prepareForReport()`. Prepare the finished segment for further report.
 
@@ -140,11 +141,15 @@
 - `TracingContext.new(serviceId, serviceInstID)`, create an active tracing context.
 - `TracingContext.newNoOP()`, create a no OP tracing context.
 - `TracingContext.drainAfterFinished()`, fetch the segment includes all finished spans.
+- `TracingContext.inject(exitSpan, peer, correlation)`, inject an exit span context and correlation context into carrier, and then they will be propagated to the downstream service by outgoing HTTP request. (**Since v1.0**, advanced API, called when you update the peer of exit span.)
 
 Create 2 kinds of span
 - `TracingContext.createEntrySpan(operationName, parent, contextCarrier)`
 - `TracingContext.createExitSpan(operationName, parent, peer, contextCarrier)`
 
+Create 2 kinds of span API v1
+- `TracingContext.createEntrySpan(operationName, parent, contextCarrier)`
+- `TracingContext.createExitSpan(operationName, parent)`
 
 # Contact Us
 * Submit an [issue](https://github.com/apache/skywalking/issues) with `[NIGNX-LUA]` as the issue title prefix.
diff --git a/lib/skywalking/correlation_context_test.lua b/lib/skywalking/correlation_context_test.lua
index a959f9a..87c08f0 100644
--- a/lib/skywalking/correlation_context_test.lua
+++ b/lib/skywalking/correlation_context_test.lua
@@ -18,9 +18,10 @@
 local lu = require('luaunit')
 local correlationContext = require('skywalking.correlation_context')
 local TC = require('skywalking.tracing_context')
+local Span = require("skywalking.span")
 
-TestCorelationContext = {}
-    function TestCorelationContext:testFromSW8Value()
+TestCorrelationContext = {}
+    function TestCorrelationContext:testFromSW8Value()
         -- simple analyze
         local context = correlationContext.fromSW8Value('dGVzdDE=:dDE=,dGVzdDI=:dDI=')
         lu.assertNotNil(context)
@@ -38,7 +39,7 @@
         lu.assertNotNil(#context == 0)
     end
 
-    function TestCorelationContext:testSerialize()
+    function TestCorrelationContext:testSerialize()
         -- serialize empty correlation
         local context = correlationContext.fromSW8Value('')
         local encode_context = correlationContext.serialize(context)
@@ -64,7 +65,7 @@
         lu.assertEquals(encode_context, "")
     end
 
-    function TestCorelationContext:testPut()
+    function TestCorrelationContext:testPut()
         -- put with empty key and value
         local context = correlationContext.fromSW8Value('')
         correlationContext.put(context, nil, nil)
@@ -88,7 +89,7 @@
         lu.assertEquals(context["test3"], "t3")
     end
 
-    function TestCorelationContext:testTracingContext()
+    function TestCorrelationContext:testTracingContext()
         -- transform data
         local context = TC.new("service", "instance")
         local header = {}
@@ -96,14 +97,25 @@
         TC.createEntrySpan(context, 'operation_name', nil, header)
         lu.assertNotNil(context.correlation)
         local contextCarrier = {}
-        TC.createExitSpan(context, 'operation_name', nil, 'peer', contextCarrier)
+
+        -- mock ngx.req.set_header(k, v)
+        ngx = {
+            req = {
+                set_header = function(k, v)
+                    contextCarrier[k] = v
+                end
+            }
+        }
+        local exitSpan = TC.createExitSpan(context, 'operation_name', nil)
+        TC.inject(context, exitSpan, 'peer', context.correlation)
         lu.assertNotNil(contextCarrier['sw8-correlation'])
         local correlation = correlationContext.fromSW8Value(contextCarrier['sw8-correlation'])
         lu.assertEquals(correlation["test1"], "t1")
         lu.assertEquals(correlation["test2"], "t2")
 
         -- transform data with adding data
-        TC.createExitSpan(context, 'operation_name', nil, 'peer', contextCarrier, {
+        exitSpan = TC.createExitSpan(context, 'operation_name', nil)
+        TC.inject(context, exitSpan, 'peer', {
             test3 = "t3"
         })
         lu.assertNotNil(contextCarrier['sw8-correlation'])
diff --git a/lib/skywalking/segment_ref.lua b/lib/skywalking/segment_ref.lua
index d3917fb..cfd5a49 100644
--- a/lib/skywalking/segment_ref.lua
+++ b/lib/skywalking/segment_ref.lua
@@ -57,6 +57,20 @@
     return ref
 end
 
+-- Create and initialize an injectable reference
+function _M.createInjectableRef(context, span)
+    local injectableRef = _M.new()
+    injectableRef.trace_id = context.trace_id
+    injectableRef.segment_id = context.segment_id
+    injectableRef.span_id = span.span_id
+    injectableRef.address_used_at_client = span.peer
+    injectableRef.parent_service = context.service
+    injectableRef.parent_service_instance = context.service_instance
+    injectableRef.parent_endpoint = span.parent_endpoint_name
+
+    return injectableRef
+end
+
 -- Return string to represent this ref.
 function _M.serialize(ref)
     local encodedRef = '1'
diff --git a/lib/skywalking/span.lua b/lib/skywalking/span.lua
index ce52b01..15d8978 100644
--- a/lib/skywalking/span.lua
+++ b/lib/skywalking/span.lua
@@ -86,29 +86,12 @@
 end
 
 -- Create an exit span. Represent the HTTP outgoing request.
-function _M.createExitSpan(operationName, context, parent, peer, contextCarrier)
+function _M.createExitSpan(operationName, context, parent)
     local span = _M.new(operationName, context, parent)
     span.is_exit = true
-    span.peer = peer
 
-    if contextCarrier ~= nil then
-        -- if there is contextCarrier container, the Span will inject the value based on the current tracing context
-        local injectableRef = SegmentRef.new()
-        injectableRef.trace_id = context.trace_id
-        injectableRef.segment_id = context.segment_id
-        injectableRef.span_id = span.span_id
-        injectableRef.address_used_at_client = peer
-        injectableRef.parent_service = context.service
-        injectableRef.parent_service_instance = context.service_instance
-
-        local firstSpan = context.internal.first_span
-        local parentEndpointName
-        parentEndpointName = firstSpan.operation_name
-        injectableRef.parent_endpoint = parentEndpointName
-
-        contextCarrier[CONTEXT_CARRIER_KEY] = SegmentRef.serialize(injectableRef)
-    end
-
+    local firstSpan = context.internal.first_span
+    span.parent_endpoint_name = firstSpan.operation_name
     return span
 end
 
@@ -252,6 +235,15 @@
     return span
 end
 
+function _M.setPeer(span, peer)
+    if span.is_noop then
+        return span
+    end
+    span.peer = peer
+
+    return span
+end
+
 -- Return SpanProtocol
 function _M.transform(span)
     local spanBuilder = Util.tablepool_fetch("sw_spanBuilder", 0, 32)
diff --git a/lib/skywalking/span_test.lua b/lib/skywalking/span_test.lua
index c544441..0c7456f 100644
--- a/lib/skywalking/span_test.lua
+++ b/lib/skywalking/span_test.lua
@@ -65,16 +65,11 @@
         local context = TC.new("service", "instance")
         lu.assertNotNil(context)
 
-        local contextCarrier = {}
-        local span1 = Span.createExitSpan("operation_name", context, nil, '127.0.0.1:80', contextCarrier)
+        local span1 = Span.createExitSpan("operation_name", context, nil)
         lu.assertNotNil(span1)
         lu.assertEquals(span1.is_entry, false)
         lu.assertEquals(span1.is_exit, true)
         lu.assertEquals(span1.layer, SpanLayer.NONE)
-        lu.assertEquals(span1.peer, '127.0.0.1:80')
-
-        lu.assertEquals(#(context.internal.active_spans), 1)
-        lu.assertNotNil(contextCarrier['sw8'])
     end
 
     function TestSpan:testNew()
diff --git a/lib/skywalking/tracer.lua b/lib/skywalking/tracer.lua
index 8f62a67..cc08e6f 100644
--- a/lib/skywalking/tracer.lua
+++ b/lib/skywalking/tracer.lua
@@ -29,8 +29,6 @@
 
 
 function Tracer:start(upstream_name, correlation)
-    local log = ngx.log
-    local WARN = ngx.WARN
     local serviceName = metadata_shdict:get("serviceName")
     local serviceInstanceName = metadata_shdict:get('serviceInstanceName')
     local req_uri = ngx.var.uri
@@ -67,19 +65,18 @@
     Span.tag(entrySpan, 'http.params',
              ngx.var.scheme .. '://' .. ngx.var.host .. ngx.var.request_uri )
 
+    ------------------------------------------------------
     contextCarrier = Util.tablepool_fetch("sw_contextCarrier")
     -- Use the same URI to represent incoming and forwarding requests
     -- Change it if you need.
-    local upstreamServerName = upstream_name
-    ------------------------------------------------------
-    local exitSpan = TC.createExitSpan(tracingContext, req_uri, entrySpan,
-                        upstreamServerName, contextCarrier, correlation)
+    local exitSpan = TC.createExitSpan(tracingContext, req_uri, entrySpan)
     Span.start(exitSpan, time_now)
     Span.setComponentId(exitSpan, nginxComponentId)
     Span.setLayer(exitSpan, Layer.HTTP)
 
-    for name, value in pairs(contextCarrier) do
-        ngx.req.set_header(name, value)
+    local upstreamServerName = upstream_name
+    if upstreamServerName then
+        TC.inject(tracingContext, exitSpan, upstreamServerName, correlation)
     end
 
     -- Push the data in the context
@@ -90,6 +87,17 @@
     ctx.is_finished = false
 end
 
+-- inject an exit span context and correlation context into carrier
+-- since v1.0.0
+function Tracer:inject(exitSpan, peer, correlation)
+    local ctx = ngx.ctx
+    local context = ctx.tracingContext
+
+    if not context.is_noop and exitSpan ~= nil and not ctx.is_finished then
+        TC.inject(context, exitSpan, peer, correlation)
+    end
+end
+
 function Tracer:finish()
     -- Finish the exit span when received the first response package from upstream
     if ngx.ctx.exitSpan ~= nil and not ngx.ctx.is_finished then
diff --git a/lib/skywalking/tracing_context.lua b/lib/skywalking/tracing_context.lua
index 7c7374d..6d16209 100644
--- a/lib/skywalking/tracing_context.lua
+++ b/lib/skywalking/tracing_context.lua
@@ -17,8 +17,10 @@
 
 local Util = require('skywalking.util')
 local Span = require('skywalking.span')
+local SegmentRef = require('skywalking.segment_ref')
 local CorrelationContext = require('skywalking.correlation_context')
 
+local CONTEXT_CARRIER_KEY = 'sw8'
 local CONTEXT_CORRELATION_KEY = 'sw8-correlation'
 
 -------------- Internal Object-------------
@@ -140,22 +142,29 @@
 
 -- Delegate to Span.createExitSpan
 -- @param contextCarrier could be nil if don't need to inject any context to propagate
-function _M.createExitSpan(tracingContext, operationName, parent, peer, contextCarrier, correlation)
+function _M.createExitSpan(tracingContext, operationName, parent)
     if tracingContext.is_noop then
         return Span.newNoOP()
     end
 
-    if contextCarrier then
-        if correlation then
-            for name, value in pairs(correlation) do
-                CorrelationContext.put(tracingContext.correlation, name, value)
-            end
-        end
+    return Span.createExitSpan(operationName, tracingContext, parent)
+end
 
-        contextCarrier[CONTEXT_CORRELATION_KEY] = CorrelationContext.serialize(tracingContext.correlation)
+-- Inject an exit span context and correlation context into context carrier to propagate
+-- @param correlation is used to transport custom data to downstream service
+function _M.inject(tracingContext, exitSpan, peer, correlation)
+    Span.setPeer(exitSpan, peer)
+
+    local injectableRef = SegmentRef.createInjectableRef(tracingContext, exitSpan)
+    local correlationData = tracingContext.correlation
+    if correlation then
+        for name, value in pairs(correlation) do
+            CorrelationContext.put(correlationData, name, value)
+        end
     end
 
-    return Span.createExitSpan(operationName, tracingContext, parent, peer, contextCarrier)
+    ngx.req.set_header(CONTEXT_CARRIER_KEY, SegmentRef.serialize(injectableRef))
+    ngx.req.set_header(CONTEXT_CORRELATION_KEY, CorrelationContext.serialize(correlationData))
 end
 
 -- After all active spans finished, this segment will be treated as finished status.