2121import static org .mockito .Mockito .any ;
2222import static org .mockito .Mockito .anyInt ;
2323import static org .mockito .Mockito .anyString ;
24+ import static org .mockito .Mockito .eq ;
2425import static org .mockito .Mockito .mock ;
2526import static org .mockito .Mockito .spy ;
2627import static org .mockito .Mockito .times ;
2728import static org .mockito .Mockito .verify ;
2829import static org .mockito .Mockito .when ;
2930import java .io .IOException ;
31+ import java .util .Base64 ;
32+ import java .util .HashMap ;
3033import java .util .List ;
34+ import java .util .Map ;
3135import java .util .concurrent .CompletableFuture ;
3236import java .util .concurrent .TimeUnit ;
3337import java .util .function .Function ;
3438import javax .servlet .http .HttpServletRequest ;
39+ import javax .servlet .http .HttpServletResponse ;
3540import org .apache .pulsar .client .api .Message ;
3641import org .apache .pulsar .client .api .MessageId ;
3742import org .apache .pulsar .client .api .PulsarClient ;
4348import org .apache .pulsar .client .impl .MultiTopicsConsumerImpl ;
4449import org .apache .pulsar .client .impl .MultiTopicsReaderImpl ;
4550import org .apache .pulsar .client .impl .ReaderImpl ;
51+ import org .apache .pulsar .common .api .proto .MessageIdData ;
4652import org .eclipse .jetty .ee8 .websocket .server .JettyServerUpgradeResponse ;
4753import org .testng .Assert ;
4854import org .testng .annotations .Test ;
4955
5056public class ReaderHandlerTest {
5157
58+ @ Test
59+ @ SuppressWarnings ("unchecked" )
60+ public void testInvalidMessageIdBase64ReturnsBadRequest () throws IOException {
61+ WebSocketService wss = mock (WebSocketService .class );
62+ PulsarClient mockedClient = mock (PulsarClient .class );
63+ when (wss .getPulsarClient ()).thenReturn (mockedClient );
64+ ReaderBuilder <byte []> mockedReaderBuilder = mock (ReaderBuilder .class );
65+ when (mockedClient .newReader ()).thenReturn (mockedReaderBuilder );
66+ when (mockedReaderBuilder .topic (any ())).thenReturn (mockedReaderBuilder );
67+ // Ensure the chain doesn't NPE after startMessageId() if parsing unexpectedly succeeds.
68+ when (mockedReaderBuilder .startMessageId (any ())).thenReturn (mockedReaderBuilder );
69+
70+ Map <String , String []> params = new HashMap <>();
71+ params .put ("messageId" , new String [] { "invalidMessageId" });
72+
73+ HttpServletRequest request = mock (HttpServletRequest .class );
74+ when (request .getRequestURI ()).thenReturn ("/ws/v2/reader/persistent/my-property/my-ns/my-topic" );
75+ when (request .getParameterMap ()).thenReturn (params );
76+
77+ JettyServerUpgradeResponse servletUpgradeResponse = mock (JettyServerUpgradeResponse .class );
78+ new ReaderHandler (wss , request , servletUpgradeResponse );
79+
80+ verify (servletUpgradeResponse , times (1 ))
81+ .sendError (eq (HttpServletResponse .SC_BAD_REQUEST ), anyString ());
82+ }
83+
84+ @ Test
85+ @ SuppressWarnings ("unchecked" )
86+ public void testInvalidMessageIdBytesReturnsBadRequest () throws IOException {
87+ WebSocketService wss = mock (WebSocketService .class );
88+ PulsarClient mockedClient = mock (PulsarClient .class );
89+ when (wss .getPulsarClient ()).thenReturn (mockedClient );
90+ ReaderBuilder <byte []> mockedReaderBuilder = mock (ReaderBuilder .class );
91+ when (mockedClient .newReader ()).thenReturn (mockedReaderBuilder );
92+ when (mockedReaderBuilder .topic (any ())).thenReturn (mockedReaderBuilder );
93+ // Ensure the chain doesn't NPE after startMessageId() if parsing unexpectedly succeeds.
94+ when (mockedReaderBuilder .startMessageId (any ())).thenReturn (mockedReaderBuilder );
95+
96+ // "AQID" is valid Base64, but it doesn't decode into a valid Pulsar MessageId structure.
97+ Map <String , String []> params = new HashMap <>();
98+ params .put ("messageId" , new String [] { "AQID" });
99+
100+ HttpServletRequest request = mock (HttpServletRequest .class );
101+ when (request .getRequestURI ()).thenReturn ("/ws/v2/reader/persistent/my-property/my-ns/my-topic" );
102+ when (request .getParameterMap ()).thenReturn (params );
103+
104+ JettyServerUpgradeResponse servletUpgradeResponse = mock (JettyServerUpgradeResponse .class );
105+ new ReaderHandler (wss , request , servletUpgradeResponse );
106+
107+ verify (servletUpgradeResponse , times (1 ))
108+ .sendError (eq (HttpServletResponse .SC_BAD_REQUEST ), anyString ());
109+ }
110+
111+ @ Test
112+ @ SuppressWarnings ("unchecked" )
113+ public void testInvalidMessageIdRuntimeParseFailureReturnsBadRequest () throws IOException {
114+ WebSocketService wss = mock (WebSocketService .class );
115+ PulsarClient mockedClient = mock (PulsarClient .class );
116+ when (wss .getPulsarClient ()).thenReturn (mockedClient );
117+ ReaderBuilder <byte []> mockedReaderBuilder = mock (ReaderBuilder .class );
118+ when (mockedClient .newReader ()).thenReturn (mockedReaderBuilder );
119+ when (mockedReaderBuilder .topic (any ())).thenReturn (mockedReaderBuilder );
120+ // Ensure the chain doesn't NPE after startMessageId() if parsing unexpectedly succeeds.
121+ when (mockedReaderBuilder .startMessageId (any ())).thenReturn (mockedReaderBuilder );
122+
123+ MessageIdData invalidBatchMessageId = new MessageIdData ()
124+ .setLedgerId (1 )
125+ .setEntryId (2 )
126+ .setBatchIndex (0 )
127+ .setBatchSize (-1 );
128+ Map <String , String []> params = new HashMap <>();
129+ params .put ("messageId" , new String [] {
130+ Base64 .getEncoder ().encodeToString (invalidBatchMessageId .toByteArray ()) });
131+
132+ HttpServletRequest request = mock (HttpServletRequest .class );
133+ when (request .getRequestURI ()).thenReturn ("/ws/v2/reader/persistent/my-property/my-ns/my-topic" );
134+ when (request .getParameterMap ()).thenReturn (params );
135+
136+ JettyServerUpgradeResponse servletUpgradeResponse = mock (JettyServerUpgradeResponse .class );
137+ new ReaderHandler (wss , request , servletUpgradeResponse );
138+
139+ verify (servletUpgradeResponse , times (1 ))
140+ .sendError (eq (HttpServletResponse .SC_BAD_REQUEST ), anyString ());
141+ }
142+
52143 @ Test
53144 @ SuppressWarnings ("unchecked" )
54145 public void testCreateReaderImp () throws IOException {
@@ -68,7 +159,7 @@ public void testCreateReaderImp() throws IOException {
68159 when (consumerImp .getSubscription ()).thenReturn (subName );
69160 when (mockedReader .getConsumer ()).thenReturn (consumerImp );
70161 HttpServletRequest request = mock (HttpServletRequest .class );
71- when (request .getRequestURI ()).thenReturn ("/ws/v2/producer /persistent/my-property/my-ns/my-topic" );
162+ when (request .getRequestURI ()).thenReturn ("/ws/v2/reader /persistent/my-property/my-ns/my-topic" );
72163 // create reader handler
73164 JettyServerUpgradeResponse servletUpgradeResponse = mock (JettyServerUpgradeResponse .class );
74165 ReaderHandler readerHandler = new ReaderHandler (wss , request , servletUpgradeResponse );
@@ -97,7 +188,7 @@ public void testCreateMultipleTopicReaderImp() throws IOException {
97188 when (consumerImp .getSubscription ()).thenReturn (subName );
98189 when (mockedReader .getMultiTopicsConsumer ()).thenReturn (consumerImp );
99190 HttpServletRequest request = mock (HttpServletRequest .class );
100- when (request .getRequestURI ()).thenReturn ("/ws/v2/producer /persistent/my-property/my-ns/my-topic" );
191+ when (request .getRequestURI ()).thenReturn ("/ws/v2/reader /persistent/my-property/my-ns/my-topic" );
101192 // create reader handler
102193 JettyServerUpgradeResponse servletUpgradeResponse = mock (JettyServerUpgradeResponse .class );
103194 ReaderHandler readerHandler = new ReaderHandler (wss , request , servletUpgradeResponse );
@@ -122,7 +213,7 @@ public void testCreateIllegalReaderImp() throws IOException {
122213 IllegalReader illegalReader = new IllegalReader ();
123214 when (mockedReaderBuilder .create ()).thenReturn (illegalReader );
124215 HttpServletRequest request = mock (HttpServletRequest .class );
125- when (request .getRequestURI ()).thenReturn ("/ws/v2/producer /persistent/my-property/my-ns/my-topic" );
216+ when (request .getRequestURI ()).thenReturn ("/ws/v2/reader /persistent/my-property/my-ns/my-topic" );
126217 // create reader handler
127218 JettyServerUpgradeResponse servletUpgradeResponse = spy (JettyServerUpgradeResponse .class );
128219 new ReaderHandler (wss , request , servletUpgradeResponse );
0 commit comments