func parseRouteRequest(r *http.Request) (route.Route, *handlerError) { var request struct { Address string Key string Pickle bool Spool bool Type string Substring string Prefix string Regex string } if err := json.NewDecoder(r.Body).Decode(&request); err != nil { return nil, &handlerError{err, "Couldn't parse json", http.StatusBadRequest} } // use hard coded defaults for flush and reconn as specified in // readDestinations periodFlush := time.Duration(1000) * time.Millisecond periodReconn := time.Duration(10000) * time.Millisecond dest, err := destination.New("", "", "", request.Address, table.SpoolDir, request.Spool, request.Pickle, periodFlush, periodReconn) if err != nil { return nil, &handlerError{err, "unable to create destination", http.StatusBadRequest} } var ro route.Route var e error switch request.Type { case "sendAllMatch": ro, e = route.NewSendAllMatch(request.Key, request.Prefix, request.Substring, request.Regex, []*destination.Destination{dest}) case "sendFirstMatch": ro, e = route.NewSendFirstMatch(request.Key, request.Prefix, request.Substring, request.Regex, []*destination.Destination{dest}) default: return nil, &handlerError{nil, "unknown route type: " + request.Type, http.StatusBadRequest} } if e != nil { return nil, &handlerError{e, "could not create route", http.StatusBadRequest} } return ro, nil }
// we should read and apply all destinations at once, // or at least make sure we apply them to the global datastruct at once, // otherwise we can destabilize things / send wrong traffic, etc func readDestinations(s *toki.Scanner, table *tbl.Table, allowMatcher bool) (destinations []*destination.Destination, err error) { for t := s.Next(); t.Token != toki.EOF; { if t.Token == sep { t = s.Next() } var prefix, sub, regex, addr, spoolDir string var spool, pickle bool flush := 1000 reconn := 10000 spoolDir = table.SpoolDir if t.Token != word { return destinations, errors.New("addr not set for endpoint") } addr = string(t.Value) for t.Token != toki.EOF && t.Token != sep { t = s.Next() switch t.Token { case optPrefix: if t = s.Next(); t.Token != word { return destinations, errFmtAddRoute } prefix = string(t.Value) case optSub: if t = s.Next(); t.Token != word { return destinations, errFmtAddRoute } sub = string(t.Value) case optRegex: if t = s.Next(); t.Token != word { return destinations, errFmtAddRoute } regex = string(t.Value) case optFlush: if t = s.Next(); t.Token != num { return destinations, errFmtAddRoute } flush, err = strconv.Atoi(strings.TrimSpace(string(t.Value))) if err != nil { return destinations, err } case optReconn: if t = s.Next(); t.Token != num { return destinations, errFmtAddRoute } reconn, err = strconv.Atoi(strings.TrimSpace(string(t.Value))) if err != nil { return destinations, err } case optPickle: if t = s.Next(); t.Token != optTrue && t.Token != optFalse { return destinations, errFmtAddRoute } pickle, err = strconv.ParseBool(string(t.Value)) if err != nil { return destinations, fmt.Errorf("unrecognized pickle value '%s'", t) } case optSpool: if t = s.Next(); t.Token != optTrue && t.Token != optFalse { return destinations, errFmtAddRoute } spool, err = strconv.ParseBool(string(t.Value)) if err != nil { return destinations, fmt.Errorf("unrecognized spool value '%s'", t) } case toki.EOF: case sep: break default: return destinations, fmt.Errorf("unrecognized option '%s'", t) } } periodFlush := time.Duration(flush) * time.Millisecond periodReConn := time.Duration(reconn) * time.Millisecond if !allowMatcher && (prefix != "" || sub != "" || regex != "") { return destinations, fmt.Errorf("matching options (prefix, sub, and regex) not allowed for this route type") } dest, err := destination.New(prefix, sub, regex, addr, spoolDir, spool, pickle, periodFlush, periodReConn) if err != nil { return destinations, err } destinations = append(destinations, dest) } return destinations, nil }