github.com/awesome-flow/flow@v0.0.3-0.20190918184116-508d75d68a2c/pkg/corev1alpha1/actor/receiver_http.go (about) 1 package actor 2 3 import ( 4 "context" 5 "fmt" 6 "io/ioutil" 7 "net/http" 8 "sync" 9 "time" 10 11 core "github.com/awesome-flow/flow/pkg/corev1alpha1" 12 ) 13 14 const ( 15 ShutdownTimeout = 5 * time.Second 16 HttpMsgSendTimeout = 50 * time.Millisecond 17 ) 18 19 type codetext struct { 20 code int 21 text []byte 22 } 23 24 var MsgStatusToHttpResp = map[core.MsgStatus]codetext{ 25 core.MsgStatusDone: {http.StatusOK, []byte("OK")}, 26 core.MsgStatusPartialSend: {http.StatusConflict, []byte("Partial send")}, 27 core.MsgStatusInvalid: {http.StatusBadRequest, []byte("Invalid message")}, 28 core.MsgStatusFailed: {http.StatusInternalServerError, []byte("Failed to send")}, 29 core.MsgStatusTimedOut: {http.StatusGatewayTimeout, []byte("Timed out to send message")}, 30 core.MsgStatusUnroutable: {http.StatusNotAcceptable, []byte("Unknown destination")}, 31 core.MsgStatusThrottled: {http.StatusTooManyRequests, []byte("Message throttled")}, 32 } 33 34 type ReceiverHTTP struct { 35 name string 36 bind string 37 ctx *core.Context 38 queue chan *core.Message 39 httpsrv *http.Server 40 wg sync.WaitGroup 41 } 42 43 var _ core.Actor = (*ReceiverHTTP)(nil) 44 45 func NewReceiverHTTP(name string, ctx *core.Context, params core.Params) (core.Actor, error) { 46 bind, ok := params["bind"] 47 if !ok { 48 return nil, fmt.Errorf("http receiver is missing `bind` config") 49 } 50 51 r := &ReceiverHTTP{ 52 name: name, 53 ctx: ctx, 54 bind: bind.(string), 55 queue: make(chan *core.Message), 56 } 57 58 srvmx := http.NewServeMux() 59 srvmx.HandleFunc("/v1alpha1", r.handleReqV1alpha1) 60 61 srv := &http.Server{ 62 Addr: bind.(string), 63 Handler: srvmx, 64 } 65 66 r.httpsrv = srv 67 68 return r, nil 69 } 70 71 func (r *ReceiverHTTP) Name() string { 72 return r.name 73 } 74 75 func (r *ReceiverHTTP) Start() error { 76 go r.runsrv() 77 78 return nil 79 } 80 81 func (r *ReceiverHTTP) runsrv() { 82 r.wg.Add(1) 83 if err := r.httpsrv.ListenAndServe(); err != nil { 84 switch err { 85 case http.ErrServerClosed: 86 r.ctx.Logger().Trace("http receiver %q was successfully terminated", r.name) 87 default: 88 r.ctx.Logger().Fatal(err.Error()) 89 } 90 } 91 r.wg.Done() 92 } 93 94 func (r *ReceiverHTTP) Stop() error { 95 ctx, cancel := context.WithTimeout(context.Background(), ShutdownTimeout) 96 defer cancel() 97 close(r.queue) 98 err := r.httpsrv.Shutdown(ctx) 99 r.wg.Wait() 100 101 return err 102 } 103 104 func (r *ReceiverHTTP) Connect(nthreads int, peer core.Receiver) error { 105 for i := 0; i < nthreads; i++ { 106 r.wg.Add(1) 107 go func() { 108 for msg := range r.queue { 109 if err := peer.Receive(msg); err != nil { 110 msg.Complete(core.MsgStatusFailed) 111 r.ctx.Logger().Error(err.Error()) 112 } 113 } 114 r.wg.Done() 115 }() 116 } 117 118 return nil 119 } 120 121 func (r *ReceiverHTTP) Receive(msg *core.Message) error { 122 return fmt.Errorf("http receiver %q can not receive internal messages", r.name) 123 } 124 125 func (r *ReceiverHTTP) handleReqV1alpha1(rw http.ResponseWriter, req *http.Request) { 126 cl := req.ContentLength 127 if cl == 0 { 128 http.Error(rw, "zero size request, ignored", http.StatusBadRequest) 129 return 130 } 131 132 body, err := ioutil.ReadAll(req.Body) 133 defer req.Body.Close() 134 if err != nil { 135 r.ctx.Logger().Error(err.Error()) 136 http.Error(rw, "Bad request", http.StatusBadRequest) 137 } 138 139 msg := core.NewMessage(body) 140 for k, v := range req.URL.Query() { 141 msg.SetMeta(k, v[0]) 142 } 143 144 r.queue <- msg 145 146 select { 147 case s := <-msg.AwaitChan(): 148 resp, ok := MsgStatusToHttpResp[s] 149 if !ok { 150 resp = codetext{http.StatusTeapot, []byte("This should not happen")} 151 } 152 rw.WriteHeader(resp.code) 153 rw.Write(resp.text) 154 case <-time.After(HttpMsgSendTimeout): 155 rw.WriteHeader(http.StatusGatewayTimeout) 156 rw.Write([]byte("Timed out to send message")) 157 } 158 }