title: go 1.8 plugin use custom interface
body: <p>I want to use custom interface based on go plugin, but I found it's not support. </p>
  
  <h3>Definition of filter.Filter</h3>
  
  <pre><code>package filter
  
  import (
      "net/http"
  
      "github.com/valyala/fasthttp"
  )
  
  // Context filter context
  type Context interface {
      SetStartAt(startAt int64)
      SetEndAt(endAt int64)
      GetStartAt() int64
      GetEndAt() int64
  
      GetProxyServerAddr() string
      GetProxyOuterRequest() *fasthttp.Request
      GetProxyResponse() *fasthttp.Response
      NeedMerge() bool
  
      GetOriginRequestCtx() *fasthttp.RequestCtx
  
      GetMaxQPS() int
  
      ValidateProxyOuterRequest() bool
  
      InBlacklist(ip string) bool
      InWhitelist(ip string) bool
  
      IsCircuitOpen() bool
      IsCircuitHalf() bool
  
      GetOpenToCloseFailureRate() int
      GetHalfTrafficRate() int
      GetHalfToOpenSucceedRate() int
      GetOpenToCloseCollectSeconds() int
  
      ChangeCircuitStatusToClose()
      ChangeCircuitStatusToOpen()
  
      RecordMetricsForRequest()
      RecordMetricsForResponse()
      RecordMetricsForFailure()
      RecordMetricsForReject()
  
      GetRecentlyRequestSuccessedCount(sec int) int
      GetRecentlyRequestCount(sec int) int
      GetRecentlyRequestFailureCount(sec int) int
  }
  
  // Filter filter interface
  type Filter interface {
      Name() string
  
      Pre(c Context) (statusCode int, err error)
      Post(c Context) (statusCode int, err error)
      PostErr(c Context)
  }
  
  // BaseFilter base filter support default implemention
  type BaseFilter struct{}
  
  // Pre execute before proxy
  func (f BaseFilter) Pre(c Context) (statusCode int, err error) {
      return http.StatusOK, nil
  }
  
  // Post execute after proxy
  func (f BaseFilter) Post(c Context) (statusCode int, err error) {
      return http.StatusOK, nil
  }
  
  // PostErr execute proxy has errors
  func (f BaseFilter) PostErr(c Context) {
  
  }
  </code></pre>
  
  <p>This pkg is in my go app project.</p>
  
  <h3>load plugin file</h3>
  
  <pre><code>package proxy
  
  import (
      "errors"
      "plugin"
      "strings"
  
      "github.com/fagongzi/gateway/pkg/conf"
      "github.com/fagongzi/gateway/pkg/filter"
  )
  
  var (
      // ErrKnownFilter known filter error
      ErrKnownFilter = errors.New("unknow filter")
  )
  
  const (
      // FilterHTTPAccess access log filter
      FilterHTTPAccess = "HTTP-ACCESS"
      // FilterHeader header filter
      FilterHeader = "HEAD" // process header fiter
      // FilterXForward xforward fiter
      FilterXForward = "XFORWARD"
      // FilterBlackList blacklist filter
      FilterBlackList = "BLACKLIST"
      // FilterWhiteList whitelist filter
      FilterWhiteList = "WHITELIST"
      // FilterAnalysis analysis filter
      FilterAnalysis = "ANALYSIS"
      // FilterRateLimiting limit filter
      FilterRateLimiting = "RATE-LIMITING"
      // FilterCircuitBreake circuit breake filter
      FilterCircuitBreake = "CIRCUIT-BREAKE"
      // FilterValidation validation request filter
      FilterValidation = "VALIDATION"
  )
  
  func newFilter(filterSpec *conf.FilterSpec) (filter.Filter, error) {
      if filterSpec.External {
          return newExternalFilter(filterSpec)
      }
  
      input := strings.ToUpper(filterSpec.Name)
  
      switch input {
      case FilterHTTPAccess:
          return newAccessFilter(), nil
      case FilterHeader:
          return newHeadersFilter(), nil
      case FilterXForward:
          return newXForwardForFilter(), nil
      case FilterAnalysis:
          return newAnalysisFilter(), nil
      case FilterBlackList:
          return newBlackListFilter(), nil
      case FilterWhiteList:
          return newWhiteListFilter(), nil
      case FilterRateLimiting:
          return newRateLimitingFilter(), nil
      case FilterCircuitBreake:
          return newCircuitBreakeFilter(), nil
      case FilterValidation:
          return newValidationFilter(), nil
      default:
          return nil, ErrKnownFilter
      }
  }
  
  func newExternalFilter(filterSpec *conf.FilterSpec) (filter.Filter, error) {
      p, err := plugin.Open(filterSpec.ExternalPluginFile)
      if err != nil {
          return nil, err
      }
  
      s, err := p.Lookup("NewExternalFilter")
      if err != nil {
          return nil, err
      }
  
      sf := s.(func() (filter.Filter, error))
      return sf()
  }
  </code></pre>
  
  <p>This is the code of load plugin in my go app project</p>
  
  <pre><code>package main
  
  import (
      "C"
      "strings"
      "time"
  
      "github.com/CodisLabs/codis/pkg/utils/log"
      "github.com/fagongzi/gateway/pkg/filter"
      "github.com/valyala/fasthttp"
  )
  
  // AccessFilter record the http access log
  // log format: $remoteip "$method $path" $code "$agent" $svr $cost
  type AccessFilter struct {
  }
  
  // NewExternalFilter create a External filter
  func NewExternalFilter() (filter.Filter, error) {
      return &amp;AccessFilter{}, nil
  }
  
  // Name return name of this filter
  func (f *AccessFilter) Name() string {
      return "HTTP-ACCESS"
  }
  
  // Pre pre process
  func (f *AccessFilter) Pre(c filter.Context) (statusCode int, err error) {
      return 200, nil
  }
  
  // Post execute after proxy
  func (f *AccessFilter) Post(c filter.Context) (statusCode int, err error) {
      cost := (c.GetStartAt() - c.GetEndAt())
  
      log.Infof("%s %s \"%s\" %d \"%s\" %s %s",
          GetRealClientIP(c.GetOriginRequestCtx()),
          c.GetOriginRequestCtx().Method(),
          c.GetProxyOuterRequest().RequestURI(),
          c.GetProxyResponse().StatusCode(),
          c.GetOriginRequestCtx().UserAgent(),
          c.GetProxyServerAddr(),
          time.Duration(cost))
  
      return 200, nil
  }
  
  // PostErr post error process
  func (f *AccessFilter) PostErr(c filter.Context) {
  
  }
  
  // GetRealClientIP get read client ip
  func GetRealClientIP(ctx *fasthttp.RequestCtx) string {
      xforward := ctx.Request.Header.Peek("X-Forwarded-For")
      if nil == xforward {
          return strings.SplitN(ctx.RemoteAddr().String(), ":", 2)[0]
      }
  
      return strings.SplitN(string(xforward), ",", 2)[0]
  }
  </code></pre>
  
  <p>This is the definition of plugin, it's in my plugin project. The plugin project and go app project are different projects.</p>
  
  <h3>I found errors:</h3>
  
  <pre><code>panic: interface conversion: plugin.Symbol is func() (filter.Filter, error), not func() (filter.Filter, error)
  </code></pre>
  
  <p>You can find code in this project https://github.com/fagongzi/gateway/tree/go18-plugin-support.</p>
  
  <ol>
  <li><code>filter.Filter</code> is in pkg/filter package.</li>
  <li><code>load plugin file</code> in proxy/factory.go</li>
  <li><code>plugin go file</code> is in another project.</li>
  </ol>