Egress Implementation (#18)

* Egress Implementation

* Addressing Review Comments

* egress changes

* Added istio pilot support

* Added Doc for egress

* Addressing review comments

* Addressed Review comments

* update go.mod

* Addressing review comments

* addressing review comment

* unit test
diff --git a/bootstrap/bootstrap.go b/bootstrap/bootstrap.go
index 2574fdb..819929e 100644
--- a/bootstrap/bootstrap.go
+++ b/bootstrap/bootstrap.go
@@ -30,9 +30,12 @@
 	"github.com/go-mesh/mesher/resolver"
 
 	"github.com/go-chassis/go-chassis"
+	"github.com/go-chassis/go-chassis/core/handler"
 	chassisHandler "github.com/go-chassis/go-chassis/core/handler"
 	"github.com/go-chassis/go-chassis/core/lager"
 	"github.com/go-chassis/go-chassis/core/metadata"
+	"github.com/go-mesh/mesher/control"
+	"github.com/go-mesh/mesher/pkg/egress"
 	"github.com/go-mesh/mesher/pkg/metrics"
 	"github.com/go-mesh/mesher/pkg/runtime"
 	"github.com/go-mesh/openlogging"
@@ -64,6 +67,14 @@
 	} else {
 		lager.Logger.Infof("local service ports is [%v]", cmd.Configs.PortsMap)
 	}
+	err := egress.Init()
+	if err != nil {
+		return err
+	}
+
+	if err := control.Init(); err != nil {
+		return err
+	}
 
 	return nil
 
@@ -120,3 +131,18 @@
 	chassis.SetDefaultConsumerChains(consumerChainMap)
 	chassis.SetDefaultProviderChains(providerChainMap)
 }
+
+//InitEgressChain init the egress handler chain
+func InitEgressChain() error {
+	egresschain := strings.Join([]string{
+		handler.RatelimiterConsumer,
+		handler.BizkeeperConsumer,
+		handler.Transport,
+	}, ",")
+
+	egressChainMap := map[string]string{
+		common.ChainConsumerEgress: egresschain,
+	}
+
+	return handler.CreateChains(common.ConsumerEgress, egressChainMap)
+}
diff --git a/common/common.go b/common/common.go
index 6eb7cff..5c1ba00 100644
--- a/common/common.go
+++ b/common/common.go
@@ -52,4 +52,8 @@
 const (
 	ChainConsumerOutgoing = "outgoing"
 	ChainProviderIncoming = "incoming"
+	ChainConsumerEgress   = "egress"
 )
+
+//ConsumerEgress constant for egress
+const ConsumerEgress = "ConsumerEgress"
diff --git a/conf/egress.yaml b/conf/egress.yaml
new file mode 100644
index 0000000..de36a85
--- /dev/null
+++ b/conf/egress.yaml
@@ -0,0 +1,17 @@
+#egress:
+#  infra: cse  # pilot or cse
+#  address: http://istio-pilot.istio-system:15010
+#egressRule:
+#  google-ext:
+#    - hosts:
+#        - "google.com"
+#        - "*.yahoo.com"
+#      ports:
+#        - port: 80
+#          protocol: HTTP
+#  facebook-ext:
+#    - hosts:
+#        - "www.facebook.com"
+#      ports:
+#        - port: 80
+#          protocol: HTTP
diff --git a/config/config.go b/config/config.go
index db7ea4d..3062cb2 100644
--- a/config/config.go
+++ b/config/config.go
@@ -35,10 +35,12 @@
 
 //Constant for mesher conf file
 const (
-	ConfFile = "mesher.yaml"
+	ConfFile       = "mesher.yaml"
+	EgressConfFile = "egress.yaml"
 )
 
 var mesherConfig *MesherConfig
+var egressConfig *EgressConfig
 
 //GetConfig returns mesher config
 func GetConfig() *MesherConfig {
@@ -53,14 +55,19 @@
 	*mesherConfig = *nc
 }
 
+//GetEgressConfig returns Egress config
+func GetEgressConfig() *EgressConfig {
+	return egressConfig
+}
+
 //GetConfigFilePath returns config file path
-func GetConfigFilePath() (string, error) {
-	if cmd.Configs.ConfigFile == "" {
+func GetConfigFilePath(key string) (string, error) {
+	if cmd.Configs.ConfigFile == "" || key == EgressConfFile {
 		wd, err := fileutil.GetWorkDir()
 		if err != nil {
 			return "", err
 		}
-		return filepath.Join(wd, "conf", ConfFile), nil
+		return filepath.Join(wd, "conf", key), nil
 	}
 	return cmd.Configs.ConfigFile, nil
 }
@@ -85,12 +92,24 @@
 	if err != nil {
 		return err
 	}
-	return yaml.Unmarshal([]byte(contents), mesherConfig)
+	if err := yaml.Unmarshal([]byte(contents), mesherConfig); err != nil {
+		return err
+	}
+
+	egressConfig = &EgressConfig{}
+	egressContents, err := GetConfigContents(EgressConfFile)
+	if err != nil {
+		return err
+	}
+	if err := yaml.Unmarshal([]byte(egressContents), egressConfig); err != nil {
+		return err
+	}
+	return nil
 }
 
 //GetConfigContents returns config contents
 func GetConfigContents(key string) (string, error) {
-	f, err := GetConfigFilePath()
+	f, err := GetConfigFilePath(key)
 	if err != nil {
 		return "", err
 	}
@@ -113,10 +132,15 @@
 	}
 	b, err := ioutil.ReadFile(f)
 	if err != nil {
-		lager.Logger.Error("Can not read mesher.yaml: " + err.Error())
+		lager.Logger.Error("Can not read yaml file" + err.Error())
 		return ""
 	}
 	contents = string(b)
 	archaius.AddKeyValue(key, contents)
 	return contents
 }
+
+// GetEgressEndpoints returns the pilot address
+func GetEgressEndpoints() string {
+	return egressConfig.Egress.Address
+}
diff --git a/config/config_test.go b/config/config_test.go
index 7913229..140f57b 100644
--- a/config/config_test.go
+++ b/config/config_test.go
@@ -15,9 +15,10 @@
 )
 
 func TestGetConfigFilePath(t *testing.T) {
+	var key = "mesher.yaml"
 	cmd.Init()
-	f, _ := config.GetConfigFilePath()
-	assert.Contains(t, f, "mesher.yaml")
+	f, _ := config.GetConfigFilePath(key)
+	assert.Contains(t, f, key)
 }
 
 var file = []byte(`
diff --git a/config/egress.go b/config/egress.go
new file mode 100644
index 0000000..53b44fd
--- /dev/null
+++ b/config/egress.go
@@ -0,0 +1,42 @@
+/*
+ * 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 config
+
+//EgressConfig is the struct having info about egress rule destinations
+type EgressConfig struct {
+	Egress       Egress                   `yaml:"egress"`
+	Destinations map[string][]*EgressRule `yaml:"egressRule"`
+}
+
+// Egress define where rule comes from
+type Egress struct {
+	Infra   string `yaml:"infra"`
+	Address string `yaml:"address"`
+}
+
+//EgressRule has hosts and ports information
+type EgressRule struct {
+	Hosts []string      `yaml:"hosts"`
+	Ports []*EgressPort `yaml:"ports"`
+}
+
+//EgressPort protocol and the corresponding port
+type EgressPort struct {
+	Port     int32  `yaml:"port"`
+	Protocol string `yaml:"protocol"`
+}
diff --git a/control/istio/cache.go b/control/istio/cache.go
new file mode 100644
index 0000000..48ef996
--- /dev/null
+++ b/control/istio/cache.go
@@ -0,0 +1,9 @@
+package istio
+
+import "github.com/patrickmn/go-cache"
+
+//save configs
+var (
+	//key is service name
+	EgressConfigCache = cache.New(0, 0)
+)
diff --git a/control/istio/panel.go b/control/istio/panel.go
new file mode 100644
index 0000000..72f849c
--- /dev/null
+++ b/control/istio/panel.go
@@ -0,0 +1,55 @@
+package istio
+
+import (
+	"github.com/go-chassis/go-chassis/control"
+	"github.com/go-chassis/go-chassis/core/config/model"
+	"github.com/go-chassis/go-chassis/core/invocation"
+	"github.com/go-chassis/go-chassis/third_party/forked/afex/hystrix-go/hystrix"
+	meshercontrol "github.com/go-mesh/mesher/control"
+	"github.com/go-mesh/mesher/pkg/egress"
+)
+
+func init() {
+	meshercontrol.InstallPlugin("pilot", newPilotPanel)
+}
+
+//PilotPanel pull configs from istio pilot
+type PilotPanel struct {
+}
+
+func newPilotPanel(options meshercontrol.Options) control.Panel {
+	SaveToEgressCache(egress.DefaultEgress.FetchEgressRule())
+	return &PilotPanel{}
+}
+
+//GetEgressRule get egress config
+func (p *PilotPanel) GetEgressRule() []control.EgressConfig {
+	c, ok := EgressConfigCache.Get("")
+	if !ok {
+
+		return nil
+	}
+	return c.([]control.EgressConfig)
+}
+
+//GetCircuitBreaker return command , and circuit breaker settings
+func (p *PilotPanel) GetCircuitBreaker(inv invocation.Invocation, serviceType string) (string, hystrix.CommandConfig) {
+	return "", hystrix.CommandConfig{}
+
+}
+
+//GetLoadBalancing get load balancing config
+func (p *PilotPanel) GetLoadBalancing(inv invocation.Invocation) control.LoadBalancingConfig {
+	return control.LoadBalancingConfig{}
+
+}
+
+//GetRateLimiting get rate limiting config
+func (p *PilotPanel) GetRateLimiting(inv invocation.Invocation, serviceType string) control.RateLimitingConfig {
+	return control.RateLimitingConfig{}
+}
+
+//GetFaultInjection get Fault injection config
+func (p *PilotPanel) GetFaultInjection(inv invocation.Invocation) model.Fault {
+	return model.Fault{}
+}
diff --git a/control/istio/transfer.go b/control/istio/transfer.go
new file mode 100644
index 0000000..064e149
--- /dev/null
+++ b/control/istio/transfer.go
@@ -0,0 +1,32 @@
+package istio
+
+import (
+	"github.com/go-chassis/go-chassis/control"
+	"github.com/go-mesh/mesher/config"
+)
+
+//SaveToEgressCache save the egress rules in the cache
+func SaveToEgressCache(egressConfigFromPilot map[string][]*config.EgressRule) {
+	{
+		var egressconfig []control.EgressConfig
+		for _, v := range egressConfigFromPilot {
+			for _, v1 := range v {
+				var Ports []*control.EgressPort
+				for _, v2 := range v1.Ports {
+					p := control.EgressPort{
+						Port:     (*v2).Port,
+						Protocol: (*v2).Protocol,
+					}
+					Ports = append(Ports, &p)
+				}
+				c := control.EgressConfig{
+					Hosts: v1.Hosts,
+					Ports: Ports,
+				}
+
+				egressconfig = append(egressconfig, c)
+			}
+		}
+		EgressConfigCache.Set("", egressconfig, 0)
+	}
+}
diff --git a/control/panel.go b/control/panel.go
new file mode 100644
index 0000000..40e43d5
--- /dev/null
+++ b/control/panel.go
@@ -0,0 +1,40 @@
+package control
+
+import (
+	"fmt"
+	"github.com/go-chassis/go-chassis/control"
+	"github.com/go-chassis/go-chassis/core/config"
+)
+
+var panelPlugin = make(map[string]func(options Options) control.Panel)
+
+//DefaultPanelEgress get fetch config
+var DefaultPanelEgress control.Panel
+
+//InstallPlugin install implementation
+func InstallPlugin(name string, f func(options Options) control.Panel) {
+	panelPlugin[name] = f
+}
+
+//Options is options
+type Options struct {
+	Address string
+}
+
+//Init initialize DefaultPanel
+func Init() error {
+	infra := config.GlobalDefinition.Panel.Infra
+	if infra == "" || infra == "archaius" {
+		return nil
+	}
+
+	f, ok := panelPlugin[infra]
+	if !ok {
+		return fmt.Errorf("do not support [%s] panel", infra)
+	}
+
+	DefaultPanelEgress = f(Options{
+		Address: config.GlobalDefinition.Panel.Settings["address"],
+	})
+	return nil
+}
diff --git a/control/struct.go b/control/struct.go
new file mode 100644
index 0000000..3f11b4a
--- /dev/null
+++ b/control/struct.go
@@ -0,0 +1,13 @@
+package control
+
+//EgressConfig is a standardized model
+type EgressConfig struct {
+	Hosts []string
+	Ports []*EgressPort
+}
+
+//EgressPort protocol and the corresponding port
+type EgressPort struct {
+	Port     int32
+	Protocol string
+}
diff --git a/docs/egress.md b/docs/egress.md
new file mode 100644
index 0000000..5a4ce8a
--- /dev/null
+++ b/docs/egress.md
@@ -0,0 +1,43 @@
+# Egress
+## Introduction
+
+Mesher support Egress for your service, so that you can access any publicly accessible service from your microservice.
+ 
+## Configuration
+
+The egress related configurations is all in egress.yaml.
+
+**infra**
+> *(optional, string)* specifies from where the egress configuration need to be taken supports two values cse or pilot ,
+      cse means the egress configuration from egress.yaml file,
+      pilot means egress configuaration are taken from pilot of istio,
+      default is *cse*
+      
+**address**
+> *(optional, string)* The end point of pilot from which configuration need to be fetched.
+
+**hosts**
+> *(optional, []string)* host associated with external service, could be a DNS name with wildcard prefix
+
+**ports.port**
+> *(optional, int)* The port associated with the external service, default is *80*
+
+**ports.protocol**
+> *(optional, int)* The protocol associated with the external service,supports only http default is *HTTP*
+
+## example
+edit egress.yaml
+
+```yaml
+egress:
+  infra: cse  # pilot or cse
+  address: http://istio-pilot.istio-system:15010
+egressRule:
+  google-ext:
+    - hosts:
+        - "www.google.com"
+        - "*.yahoo.com"
+      ports:
+        - port: 80
+          protocol: HTTP
+```
diff --git a/go.mod b/go.mod
index 86cae6c..c3983db 100644
--- a/go.mod
+++ b/go.mod
@@ -5,7 +5,7 @@
 	github.com/emicklei/go-restful-swagger12 v0.0.0-20170926063155-7524189396c6 // indirect
 	github.com/envoyproxy/go-control-plane v0.6.0
 	github.com/ghodss/yaml v1.0.0 // indirect
-	github.com/go-chassis/go-archaius v0.0.0-20181108111652-ab19b4eae276
+	github.com/go-chassis/go-archaius v0.0.0-20181119064113-720d998498f3
 
 	github.com/go-chassis/go-cc-client v0.0.0-20181102101915-dea430061a34
 	github.com/go-chassis/go-chassis v1.1.3
diff --git a/mesher.go b/mesher.go
index 128aa25..93f41e3 100644
--- a/mesher.go
+++ b/mesher.go
@@ -16,6 +16,11 @@
 	_ "github.com/go-mesh/mesher/protocol/http"
 
 	"github.com/go-mesh/mesher/server"
+
+	_ "github.com/go-mesh/mesher/pkg/egress/archaius"
+	_ "github.com/go-mesh/mesher/pkg/egress/pilot"
+
+	_ "github.com/go-mesh/mesher/control/istio"
 )
 
 func main() {
diff --git a/pkg/egress/archaius/egress.go b/pkg/egress/archaius/egress.go
new file mode 100644
index 0000000..203830d
--- /dev/null
+++ b/pkg/egress/archaius/egress.go
@@ -0,0 +1,86 @@
+/*
+ * 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 archaius
+
+import (
+	"fmt"
+	"github.com/go-mesh/mesher/config"
+	"github.com/go-mesh/mesher/pkg/egress"
+	"sync"
+)
+
+//Egress is cse Egress service
+type Egress struct {
+}
+
+//FetchEgressRule return all rules
+func (r *Egress) FetchEgressRule() map[string][]*config.EgressRule {
+	return GetEgressRule()
+}
+
+//SetEgressRule set rules
+func (r *Egress) SetEgressRule(rr map[string][]*config.EgressRule) {
+	SetEgressRule(rr)
+}
+
+//Init init egress config
+func (r *Egress) Init(op egress.Options) error {
+	// the manager use dests to init, so must init after dests
+	if err := initEgressManager(); err != nil {
+		return err
+	}
+	return refresh()
+}
+
+// refresh all the egress config
+func refresh() error {
+	configs := config.GetEgressConfig()
+	ok, _ := egress.ValidateEgressRule(configs.Destinations)
+	if !ok {
+		err := fmt.Errorf("Egress rule type assertion fail, key: %s", configs.Destinations)
+		return err
+	}
+
+	dests = configs.Destinations
+	return nil
+}
+
+func newEgress() (egress.Egress, error) {
+	return &Egress{}, nil
+}
+
+var dests = make(map[string][]*config.EgressRule)
+var lock sync.RWMutex
+
+// GetEgressRule get egress rule
+func GetEgressRule() map[string][]*config.EgressRule {
+	lock.RLock()
+	defer lock.RUnlock()
+	return dests
+}
+
+// SetEgressRule set egress rule
+func SetEgressRule(rule map[string][]*config.EgressRule) {
+	lock.RLock()
+	defer lock.RUnlock()
+	dests = rule
+}
+
+func init() {
+	egress.InstallEgressService("cse", newEgress)
+}
diff --git a/pkg/egress/archaius/egress_manager.go b/pkg/egress/archaius/egress_manager.go
new file mode 100644
index 0000000..23d642d
--- /dev/null
+++ b/pkg/egress/archaius/egress_manager.go
@@ -0,0 +1,83 @@
+/*
+ * 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 archaius
+
+import (
+	"gopkg.in/yaml.v2"
+	"path/filepath"
+	"strings"
+
+	"github.com/go-chassis/go-archaius"
+	"github.com/go-chassis/go-archaius/core"
+	"github.com/go-chassis/go-archaius/sources/file-source"
+	"github.com/go-chassis/go-chassis/core/lager"
+	"github.com/go-chassis/go-chassis/pkg/util/fileutil"
+	"github.com/go-mesh/mesher/config"
+	"github.com/go-mesh/mesher/pkg/egress"
+)
+
+//EgressYaml egress yaml file name
+const EgressYaml = "egress.yaml"
+
+type egressRuleEventListener struct{}
+
+// update egress rule of a service
+func (r *egressRuleEventListener) Event(e *core.Event) {
+	if e == nil {
+		lager.Logger.Warn("Event pointer is nil", nil)
+		return
+	}
+	if !strings.Contains(e.Key, EgressYaml) {
+		return
+	}
+	v := archaius.Get(e.Key)
+	if v == nil {
+		lager.Logger.Infof("[%s] Error getting egress key", e.Key)
+		return
+	}
+
+	var egressconfig config.EgressConfig
+
+	if err := yaml.Unmarshal([]byte(v.([]byte)), &egressconfig); err != nil {
+		lager.Logger.Error("yaml unmarshal failed", nil)
+		return
+	}
+	var egressRules []*config.EgressRule
+
+	for key, value := range egressconfig.Destinations {
+		ok, _ := egress.ValidateEgressRule(map[string][]*config.EgressRule{key: value})
+		if !ok {
+			lager.Logger.Warn("Validating Egress Rule Failed")
+			return
+
+		}
+		egressRules = append(egressRules, value...)
+	}
+
+	SetEgressRule(map[string][]*config.EgressRule{e.Key: egressRules})
+	lager.Logger.Infof("Update [%s] egress rule SUCCESS", e.Key)
+}
+
+// initialize the config mgr and add several sources
+func initEgressManager() error {
+	egressListener := &egressRuleEventListener{}
+	archaius.AddFile(filepath.Join(fileutil.GetConfDir(), EgressYaml), archaius.WithFileHandler(filesource.Convert2configMap))
+	archaius.RegisterListener(egressListener, ".*")
+
+	return nil
+}
diff --git a/pkg/egress/egress.go b/pkg/egress/egress.go
new file mode 100644
index 0000000..a21ffa6
--- /dev/null
+++ b/pkg/egress/egress.go
@@ -0,0 +1,112 @@
+/*
+ * 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 egress
+
+import (
+	"errors"
+	"github.com/go-chassis/go-chassis/control"
+	"github.com/go-mesh/mesher/config"
+	meshercontrol "github.com/go-mesh/mesher/control"
+	"regexp"
+	"sync"
+)
+
+var lock sync.RWMutex
+
+var plainHosts = make(map[string]*config.EgressRule)
+var regexHosts = make(map[string]*config.EgressRule)
+
+//Egress return egress rule, you can also set custom egress rule
+type Egress interface {
+	Init(Options) error
+	SetEgressRule(map[string][]*config.EgressRule)
+	FetchEgressRule() map[string][]*config.EgressRule
+}
+
+// ErrNoExist means if there is no egress implementation
+var ErrNoExist = errors.New("Egress not exists")
+var egressServices = make(map[string]func() (Egress, error))
+
+// DefaultEgress is current egress implementation
+var DefaultEgress Egress
+
+// InstallEgressService install egress service for developer
+func InstallEgressService(name string, f func() (Egress, error)) {
+	egressServices[name] = f
+}
+
+//BuildEgress create a Egress
+func BuildEgress(name string) error {
+	f, ok := egressServices[name]
+	if !ok {
+		return ErrNoExist
+	}
+	r, err := f()
+	if err != nil {
+		return err
+	}
+	DefaultEgress = r
+	return nil
+}
+
+//Match Check Egress rule matches
+func Match(hostname string) (bool, *control.EgressConfig) {
+	var EgressRules []control.EgressConfig
+	if meshercontrol.DefaultPanelEgress != nil {
+		EgressRules = meshercontrol.DefaultPanelEgress.GetEgressRule()
+	} else {
+		mapEgressRules := DefaultEgress.FetchEgressRule()
+		for _, value := range mapEgressRules {
+			for _, rule := range value {
+				var Ports []*control.EgressPort
+				for _, port := range rule.Ports {
+					p := control.EgressPort{
+						Port:     (*port).Port,
+						Protocol: (*port).Protocol,
+					}
+					Ports = append(Ports, &p)
+				}
+				c := control.EgressConfig{
+					Hosts: rule.Hosts,
+					Ports: Ports,
+				}
+				EgressRules = append(EgressRules, c)
+			}
+		}
+	}
+
+	for _, egress := range EgressRules {
+
+		for _, host := range egress.Hosts {
+			// Check host length greater than 0 and does not
+			// start with *
+			if len(host) > 0 && string(host[0]) != "*" {
+				if host == hostname {
+					return true, &egress
+				}
+			} else if string(host[0]) == "*" {
+				substring := host[1:]
+				match, _ := regexp.MatchString(substring+"$", hostname)
+				if match == true {
+					return true, &egress
+				}
+			}
+		}
+	}
+	return false, nil
+}
diff --git a/pkg/egress/egress_config.go b/pkg/egress/egress_config.go
new file mode 100644
index 0000000..d231db7
--- /dev/null
+++ b/pkg/egress/egress_config.go
@@ -0,0 +1,166 @@
+/*
+ * 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 egress
+
+import (
+	"crypto/tls"
+	"errors"
+	"fmt"
+	"regexp"
+	"strings"
+
+	gochassisconfig "github.com/go-chassis/go-chassis/core/config"
+	"github.com/go-chassis/go-chassis/core/lager"
+	chassisTLS "github.com/go-chassis/go-chassis/core/tls"
+	"github.com/go-chassis/go-chassis/pkg/util/iputil"
+	"github.com/go-mesh/mesher/config"
+)
+
+const (
+	dns1123LabelMaxLength int    = 63
+	dns1123LabelFmt       string = "[a-zA-Z0-9]([-a-z-A-Z0-9]*[a-zA-Z0-9])?"
+	wildcardPrefix        string = "\\*|(\\*)?" + dns1123LabelFmt
+	DefaultEgressType            = "cse"
+	// EgressTLS defines tls prefix
+	EgressTLS = "egress"
+)
+
+var (
+	dns1123LabelRegexp   = regexp.MustCompile("^" + dns1123LabelFmt + "$")
+	wildcardPrefixRegexp = regexp.MustCompile("^" + wildcardPrefix + "$")
+)
+
+// Init initialize Egress config
+func Init() error {
+
+	// init dests
+	egressConfigFromFile := config.GetEgressConfig()
+	BuildEgress(GetEgressType(egressConfigFromFile.Egress))
+
+	op, err := getSpecifiedOptions()
+	if err != nil {
+		return fmt.Errorf("Egress options error: %v", err)
+	}
+	DefaultEgress.Init(op)
+	lager.Logger.Info("Egress init success")
+	return nil
+}
+
+//ValidateEgressRule validate the Egress rules of each service
+func ValidateEgressRule(rules map[string][]*config.EgressRule) (bool, error) {
+	for _, rule := range rules {
+		for _, egressrule := range rule {
+			if len(egressrule.Hosts) == 0 {
+				return false, errors.New("Egress rule should have atleast one host")
+			}
+			for _, host := range egressrule.Hosts {
+				err := ValidateHostName(host)
+				if err != nil {
+					return false, err
+				}
+			}
+		}
+
+	}
+	return true, nil
+}
+
+//ValidateHostName validates the host
+func ValidateHostName(host string) error {
+	if len(host) > 255 {
+		return fmt.Errorf("host name %q too long (max 255)", host)
+	}
+	if len(host) == 0 {
+		return fmt.Errorf("empty host name not allowed")
+	}
+
+	parts := strings.SplitN(host, ".", 2)
+	if !IsWildcardDNS1123Label(parts[0]) {
+		return fmt.Errorf("host name %q invalid (label %q invalid)", host, parts[0])
+	} else if len(parts) > 1 {
+		err := validateDNS1123Labels(parts[1])
+		return err
+	}
+
+	return nil
+
+}
+
+//IsWildcardDNS1123Label validate wild card label
+func IsWildcardDNS1123Label(value string) bool {
+	return len(value) <= dns1123LabelMaxLength && wildcardPrefixRegexp.MatchString(value)
+}
+
+//validateDNS1123Labels validate host
+func validateDNS1123Labels(host string) error {
+	for _, label := range strings.Split(host, ".") {
+		if !IsDNS1123Label(label) {
+			return fmt.Errorf("host name %q invalid (label %q invalid)", host, label)
+		}
+	}
+	return nil
+}
+
+//IsDNS1123Label validate label
+func IsDNS1123Label(value string) bool {
+	return len(value) <= dns1123LabelMaxLength && dns1123LabelRegexp.MatchString(value)
+}
+
+// Options defines how to init Egress and its fetcher
+type Options struct {
+	Endpoints  []string
+	EnableSSL  bool
+	TLSConfig  *tls.Config
+	Version    string
+	ConfigPath string
+
+	//TODO: need timeout for client
+	// TimeOut time.Duration
+}
+
+/*
+// ToPilotOptions translate options to client options
+func (o Options) ToPilotOptions() *client.PilotOptions {
+	return &client.PilotOptions{Endpoints: o.Endpoints}
+}
+*/
+func getSpecifiedOptions() (opts Options, err error) {
+	hosts, scheme, err := iputil.URIs2Hosts(strings.Split(config.GetEgressEndpoints(), ","))
+	if err != nil {
+		return
+	}
+	opts.Endpoints = hosts
+	// TODO: envoy api v1 or v2
+	opts.TLSConfig, err = chassisTLS.GetTLSConfig(scheme, EgressTLS)
+	if err != nil {
+		return
+	}
+	if opts.TLSConfig != nil {
+		opts.EnableSSL = true
+	}
+	opts.ConfigPath = gochassisconfig.GetServiceDiscoveryConfigPath()
+	return
+}
+
+// GetEgressType returns the type of egress
+func GetEgressType(egress config.Egress) string {
+	if egress.Infra != "" {
+		return egress.Infra
+	}
+	return DefaultEgressType
+}
diff --git a/pkg/egress/egress_config_test.go b/pkg/egress/egress_config_test.go
new file mode 100644
index 0000000..9dddb52
--- /dev/null
+++ b/pkg/egress/egress_config_test.go
@@ -0,0 +1,41 @@
+package egress_test
+
+import (
+	"fmt"
+	mesherconfig "github.com/go-mesh/mesher/config"
+	"github.com/go-mesh/mesher/pkg/egress"
+	"gopkg.in/yaml.v2"
+	"testing"
+)
+
+func TestValidateEgressRule(t *testing.T) {
+	var yamlContent = `---
+egress:
+  infra: cse # pilot or cse
+  address: http://istio-pilot.istio-system:15010
+egressRule:
+  google-ext:
+    - hosts:
+        - "www.google.com"
+        - "*.yahoo.com"
+      ports:
+        - port: 80
+          protocol: HTTP
+  facebook-ext:
+    - hosts:
+        - "www.facebook.com"
+      ports:
+        - port: 80
+          protocol: HTTP`
+
+	ss := mesherconfig.EgressConfig{}
+	err := yaml.Unmarshal([]byte(yamlContent), &ss)
+	if err != nil {
+		fmt.Println("unmarshal failed")
+	}
+
+	bool, err := egress.ValidateEgressRule(ss.Destinations)
+	if bool == false {
+		t.Errorf("Expected true but got false")
+	}
+}
diff --git a/pkg/egress/egress_test.go b/pkg/egress/egress_test.go
new file mode 100644
index 0000000..ff54e5c
--- /dev/null
+++ b/pkg/egress/egress_test.go
@@ -0,0 +1,109 @@
+package egress_test
+
+import (
+	"fmt"
+	"os"
+	"testing"
+
+	"github.com/go-chassis/go-chassis/control"
+	"github.com/go-chassis/go-chassis/core/config"
+	"github.com/go-chassis/go-chassis/core/lager"
+	"github.com/go-mesh/mesher/cmd"
+	mesherconfig "github.com/go-mesh/mesher/config"
+	_ "github.com/go-mesh/mesher/control/istio"
+	"github.com/go-mesh/mesher/pkg/egress"
+	"github.com/go-mesh/mesher/pkg/egress/archaius"
+	"gopkg.in/yaml.v2"
+)
+
+func BenchmarkMatch(b *testing.B) {
+	lager.Initialize("", "DEBUG", "",
+		"size", true, 1, 10, 7)
+
+	gopath := os.Getenv("GOPATH")
+	os.Setenv("CHASSIS_HOME", gopath+"/src/github.com/go-mesh/mesher")
+	cmd.Init()
+	config.Init()
+	mesherconfig.Init()
+	egress.Init()
+	control.Init()
+	var yamlContent = `---
+egress:
+  infra: cse # pilot or cse
+  address: http://istio-pilot.istio-system:15010
+egressRule:
+  google-ext:
+    - hosts:
+        - "www.google.com"
+        - "*.yahoo.com"
+      ports:
+        - port: 80
+          protocol: HTTP
+  facebook-ext:
+    - hosts:
+        - "www.facebook.com"
+      ports:
+        - port: 80
+          protocol: HTTP`
+
+	ss := mesherconfig.EgressConfig{}
+	err := yaml.Unmarshal([]byte(yamlContent), &ss)
+	if err != nil {
+		fmt.Println("unmarshal failed")
+	}
+	archaius.SetEgressRule(ss.Destinations)
+
+	myString := "www.google.com"
+	for i := 0; i < b.N; i++ {
+		egress.Match(myString)
+	}
+}
+
+func TestMatch(t *testing.T) {
+	lager.Initialize("", "DEBUG", "",
+		"size", true, 1, 10, 7)
+
+	gopath := os.Getenv("GOPATH")
+	os.Setenv("CHASSIS_HOME", gopath+"/src/github.com/go-mesh/mesher")
+	cmd.Init()
+	config.Init()
+	mesherconfig.Init()
+	egress.Init()
+	//control.Init()
+	var yamlContent = `---
+egress:
+  infra: cse # pilot or cse
+  address: http://istio-pilot.istio-system:15010
+egressRule:
+  google-ext:
+    - hosts:
+        - "www.google.com"
+        - "*.yahoo.com"
+      ports:
+        - port: 80
+          protocol: HTTP
+  facebook-ext:
+    - hosts:
+        - "www.facebook.com"
+      ports:
+        - port: 80
+          protocol: HTTP`
+
+	ss := mesherconfig.EgressConfig{}
+	err := yaml.Unmarshal([]byte(yamlContent), &ss)
+	if err != nil {
+		fmt.Println("unmarshal failed")
+	}
+	archaius.SetEgressRule(ss.Destinations)
+
+	myString := "www.google.com"
+	b, _ := egress.Match(myString)
+	if b == false {
+		t.Errorf("Expected true but got false")
+	}
+	myString = "*.yahoo.com"
+	b, _ = egress.Match(myString)
+	if b == false {
+		t.Errorf("Expected true but got false")
+	}
+}
diff --git a/pkg/egress/pilot/egress.go b/pkg/egress/pilot/egress.go
new file mode 100644
index 0000000..07c6201
--- /dev/null
+++ b/pkg/egress/pilot/egress.go
@@ -0,0 +1,71 @@
+package pilot
+
+import (
+	"fmt"
+	"github.com/go-mesh/mesher/config"
+	"github.com/go-mesh/mesher/pkg/egress"
+	"sync"
+)
+
+func init() { egress.InstallEgressService("pilot", newPilotEgress) }
+
+func newPilotEgress() (egress.Egress, error) { return &PilotEgress{}, nil }
+
+//PilotEgress is pilot egress service
+type PilotEgress struct{}
+
+//FetchEgressRule return all rules
+func (r *PilotEgress) FetchEgressRule() map[string][]*config.EgressRule {
+	return GetEgressRule()
+}
+
+//SetEgressRule set rules
+func (r *PilotEgress) SetEgressRule(rr map[string][]*config.EgressRule) {
+	SetEgressRule(rr)
+}
+
+//Init init egress config
+func (r *PilotEgress) Init(o egress.Options) error {
+	// the manager use dests to init, so must init after dests
+	if err := InitPilotFetcher(o); err != nil {
+		return err
+	}
+	return refresh()
+}
+
+// refresh all the egress config
+func refresh() error {
+	configs := pilotfetcher.GetConfigurations()
+
+	d := make(map[string][]*config.EgressRule)
+	for k, v := range configs {
+		rules, ok := v.([]*config.EgressRule)
+		if !ok {
+			err := fmt.Errorf("Egress rule type assertion fail, key: %s", k)
+			return err
+		}
+		d[k] = rules
+	}
+	ok, _ := egress.ValidateEgressRule(d)
+	if ok {
+		dests = d
+	}
+	return nil
+}
+
+var dests = make(map[string][]*config.EgressRule)
+var lock sync.RWMutex
+
+// GetEgressRule get egress rule
+func GetEgressRule() map[string][]*config.EgressRule {
+	lock.RLock()
+	defer lock.RUnlock()
+	return dests
+}
+
+// SetEgressRule set egress rule
+func SetEgressRule(rule map[string][]*config.EgressRule) {
+	lock.RLock()
+	defer lock.RUnlock()
+	dests = rule
+}
diff --git a/pkg/egress/pilot/pilotsource.go b/pkg/egress/pilot/pilotsource.go
new file mode 100644
index 0000000..5216b19
--- /dev/null
+++ b/pkg/egress/pilot/pilotsource.go
@@ -0,0 +1,299 @@
+package pilot
+
+import (
+	"errors"
+	"fmt"
+	"os"
+	"reflect"
+	"strconv"
+	"strings"
+	"sync"
+	"time"
+
+	"github.com/go-chassis/go-archaius/core"
+	cm "github.com/go-chassis/go-archaius/core/config-manager"
+	"github.com/go-chassis/go-archaius/core/event-system"
+	"github.com/go-chassis/go-chassis/core/lager"
+	"github.com/go-mesh/mesher/config"
+	"github.com/go-mesh/mesher/control/istio"
+	"github.com/go-mesh/mesher/pkg/egress"
+	istioinfra "github.com/go-mesh/mesher/pkg/infras/istio"
+)
+
+const egressPilotSourceName = "EgressPilotSource"
+const egressPilotSourcePriority = 8
+
+//OUTBOUND outbound
+const OUTBOUND = "outbound"
+
+//clusteroriginaldst cluster type
+const clusteroriginaldst = 4
+
+// DefaultPilotRefresh is default pilot refresh time
+// TODO: use stream instead
+var DefaultPilotRefresh = 10 * time.Second
+
+var pilotfetcher core.ConfigMgr
+var pilotChan = make(chan string, 10)
+
+func setChanForPilot(k string) bool {
+	select {
+	case pilotChan <- k:
+		return true
+	default:
+		return false
+	}
+}
+
+// InitPilotFetcher init the config mgr and add several sources
+func InitPilotFetcher(o egress.Options) error {
+	d := eventsystem.NewDispatcher()
+
+	// register and init pilot fetcher
+	d.RegisterListener(&pilotEventListener{}, ".*")
+	pilotfetcher = cm.NewConfigurationManager(d)
+
+	return addEgressPilotSource(o)
+}
+
+// addEgressPilotSource adds a config source to pilotfetcher
+func addEgressPilotSource(o egress.Options) error {
+	if pilotfetcher == nil {
+		return errors.New("pilotfetcher is nil, please init it first")
+	}
+
+	s, err := newPilotSource(o)
+	if err != nil {
+		return err
+	}
+	lager.Logger.Infof("New [%s] source success", s.GetSourceName())
+	return pilotfetcher.AddSource(s, s.GetPriority())
+}
+
+// pilotSource keeps the egress rule in istio
+type pilotSource struct {
+	refreshInverval time.Duration
+	fetcher         istioinfra.XdsClient
+
+	mu             sync.RWMutex
+	pmu            sync.RWMutex
+	Configurations map[string]interface{}
+	PortToService  map[string]string
+}
+
+func newPilotSource(o egress.Options) (*pilotSource, error) {
+	pilotAddr := o.Endpoints[0]
+	nodeInfo := getNodeInfo()
+	xdsClient, err := istioinfra.NewXdsClient(pilotAddr, nil, nodeInfo, o.ConfigPath)
+	if err != nil {
+		return nil, fmt.Errorf("connect to pilot failed: %v", err)
+	}
+
+	return &pilotSource{
+		// TODO: read from config
+		refreshInverval: DefaultPilotRefresh,
+		Configurations:  map[string]interface{}{},
+		PortToService:   map[string]string{},
+		fetcher:         *xdsClient,
+	}, nil
+}
+
+func getNodeInfo() *istioinfra.NodeInfo {
+	PodName := os.Getenv("POD_NAME")
+	PodNamespace := os.Getenv("POD_NAMESPACE")
+	InstanceIP := os.Getenv("INSTANCE_IP")
+
+	nodeInfo := &istioinfra.NodeInfo{
+		PodName:    PodName,
+		Namespace:  PodNamespace,
+		InstanceIP: InstanceIP,
+	}
+	return nodeInfo
+}
+
+func (r *pilotSource) GetSourceName() string { return egressPilotSourceName }
+func (r *pilotSource) GetPriority() int      { return egressPilotSourcePriority }
+func (r *pilotSource) Cleanup() error        { return nil }
+
+func (r *pilotSource) AddDimensionInfo(d string) (map[string]string, error)           { return nil, nil }
+func (r *pilotSource) GetConfigurationsByDI(d string) (map[string]interface{}, error) { return nil, nil }
+func (r *pilotSource) GetConfigurationByKeyAndDimensionInfo(key, d string) (interface{}, error) {
+	return nil, nil
+}
+
+func (r *pilotSource) GetConfigurations() (map[string]interface{}, error) {
+	egressConfigs, err := r.getEgressConfigFromPilot()
+	if err != nil {
+		lager.Logger.Error("Get egress config from pilot failed" + err.Error())
+		return nil, err
+	}
+	d := make(map[string]interface{}, 0)
+	d["pilotEgress"] = egressConfigs
+	r.mu.Lock()
+	r.Configurations = d
+	r.mu.Unlock()
+	return d, nil
+}
+
+func (r *pilotSource) GetConfigurationByKey(k string) (interface{}, error) {
+	r.mu.RLock()
+	defer r.mu.RUnlock()
+	if value, ok := r.Configurations[k]; ok {
+		return value, nil
+	}
+	return nil, fmt.Errorf("not found %s", k)
+}
+
+// get egress config from pilot
+func (r *pilotSource) getEgressConfigFromPilot() ([]*config.EgressRule, error) {
+	clusters, _ := r.fetcher.CDS()
+	var egressRules []*config.EgressRule
+	for _, cluster := range clusters {
+
+		if cluster.Type == clusteroriginaldst {
+			data := strings.Split(cluster.Name, "|")
+			if len(data) > 1 && data[0] == OUTBOUND {
+				intport, err := strconv.Atoi(data[1])
+				if err != nil {
+					return nil, nil
+				}
+				var rule config.EgressRule
+				rule.Hosts = []string{data[len(data)-1]}
+				rule.Ports = []*config.EgressPort{
+					{Port: int32(intport),
+						Protocol: "http"}}
+
+				egressRules = append(egressRules, &rule)
+			}
+
+		}
+
+	}
+
+	return egressRules, nil
+}
+
+func (r *pilotSource) setPortForDestination(service, port string) {
+	r.pmu.RLock()
+	r.PortToService[port] = service
+	r.pmu.RUnlock()
+}
+
+func (r *pilotSource) DynamicConfigHandler(callback core.DynamicConfigCallback) error {
+	// Periodically refresh configurations
+	ticker := time.NewTicker(r.refreshInverval)
+	for {
+		select {
+		case <-pilotChan:
+			data, err := r.GetConfigurations()
+			if err != nil {
+				lager.Logger.Error("pilot pull configuration error" + err.Error())
+				continue
+			}
+			for k, d := range data {
+				istio.SaveToEgressCache(map[string][]*config.EgressRule{k: d.([]*config.EgressRule)})
+			}
+
+		case <-ticker.C:
+			data, err := r.refreshConfigurations()
+			if err != nil {
+				lager.Logger.Error("pilot refresh configuration error" + err.Error())
+				continue
+			}
+			events, err := r.populateEvents(data)
+			if err != nil {
+				lager.Logger.Warnf("populate event error", err)
+				return err
+			}
+			//Generate OnEvent Callback based on the events created
+			lager.Logger.Debugf("event On receive %+v", events)
+			for _, event := range events {
+				callback.OnEvent(event)
+			}
+		}
+	}
+	return nil
+}
+
+func (r *pilotSource) refreshConfigurations() (map[string]interface{}, error) {
+	data := make(map[string]interface{}, 0)
+
+	egressConfigs, err := r.getEgressConfigFromPilot()
+	if err != nil {
+		lager.Logger.Error("Get egress config from pilot failed" + err.Error())
+		return nil, err
+	}
+
+	data["pilotEgress"] = egressConfigs
+	return data, nil
+}
+
+func (r *pilotSource) populateEvents(updates map[string]interface{}) ([]*core.Event, error) {
+	events := make([]*core.Event, 0)
+	new := make(map[string]interface{})
+
+	// generate create and update event
+	r.mu.RLock()
+	current := r.Configurations
+	r.mu.RUnlock()
+
+	for key, value := range updates {
+		new[key] = value
+		currentValue, ok := current[key]
+		if !ok { // if new configuration introduced
+			events = append(events, constructEvent(core.Create, key, value))
+		} else if !reflect.DeepEqual(currentValue, value) {
+			events = append(events, constructEvent(core.Update, key, value))
+		}
+	}
+	// generate delete event
+	for key, value := range current {
+		_, ok := new[key]
+		if !ok { // when old config not present in new config
+			events = append(events, constructEvent(core.Delete, key, value))
+		}
+	}
+
+	// update with latest config
+	r.mu.Lock()
+	r.Configurations = new
+	r.mu.Unlock()
+	return events, nil
+}
+
+func constructEvent(eventType string, key string, value interface{}) *core.Event {
+	return &core.Event{
+		EventType:   eventType,
+		EventSource: egressPilotSourceName,
+		Value:       value,
+		Key:         key,
+	}
+}
+
+// pilotEventListener handle event dispatcher
+type pilotEventListener struct{}
+
+// update egress rule of a service
+func (r *pilotEventListener) Event(e *core.Event) {
+	if e == nil {
+		lager.Logger.Warn("pilot event pointer is nil", nil)
+		return
+	}
+
+	v := pilotfetcher.GetConfigurationsByKey(e.Key)
+	if v == nil {
+		istio.SaveToEgressCache(nil)
+		return
+	}
+	egressRules, ok := v.([]*config.EgressRule)
+	if !ok {
+		lager.Logger.Error("value of pilot is not type []*EgressRule", nil)
+		return
+	}
+
+	ok, _ = egress.ValidateEgressRule(map[string][]*config.EgressRule{e.Key: egressRules})
+	if ok {
+		istio.SaveToEgressCache(map[string][]*config.EgressRule{e.Key: egressRules})
+		lager.Logger.Infof("Update [%s] egress rule of pilot success", e.Key)
+	}
+}
diff --git a/protocol/http/reverse_proxy.go b/protocol/http/reverse_proxy.go
index ddc2814..2ec0a5e 100755
--- a/protocol/http/reverse_proxy.go
+++ b/protocol/http/reverse_proxy.go
@@ -39,10 +39,13 @@
 	"github.com/go-chassis/go-chassis/third_party/forked/afex/hystrix-go/hystrix"
 	"github.com/go-mesh/mesher/cmd"
 	"github.com/go-mesh/mesher/common"
+	"github.com/go-mesh/mesher/pkg/egress"
 	"github.com/go-mesh/mesher/pkg/metrics"
 	"github.com/go-mesh/mesher/protocol"
 	"github.com/go-mesh/mesher/resolver"
 	"github.com/go-mesh/openlogging"
+	"strconv"
+	"strings"
 )
 
 var dr = resolver.GetDestinationResolver("http")
@@ -113,11 +116,33 @@
 
 	//transfer header into ctx
 	inv.Ctx = context.WithValue(inv.Ctx, chassisCommon.ContextHeaderKey{}, h)
-	c, err := handler.GetChain(chassisCommon.Consumer, common.ChainConsumerOutgoing)
-	if err != nil {
-		handleErrorResponse(inv, w, http.StatusBadGateway, err)
-		lager.Logger.Error("Get chain failed: " + err.Error())
-		return
+
+	var c *handler.Chain
+	ok, egressRule := egress.Match(inv.MicroServiceName)
+	if ok {
+		var intport int32 = 80
+		for _, port := range egressRule.Ports {
+			if strings.EqualFold(port.Protocol, common.HTTPProtocol) {
+				intport = port.Port
+				break
+			}
+		}
+		inv.Endpoint = inv.MicroServiceName + ":" + strconv.Itoa(int(intport))
+		c, err = handler.GetChain(common.ConsumerEgress, common.ChainConsumerEgress)
+
+		if err != nil {
+			handleErrorResponse(inv, w, http.StatusBadGateway, err)
+			lager.Logger.Error("Get chain failed" + err.Error())
+			return
+		}
+
+	} else {
+		c, err = handler.GetChain(chassisCommon.Consumer, common.ChainConsumerOutgoing)
+		if err != nil {
+			handleErrorResponse(inv, w, http.StatusBadGateway, err)
+			lager.Logger.Error("Get chain failed: " + err.Error())
+			return
+		}
 	}
 	defer func(begin time.Time) {
 		timeTaken := time.Since(begin).Seconds()
@@ -229,6 +254,7 @@
 				handleErrorResponse(inv, w, ir.Status, ir.Err)
 			default: //for other error, check response and response body, if there is body, just transparent response
 				resp, ok := inv.Reply.(*http.Response)
+
 				if ok { // return raw transport error
 					if resp != nil {
 						if resp.Body == nil {
diff --git a/server/server.go b/server/server.go
index dfe63ff..1bee253 100644
--- a/server/server.go
+++ b/server/server.go
@@ -28,6 +28,11 @@
 		lager.Logger.Error("Go chassis init failed, Mesher is not available: " + err.Error())
 		panic(err)
 	}
+	if err := bootstrap.InitEgressChain(); err != nil {
+		lager.Logger.Errorf("Egress chain int failed: %s", err)
+		panic(err)
+	}
+
 	if err := bootstrap.Start(); err != nil {
 		lager.Logger.Error("Bootstrap failed: " + err.Error())
 		panic(err)