@@ -3,17 +3,15 @@ package httptransport
33import  (
44	"context" 
55	"fmt" 
6- 	"net" 
76	"net/http" 
87	"time" 
98
109	"github.com/quay/clair/config" 
1110	"github.com/quay/zlog" 
12- 	othttp  "go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp" 
11+ 	"go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp" 
1312	"go.opentelemetry.io/otel" 
1413	"golang.org/x/sync/semaphore" 
1514
16- 	clairerror "github.com/quay/clair/v4/clair-error" 
1715	"github.com/quay/clair/v4/indexer" 
1816	"github.com/quay/clair/v4/matcher" 
1917	"github.com/quay/clair/v4/notifier" 
@@ -39,193 +37,85 @@ const (
3937	OpenAPIV1Path                 =  "/openapi/v1" 
4038)
4139
42- // Server is the primary http server Clair exposes its functionality on. 
43- type  Server  struct  {
44- 	// Server embeds a http.Server and http.ServeMux. 
45- 	// The http.Server will be configured with the ServeMux on successful 
46- 	// initialization. 
47- 	conf  config.Config 
48- 	* http.Server 
49- 	* http.ServeMux 
50- 	indexer   indexer.Service 
51- 	matcher   matcher.Service 
52- 	notifier  notifier.Service 
53- 	traceOpt  othttp.Option 
54- }
55- 
56- func  New (ctx  context.Context , conf  config.Config , indexer  indexer.Service , matcher  matcher.Service , notifier  notifier.Service ) (* Server , error ) {
57- 	serv  :=  & http.Server {
58- 		Addr : conf .HTTPListenAddr ,
59- 		// use the passed in global context as the base context for all http 
60- 		// requests handled by this server 
61- 		BaseContext : func (net.Listener ) context.Context  { return  ctx  },
62- 	}
40+ func  New (ctx  context.Context , conf  * config.Config , indexer  indexer.Service , matcher  matcher.Service , notifier  notifier.Service ) (http.Handler , error ) {
6341	mux  :=  http .NewServeMux ()
64- 	t  :=  & Server {
65- 		conf :     conf ,
66- 		Server :   serv ,
67- 		ServeMux : mux ,
68- 		indexer :  indexer ,
69- 		matcher :  matcher ,
70- 		notifier : notifier ,
71- 		traceOpt : othttp .WithTracerProvider (otel .GetTracerProvider ()),
72- 	}
42+ 	traceOpt  :=  otelhttp .WithTracerProvider (otel .GetTracerProvider ())
7343	ctx  =  zlog .ContextWithValues (ctx , "component" , "httptransport/New" )
7444
75- 	mux .Handle (OpenAPIV1Path , DiscoveryHandler (ctx , OpenAPIV1Path , t . traceOpt ))
45+ 	mux .Handle (OpenAPIV1Path , DiscoveryHandler (ctx , OpenAPIV1Path , traceOpt ))
7646	zlog .Info (ctx ).Str ("path" , OpenAPIV1Path ).Msg ("openapi discovery configured" )
7747
78- 	var  e  error 
79- 	switch  conf .Mode  {
80- 	case  config .ComboMode :
81- 		e  =  t .configureComboMode (ctx )
82- 		if  e  !=  nil  {
83- 			return  nil , e 
48+ 	// NOTE(hank) My brain always wants to rewrite constructions like the 
49+ 	// following as a switch, but this is actually cleaner as an "if" sequence. 
50+ 
51+ 	if  conf .Mode  ==  config .IndexerMode  ||  conf .Mode  ==  config .ComboMode  {
52+ 		if  indexer  ==  nil  {
53+ 			return  nil , fmt .Errorf ("mode %q requires an indexer service" , conf .Mode )
54+ 		}
55+ 		prefix  :=  indexerRoot  +  apiRoot 
56+ 
57+ 		v1 , err  :=  NewIndexerV1 (ctx , prefix , indexer , traceOpt )
58+ 		if  err  !=  nil  {
59+ 			return  nil , fmt .Errorf ("indexer configuration: %w" , err )
8460		}
85- 	case  config .IndexerMode :
86- 		e  =  t .configureIndexerMode (ctx )
87- 		if  e  !=  nil  {
88- 			return  nil , e 
61+ 		var  sem  * semaphore.Weighted 
62+ 		if  ct  :=  conf .Indexer .IndexReportRequestConcurrency ; ct  >  0  {
63+ 			sem  =  semaphore .NewWeighted (int64 (ct ))
8964		}
90- 	case  config .MatcherMode :
91- 		e  =  t .configureMatcherMode (ctx )
92- 		if  e  !=  nil  {
93- 			return  nil , e 
65+ 		rl  :=  & limitHandler {
66+ 			Check : func (r  * http.Request ) (* semaphore.Weighted , string ) {
67+ 				if  r .Method  !=  http .MethodPost  &&  r .URL .Path  !=  IndexAPIPath  {
68+ 					return  nil , "" 
69+ 				}
70+ 				// Nil if the relevant config option isn't set. 
71+ 				return  sem , IndexAPIPath 
72+ 			},
73+ 			Next : v1 ,
9474		}
95- 	case  config .NotifierMode :
96- 		e  =  t .configureNotifierMode (ctx )
97- 		if  e  !=  nil  {
98- 			return  nil , e 
75+ 		mux .Handle (prefix , rl )
76+ 	}
77+ 	if  conf .Mode  ==  config .MatcherMode  ||  conf .Mode  ==  config .ComboMode  {
78+ 		if  indexer  ==  nil  ||  matcher  ==  nil  {
79+ 			return  nil , fmt .Errorf ("mode %q requires both an indexer service and a matcher service" , conf .Mode )
9980		}
81+ 		prefix  :=  matcherRoot  +  apiRoot 
82+ 		v1  :=  NewMatcherV1 (ctx , prefix , matcher , indexer , time .Duration (conf .Matcher .CacheAge ), traceOpt )
83+ 		mux .Handle (prefix , v1 )
10084	}
101- 
102- 	// attach HttpTransport to server, this works because we embed http.ServeMux 
103- 	t .Server .Handler  =  t 
104- 
85+ 	if  conf .Mode  ==  config .NotifierMode  ||  (conf .Mode  ==  config .ComboMode  &&  notifier  !=  nil ) {
86+ 		if  notifier  ==  nil  {
87+ 			return  nil , fmt .Errorf ("mode %q requires a notifier service" , conf .Mode )
88+ 		}
89+ 		prefix  :=  notifierRoot  +  apiRoot 
90+ 		v1 , err  :=  NewNotificationV1 (ctx , prefix , notifier , traceOpt )
91+ 		if  err  !=  nil  {
92+ 			return  nil , fmt .Errorf ("notifier configuration: %w" , err )
93+ 		}
94+ 		mux .Handle (prefix , v1 )
95+ 	}
96+ 	if  conf .Mode  ==  config .ComboMode  &&  notifier  ==  nil  {
97+ 		zlog .Debug (ctx ).Msg ("skipping unconfigured notifier" )
98+ 	}
99+ 	var  ret  http.Handler  =  mux 
105100	// Add endpoint authentication if configured to add auth. Must happen after 
106101	// mux was configured for given mode. 
107102	if  conf .Auth .Any () {
108- 		err  :=  t . configureWithAuth ( ctx )
103+ 		h ,  err  :=  authHandler ( conf ,  mux )
109104		if  err  !=  nil  {
110105			zlog .Warn (ctx ).
111106				Err (err ).
112107				Msg ("received error configuring auth middleware" )
108+ 			return  nil , err 
113109		}
110+ 		ret  =  h 
114111	}
115- 
116- 	return  t , nil 
117- }
118- 
119- // ConfigureComboMode configures the HttpTransport for ComboMode. 
120- // 
121- // This mode runs both Indexer and Matcher in a single process. 
122- func  (t  * Server ) configureComboMode (ctx  context.Context ) error  {
123- 	// requires both indexer and matcher services 
124- 	if  t .indexer  ==  nil  ||  t .matcher  ==  nil  {
125- 		return  clairerror.ErrNotInitialized {Msg : "Combo mode requires both indexer and macher services" }
126- 	}
127- 
128- 	err  :=  t .configureIndexerMode (ctx )
129- 	if  err  !=  nil  {
130- 		return  clairerror.ErrNotInitialized {Msg : "could not configure indexer: "  +  err .Error ()}
131- 	}
132- 
133- 	err  =  t .configureMatcherMode (ctx )
134- 	if  err  !=  nil  {
135- 		return  clairerror.ErrNotInitialized {Msg : "could not configure matcher: "  +  err .Error ()}
136- 	}
137- 
138- 	if  t .notifier  !=  nil  {
139- 		if  err  :=  t .configureNotifierMode (ctx ); err  !=  nil  {
140- 			return  clairerror.ErrNotInitialized {Msg : "could not configure notifier: "  +  err .Error ()}
141- 		}
142- 	}
143- 
144- 	return  nil 
145- }
146- 
147- // ConfigureIndexerMode configures the HttpTransport for IndexerMode. 
148- // 
149- // This mode runs only an Indexer in a single process. 
150- func  (t  * Server ) configureIndexerMode (ctx  context.Context ) error  {
151- 	// requires only indexer service 
152- 	if  t .indexer  ==  nil  {
153- 		return  clairerror.ErrNotInitialized {Msg : "IndexerMode requires an indexer service" }
154- 	}
155- 	prefix  :=  indexerRoot  +  apiRoot 
156- 
157- 	v1 , err  :=  NewIndexerV1 (ctx , prefix , t .indexer , t .traceOpt )
158- 	if  err  !=  nil  {
159- 		return  fmt .Errorf ("indexer configuration: %w" , err )
160- 	}
161- 	var  sem  * semaphore.Weighted 
162- 	if  ct  :=  t .conf .Indexer .IndexReportRequestConcurrency ; ct  >  0  {
163- 		sem  =  semaphore .NewWeighted (int64 (ct ))
164- 	}
165- 	rl  :=  & limitHandler {
166- 		Check : func (r  * http.Request ) (* semaphore.Weighted , string ) {
167- 			if  r .Method  !=  http .MethodPost  &&  r .URL .Path  !=  IndexAPIPath  {
168- 				return  nil , "" 
169- 			}
170- 			// Nil if the relevant config option isn't set. 
171- 			return  sem , IndexAPIPath 
172- 		},
173- 		Next : v1 ,
174- 	}
175- 	t .Handle (prefix , rl )
176- 	return  nil 
177- }
178- 
179- // ConfigureMatcherMode configures HttpTransport for MatcherMode. 
180- // 
181- // This mode runs only a Matcher in a single process. 
182- func  (t  * Server ) configureMatcherMode (ctx  context.Context ) error  {
183- 	// requires both an indexer and matcher service. indexer service 
184- 	// is assumed to be a remote call over the network 
185- 	if  t .indexer  ==  nil  ||  t .matcher  ==  nil  {
186- 		return  clairerror.ErrNotInitialized {Msg : "MatcherMode requires both indexer and matcher services" }
187- 	}
188- 	prefix  :=  matcherRoot  +  apiRoot 
189- 	v1  :=  NewMatcherV1 (ctx , prefix , t .matcher , t .indexer , time .Duration (t .conf .Matcher .CacheAge ), t .traceOpt )
190- 
191- 	t .Handle (prefix , v1 )
192- 	return  nil 
193- }
194- 
195- // ConfigureNotifierMode configures HttpTransport for NotifierMode. 
196- // 
197- // This mode runs only a Notifier in a single process. 
198- func  (t  * Server ) configureNotifierMode (ctx  context.Context ) error  {
199- 	if  t .notifier  ==  nil  {
200- 		return  clairerror.ErrNotInitialized {Msg : "NotifierMode requires a notifier service" }
201- 	}
202- 	prefix  :=  notifierRoot  +  apiRoot 
203- 	v1 , err  :=  NewNotificationV1 (ctx , prefix , t .notifier , t .traceOpt )
204- 	if  err  !=  nil  {
205- 		return  fmt .Errorf ("notifier configuration: %w" , err )
206- 	}
207- 
208- 	t .Handle (prefix , v1 )
209- 	return  nil 
112+ 	return  ret , nil 
210113}
211114
212115// IntraserviceIssuer is the issuer that will be used if Clair is configured to 
213116// mint its own JWTs. 
214117const  IntraserviceIssuer  =  `clair-intraservice` 
215118
216- // ConfigureWithAuth will take the current serve mux and wrap it in an Auth 
217- // middleware handler. 
218- // 
219- // Must be ran after the config*Mode method of choice. 
220- func  (t  * Server ) configureWithAuth (_  context.Context ) error  {
221- 	h , err  :=  authHandler (& t .conf , t .Server .Handler )
222- 	if  err  !=  nil  {
223- 		return  err 
224- 	}
225- 	t .Server .Handler  =  h 
226- 	return  nil 
227- }
228- 
229119// Unmodified determines whether to return a conditional response. 
230120func  unmodified (r  * http.Request , v  string ) bool  {
231121	if  vs , ok  :=  r .Header ["If-None-Match" ]; ok  {
0 commit comments