github.com/btccom/go-micro/v2@v2.9.3/api/handler/event/event.go (about) 1 // Package event provides a handler which publishes an event 2 package event 3 4 import ( 5 "encoding/json" 6 "fmt" 7 "net/http" 8 "path" 9 "regexp" 10 "strings" 11 "time" 12 13 "github.com/google/uuid" 14 "github.com/btccom/go-micro/v2/api/handler" 15 proto "github.com/btccom/go-micro/v2/api/proto" 16 "github.com/btccom/go-micro/v2/util/ctx" 17 "github.com/oxtoacart/bpool" 18 ) 19 20 var ( 21 bufferPool = bpool.NewSizedBufferPool(1024, 8) 22 ) 23 24 type event struct { 25 opts handler.Options 26 } 27 28 var ( 29 Handler = "event" 30 versionRe = regexp.MustCompilePOSIX("^v[0-9]+$") 31 ) 32 33 func eventName(parts []string) string { 34 return strings.Join(parts, ".") 35 } 36 37 func evRoute(ns, p string) (string, string) { 38 p = path.Clean(p) 39 p = strings.TrimPrefix(p, "/") 40 41 if len(p) == 0 { 42 return ns, "event" 43 } 44 45 parts := strings.Split(p, "/") 46 47 // no path 48 if len(parts) == 0 { 49 // topic: namespace 50 // action: event 51 return strings.Trim(ns, "."), "event" 52 } 53 54 // Treat /v[0-9]+ as versioning 55 // /v1/foo/bar => topic: v1.foo action: bar 56 if len(parts) >= 2 && versionRe.Match([]byte(parts[0])) { 57 topic := ns + "." + strings.Join(parts[:2], ".") 58 action := eventName(parts[1:]) 59 return topic, action 60 } 61 62 // /foo => topic: ns.foo action: foo 63 // /foo/bar => topic: ns.foo action: bar 64 topic := ns + "." + strings.Join(parts[:1], ".") 65 action := eventName(parts[1:]) 66 67 return topic, action 68 } 69 70 func (e *event) ServeHTTP(w http.ResponseWriter, r *http.Request) { 71 bsize := handler.DefaultMaxRecvSize 72 if e.opts.MaxRecvSize > 0 { 73 bsize = e.opts.MaxRecvSize 74 } 75 76 r.Body = http.MaxBytesReader(w, r.Body, bsize) 77 78 // request to topic:event 79 // create event 80 // publish to topic 81 82 topic, action := evRoute(e.opts.Namespace, r.URL.Path) 83 84 // create event 85 ev := &proto.Event{ 86 Name: action, 87 // TODO: dedupe event 88 Id: fmt.Sprintf("%s-%s-%s", topic, action, uuid.New().String()), 89 Header: make(map[string]*proto.Pair), 90 Timestamp: time.Now().Unix(), 91 } 92 93 // set headers 94 for key, vals := range r.Header { 95 header, ok := ev.Header[key] 96 if !ok { 97 header = &proto.Pair{ 98 Key: key, 99 } 100 ev.Header[key] = header 101 } 102 header.Values = vals 103 } 104 105 // set body 106 if r.Method == "GET" { 107 bytes, _ := json.Marshal(r.URL.Query()) 108 ev.Data = string(bytes) 109 } else { 110 // Read body 111 buf := bufferPool.Get() 112 defer bufferPool.Put(buf) 113 if _, err := buf.ReadFrom(r.Body); err != nil { 114 http.Error(w, err.Error(), 500) 115 return 116 } 117 ev.Data = buf.String() 118 } 119 120 // get client 121 c := e.opts.Client 122 123 // create publication 124 p := c.NewMessage(topic, ev) 125 126 // publish event 127 if err := c.Publish(ctx.FromRequest(r), p); err != nil { 128 http.Error(w, err.Error(), 500) 129 return 130 } 131 } 132 133 func (e *event) String() string { 134 return "event" 135 } 136 137 func NewHandler(opts ...handler.Option) handler.Handler { 138 return &event{ 139 opts: handler.NewOptions(opts...), 140 } 141 }