bgpwtf/cccampix/irr: limit concurrency

Change-Id: I958322f33c86469f9c3e21d1bd962faede2a3fee
diff --git a/bgpwtf/cccampix/irr/provider/arin.go b/bgpwtf/cccampix/irr/provider/arin.go
index 1781198..458c9e2 100644
--- a/bgpwtf/cccampix/irr/provider/arin.go
+++ b/bgpwtf/cccampix/irr/provider/arin.go
@@ -12,21 +12,31 @@
 
 	"code.hackerspace.pl/hscloud/bgpwtf/cccampix/irr/whois"
 	pb "code.hackerspace.pl/hscloud/bgpwtf/cccampix/proto"
+	"google.golang.org/grpc/codes"
+	"google.golang.org/grpc/status"
 )
 
 const ARINWhois = "rr.arin.net:43"
 
 type arin struct {
+	sem chan struct{}
 }
 
-func NewARIN() Provider {
-	return &arin{}
+func NewARIN(limit int) Provider {
+	return &arin{
+		sem: make(chan struct{}, limit),
+	}
 }
 
-func (r *arin) Query(ctx context.Context, asn uint64) (*pb.IRRQueryResponse, error) {
+func (a *arin) Query(ctx context.Context, asn uint64) (*pb.IRRQueryResponse, error) {
+	a.sem <- struct{}{}
+	defer func() {
+		<-a.sem
+	}()
+
 	data, err := whois.Query(ctx, ARINWhois, fmt.Sprintf("AS%d", asn))
 	if err != nil {
-		return nil, fmt.Errorf("could not contact ARIN IRR: %v", err)
+		return nil, status.Errorf(codes.Unavailable, "could not contact ARIN IRR: %v", err)
 	}
 
 	lines := strings.Split(data, "\n")
@@ -53,14 +63,14 @@
 		if strings.HasPrefix(line, " ") {
 			// Continuation
 			if len(attrs) < 1 {
-				return nil, fmt.Errorf("unparseable IRR, continuation with no previous atribute name: %q", line)
+				return nil, status.Errorf(codes.Unavailable, "unparseable IRR, continuation with no previous atribute name: %q", line)
 			}
 
 			attrs[len(attrs)-1].value += " " + strings.TrimSpace(line)
 		} else {
 			parts := strings.SplitN(line, ":", 2)
 			if len(parts) != 2 {
-				return nil, fmt.Errorf("unparseable IRR, line with no attribute key: %q", line)
+				return nil, status.Errorf(codes.Unavailable, "unparseable IRR, line with no attribute key: %q", line)
 			}
 			name := strings.TrimSpace(parts[0])
 			value := strings.TrimSpace(parts[1])
@@ -71,6 +81,10 @@
 		}
 	}
 
+	if len(attrs) == 0 {
+		return nil, status.Errorf(codes.NotFound, "no such ASN")
+	}
+
 	return &pb.IRRQueryResponse{
 		Source:     pb.IRRQueryResponse_SOURCE_ARIN,
 		Attributes: parseAttributes(attrs),