@@ -133,3 +133,227 @@ func TestServer(t *testing.T) {
133133 t .Error ("received event is different" )
134134 }
135135}
136+
137+ // TestSubscriptionHeaderImmediateSend verifies that the subscription ID header
138+ // is sent immediately upon subscription, preventing the "got 0 headers" error.
139+ func TestSubscriptionHeaderImmediateSend (t * testing.T ) {
140+ grpcServerOptions := []grpc.ServerOption {}
141+ grpcServer := grpc .NewServer (grpcServerOptions ... )
142+ defer grpcServer .Stop ()
143+
144+ grpcEventServer := NewGRPCBroker (NewBrokerOptions ())
145+ pbv1 .RegisterCloudEventServiceServer (grpcServer , grpcEventServer )
146+
147+ svc := & testService {evts : make (map [string ]* cloudevents.Event )}
148+ grpcEventServer .RegisterService (context .Background (), dataType , svc )
149+
150+ ctx , cancel := context .WithCancel (context .Background ())
151+ defer cancel ()
152+
153+ lis , err := net .Listen ("tcp" , "127.0.0.1:0" )
154+ if err != nil {
155+ t .Fatalf ("failed to listen: %v" , err )
156+ }
157+ t .Cleanup (func () {
158+ grpcServer .GracefulStop ()
159+ _ = lis .Close ()
160+ })
161+
162+ go func () {
163+ if err := grpcServer .Serve (lis ); err != nil {
164+ t .Errorf ("failed to serve: %v" , err )
165+ }
166+ }()
167+
168+ grpcClientOptions := grpccli .NewGRPCOptions ()
169+ grpcClientOptions .Dialer = & grpccli.GRPCDialer {URL : lis .Addr ().String ()}
170+ agentOption := grpcv2 .NewAgentOptions (grpcClientOptions , "cluster1" , "agent1" , dataType )
171+
172+ // Test that connection and subscription work without "got 0 headers" error
173+ if err := agentOption .CloudEventsTransport .Connect (ctx ); err != nil {
174+ t .Fatalf ("failed to connect: %v" , err )
175+ }
176+
177+ if err := agentOption .CloudEventsTransport .Subscribe (ctx ); err != nil {
178+ t .Fatalf ("failed to subscribe, expected header to be sent immediately: %v" , err )
179+ }
180+ }
181+
182+ // TestReconnectionScenario simulates a client restart (disconnect and reconnect)
183+ // to verify that subscription headers are properly sent on reconnection.
184+ func TestReconnectionScenario (t * testing.T ) {
185+ grpcServerOptions := []grpc.ServerOption {}
186+ grpcServer := grpc .NewServer (grpcServerOptions ... )
187+ defer grpcServer .Stop ()
188+
189+ grpcEventServer := NewGRPCBroker (NewBrokerOptions ())
190+ pbv1 .RegisterCloudEventServiceServer (grpcServer , grpcEventServer )
191+
192+ svc := & testService {evts : make (map [string ]* cloudevents.Event )}
193+ grpcEventServer .RegisterService (context .Background (), dataType , svc )
194+
195+ ctx , cancel := context .WithCancel (context .Background ())
196+ defer cancel ()
197+
198+ lis , err := net .Listen ("tcp" , "127.0.0.1:0" )
199+ if err != nil {
200+ t .Fatalf ("failed to listen: %v" , err )
201+ }
202+ t .Cleanup (func () {
203+ grpcServer .GracefulStop ()
204+ _ = lis .Close ()
205+ })
206+
207+ go func () {
208+ if err := grpcServer .Serve (lis ); err != nil {
209+ t .Errorf ("failed to serve: %v" , err )
210+ }
211+ }()
212+
213+ grpcClientOptions := grpccli .NewGRPCOptions ()
214+ grpcClientOptions .Dialer = & grpccli.GRPCDialer {URL : lis .Addr ().String ()}
215+
216+ // First connection and subscription
217+ agentOption := grpcv2 .NewAgentOptions (grpcClientOptions , "cluster1" , "agent1" , dataType )
218+ if err := agentOption .CloudEventsTransport .Connect (ctx ); err != nil {
219+ t .Fatalf ("failed to connect: %v" , err )
220+ }
221+
222+ if err := agentOption .CloudEventsTransport .Subscribe (ctx ); err != nil {
223+ t .Fatalf ("failed to subscribe on first connection: %v" , err )
224+ }
225+
226+ // Simulate client restart by closing and reconnecting
227+ if err := agentOption .CloudEventsTransport .Close (ctx ); err != nil {
228+ t .Fatalf ("failed to close: %v" , err )
229+ }
230+
231+ // Create a new transport for reconnection
232+ grpcClientOptions2 := grpccli .NewGRPCOptions ()
233+ grpcClientOptions2 .Dialer = & grpccli.GRPCDialer {URL : lis .Addr ().String ()}
234+ agentOption2 := grpcv2 .NewAgentOptions (grpcClientOptions2 , "cluster1" , "agent1" , dataType )
235+
236+ // Reconnect
237+ if err := agentOption2 .CloudEventsTransport .Connect (ctx ); err != nil {
238+ t .Fatalf ("failed to reconnect: %v" , err )
239+ }
240+
241+ // This should not fail with "got 0 headers" error
242+ if err := agentOption2 .CloudEventsTransport .Subscribe (ctx ); err != nil {
243+ t .Fatalf ("failed to subscribe after reconnection: %v" , err )
244+ }
245+ }
246+
247+ // TestConcurrentSubscriptions verifies that multiple clients can subscribe
248+ // simultaneously without header race conditions.
249+ func TestConcurrentSubscriptions (t * testing.T ) {
250+ grpcServerOptions := []grpc.ServerOption {}
251+ grpcServer := grpc .NewServer (grpcServerOptions ... )
252+ defer grpcServer .Stop ()
253+
254+ grpcEventServer := NewGRPCBroker (NewBrokerOptions ())
255+ pbv1 .RegisterCloudEventServiceServer (grpcServer , grpcEventServer )
256+
257+ svc := & testService {evts : make (map [string ]* cloudevents.Event )}
258+ grpcEventServer .RegisterService (context .Background (), dataType , svc )
259+
260+ ctx , cancel := context .WithCancel (context .Background ())
261+ defer cancel ()
262+
263+ lis , err := net .Listen ("tcp" , "127.0.0.1:0" )
264+ if err != nil {
265+ t .Fatalf ("failed to listen: %v" , err )
266+ }
267+ t .Cleanup (func () {
268+ grpcServer .GracefulStop ()
269+ _ = lis .Close ()
270+ })
271+
272+ go func () {
273+ if err := grpcServer .Serve (lis ); err != nil {
274+ t .Errorf ("failed to serve: %v" , err )
275+ }
276+ }()
277+
278+ // Create and subscribe multiple clients concurrently
279+ numClients := 10
280+ errCh := make (chan error , numClients )
281+
282+ for i := 0 ; i < numClients ; i ++ {
283+ go func (clientID int ) {
284+ grpcClientOptions := grpccli .NewGRPCOptions ()
285+ grpcClientOptions .Dialer = & grpccli.GRPCDialer {URL : lis .Addr ().String ()}
286+ agentOption := grpcv2 .NewAgentOptions (grpcClientOptions , "cluster1" , "agent1" , dataType )
287+
288+ if err := agentOption .CloudEventsTransport .Connect (ctx ); err != nil {
289+ errCh <- err
290+ return
291+ }
292+
293+ if err := agentOption .CloudEventsTransport .Subscribe (ctx ); err != nil {
294+ errCh <- err
295+ return
296+ }
297+
298+ errCh <- nil
299+ }(i )
300+ }
301+
302+ // Wait for all clients to complete
303+ for i := 0 ; i < numClients ; i ++ {
304+ if err := <- errCh ; err != nil {
305+ t .Errorf ("client %d failed: %v" , i , err )
306+ }
307+ }
308+ }
309+
310+ // TestMultipleRapidReconnections simulates rapid reconnection scenarios
311+ // that could trigger the race condition.
312+ func TestMultipleRapidReconnections (t * testing.T ) {
313+ grpcServerOptions := []grpc.ServerOption {}
314+ grpcServer := grpc .NewServer (grpcServerOptions ... )
315+ defer grpcServer .Stop ()
316+
317+ grpcEventServer := NewGRPCBroker (NewBrokerOptions ())
318+ pbv1 .RegisterCloudEventServiceServer (grpcServer , grpcEventServer )
319+
320+ svc := & testService {evts : make (map [string ]* cloudevents.Event )}
321+ grpcEventServer .RegisterService (context .Background (), dataType , svc )
322+
323+ ctx , cancel := context .WithCancel (context .Background ())
324+ defer cancel ()
325+
326+ lis , err := net .Listen ("tcp" , "127.0.0.1:0" )
327+ if err != nil {
328+ t .Fatalf ("failed to listen: %v" , err )
329+ }
330+ t .Cleanup (func () {
331+ grpcServer .GracefulStop ()
332+ _ = lis .Close ()
333+ })
334+
335+ go func () {
336+ if err := grpcServer .Serve (lis ); err != nil {
337+ t .Errorf ("failed to serve: %v" , err )
338+ }
339+ }()
340+
341+ // Perform multiple rapid reconnections
342+ for i := 0 ; i < 5 ; i ++ {
343+ grpcClientOptions := grpccli .NewGRPCOptions ()
344+ grpcClientOptions .Dialer = & grpccli.GRPCDialer {URL : lis .Addr ().String ()}
345+ agentOption := grpcv2 .NewAgentOptions (grpcClientOptions , "cluster1" , "agent1" , dataType )
346+
347+ if err := agentOption .CloudEventsTransport .Connect (ctx ); err != nil {
348+ t .Fatalf ("reconnection %d: failed to connect: %v" , i , err )
349+ }
350+
351+ if err := agentOption .CloudEventsTransport .Subscribe (ctx ); err != nil {
352+ t .Fatalf ("reconnection %d: failed to subscribe: %v" , i , err )
353+ }
354+
355+ if err := agentOption .CloudEventsTransport .Close (ctx ); err != nil {
356+ t .Fatalf ("reconnection %d: failed to close: %v" , i , err )
357+ }
358+ }
359+ }
0 commit comments