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  }