1616
1717package org .glassfish .jersey .client ;
1818
19+ import org .junit .jupiter .api .Assertions ;
1920import org .junit .jupiter .api .Test ;
2021
22+ import jakarta .ws .rs .Priorities ;
2123import jakarta .ws .rs .Produces ;
2224import jakarta .ws .rs .WebApplicationException ;
2325import jakarta .ws .rs .client .Client ;
2426import jakarta .ws .rs .client .ClientBuilder ;
2527import jakarta .ws .rs .client .ClientRequestContext ;
2628import jakarta .ws .rs .client .ClientRequestFilter ;
29+ import jakarta .ws .rs .client .ClientResponseContext ;
30+ import jakarta .ws .rs .client .ClientResponseFilter ;
31+ import jakarta .ws .rs .core .Application ;
2732import jakarta .ws .rs .core .GenericEntity ;
33+ import jakarta .ws .rs .core .Link ;
2834import jakarta .ws .rs .core .MediaType ;
2935import jakarta .ws .rs .core .MultivaluedMap ;
3036import jakarta .ws .rs .core .Response ;
37+ import jakarta .ws .rs .core .UriBuilder ;
38+ import jakarta .ws .rs .core .Variant ;
3139import jakarta .ws .rs .ext .MessageBodyWriter ;
40+ import jakarta .ws .rs .ext .ReaderInterceptor ;
41+ import jakarta .ws .rs .ext .ReaderInterceptorContext ;
42+ import jakarta .ws .rs .ext .RuntimeDelegate ;
3243import java .io .IOException ;
3344import java .io .OutputStream ;
3445import java .lang .annotation .Annotation ;
3748import java .nio .charset .StandardCharsets ;
3849import java .util .ArrayList ;
3950import java .util .Arrays ;
51+ import java .util .Collections ;
52+ import java .util .Iterator ;
4053import java .util .List ;
54+ import java .util .Map ;
55+ import java .util .concurrent .atomic .AtomicReference ;
4156
42- //import static java.nio.charset.StandardCharsets;
4357import static org .junit .jupiter .api .Assertions .assertEquals ;
4458
4559public class AbortTest {
4660 private static final String TEXT_CSV = "text/csv" ;
61+ private static final String TEXT_HEADER = "text/header" ;
4762 private static final String EXPECTED_CSV = "hello;goodbye\n salutations;farewell" ;
4863 private static final List <List <String >> CSV_LIST = Arrays .asList (
4964 Arrays .asList ("hello" , "goodbye" ),
5065 Arrays .asList ("salutations" , "farewell" )
5166 );
67+ private final String entity = "HI" ;
68+ private final String header = "CUSTOM_HEADER" ;
69+
5270
5371 @ Test
5472 void testAbortWithGenericEntity () {
@@ -77,7 +95,6 @@ public static class CsvWriter implements MessageBodyWriter<List<List<String>>> {
7795
7896 @ Override
7997 public boolean isWriteable (Class <?> type , Type genericType , Annotation [] annotations , MediaType mediaType ) {
80- System .out .println (genericType .getTypeName ());
8198 return List .class .isAssignableFrom (type ) && genericType instanceof ParameterizedType
8299 && ((ParameterizedType ) genericType ).getActualTypeArguments ()[0 ] instanceof ParameterizedType
83100 && String .class .equals (((ParameterizedType ) ((ParameterizedType ) genericType ).getActualTypeArguments ()[0 ])
@@ -99,4 +116,172 @@ public void writeTo(List<List<String>> csvList, Class<?> type, Type genericType,
99116 }
100117 }
101118
119+ @ Test
120+ void testAbortWithMBWWritingHeaders () {
121+ try (Response response = ClientBuilder .newClient ().register (new ClientRequestFilter () {
122+ @ Override
123+ public void filter (ClientRequestContext requestContext ) throws IOException {
124+ requestContext .abortWith (Response .ok (entity , TEXT_HEADER ).build ());
125+ }
126+ }).register (new MessageBodyWriter <String >() {
127+
128+ @ Override
129+ public boolean isWriteable (Class <?> type , Type genericType , Annotation [] annotations , MediaType mediaType ) {
130+ return mediaType .toString ().equals (TEXT_HEADER );
131+ }
132+
133+ @ Override
134+ public void writeTo (String s , Class <?> type , Type genericType , Annotation [] annotations , MediaType mediaType ,
135+ MultivaluedMap <String , Object > httpHeaders , OutputStream entityStream ) throws IOException ,
136+ WebApplicationException {
137+ httpHeaders .add (header , entity );
138+ entityStream .write (s .getBytes ());
139+ }
140+ }, Priorities .USER - 1 ).target ("http://localhost:8080" ).request ().get ()) {
141+ Assertions .assertEquals (entity , response .readEntity (String .class ));
142+ Assertions .assertEquals (entity , response .getHeaderString (header ));
143+ }
144+ }
145+
146+ @ Test
147+ void testInterceptorHeaderAdd () {
148+ final String header2 = "CUSTOM_HEADER_2" ;
149+
150+ try (Response response = ClientBuilder .newClient ().register (new ClientRequestFilter () {
151+ @ Override
152+ public void filter (ClientRequestContext requestContext ) throws IOException {
153+ requestContext .abortWith (Response .ok ().entity (entity ).build ());
154+ }
155+ }).register (new ReaderInterceptor () {
156+ @ Override
157+ public Object aroundReadFrom (ReaderInterceptorContext context ) throws IOException , WebApplicationException {
158+ MultivaluedMap <String , String > headers = context .getHeaders ();
159+ headers .put (header , Collections .singletonList (entity ));
160+ headers .add (header2 , entity );
161+ return context .proceed ();
162+ }
163+ })
164+ .target ("http://localhost:8080" ).request ().get ()) {
165+ Assertions .assertEquals (entity , response .readEntity (String .class ));
166+ Assertions .assertEquals (entity , response .getHeaderString (header ));
167+ Assertions .assertEquals (entity , response .getHeaderString (header2 ));
168+ }
169+ }
170+
171+ @ Test
172+ void testInterceptorHeaderIterate () {
173+ final AtomicReference <String > originalHeader = new AtomicReference <>();
174+
175+ try (Response response = ClientBuilder .newClient ().register (new ClientRequestFilter () {
176+ @ Override
177+ public void filter (ClientRequestContext requestContext ) throws IOException {
178+ requestContext .abortWith (Response .ok ().header (header , header ).entity (entity ).build ());
179+ }
180+ }).register (new ReaderInterceptor () {
181+ @ Override
182+ public Object aroundReadFrom (ReaderInterceptorContext context ) throws IOException , WebApplicationException {
183+ MultivaluedMap <String , String > headers = context .getHeaders ();
184+ Iterator <Map .Entry <String , List <String >>> it = headers .entrySet ().iterator ();
185+ while (it .hasNext ()) {
186+ Map .Entry <String , List <String >> next = it .next ();
187+ if (header .equals (next .getKey ())) {
188+ originalHeader .set (next .setValue (Collections .singletonList (entity )).get (0 ));
189+ }
190+ }
191+ return context .proceed ();
192+ }
193+ })
194+ .target ("http://localhost:8080" ).request ().get ()) {
195+ Assertions .assertEquals (entity , response .readEntity (String .class ));
196+ Assertions .assertEquals (entity , response .getHeaderString (header ));
197+ Assertions .assertEquals (header , originalHeader .get ());
198+ }
199+ }
200+
201+ @ Test
202+ void testNullHeader () {
203+ final AtomicReference <String > originalHeader = new AtomicReference <>();
204+ RuntimeDelegate .setInstance (new StringHeaderRuntimeDelegate (RuntimeDelegate .getInstance ()));
205+ try (Response response = ClientBuilder .newClient ().register (new ClientRequestFilter () {
206+ @ Override
207+ public void filter (ClientRequestContext requestContext ) throws IOException {
208+ requestContext .abortWith (Response .ok ()
209+ .header (header , new StringHeader ())
210+ .entity (entity ).build ());
211+ }
212+ }).register (new ClientResponseFilter () {
213+ @ Override
214+ public void filter (ClientRequestContext requestContext , ClientResponseContext responseContext )
215+ throws IOException {
216+ originalHeader .set (responseContext .getHeaderString (header ));
217+ }
218+ })
219+ .target ("http://localhost:8080" ).request ().get ()) {
220+ Assertions .assertEquals (entity , response .readEntity (String .class ));
221+ Assertions .assertEquals ("" , originalHeader .get ());
222+ }
223+ }
224+
225+ private static class StringHeader extends AtomicReference <String > {
226+
227+ }
228+
229+ private static class StringHeaderDelegate implements RuntimeDelegate .HeaderDelegate <StringHeader > {
230+ @ Override
231+ public StringHeader fromString (String value ) {
232+ StringHeader stringHeader = new StringHeader ();
233+ stringHeader .set (value );
234+ return stringHeader ;
235+ }
236+
237+ @ Override
238+ public String toString (StringHeader value ) {
239+ //on purpose
240+ return null ;
241+ }
242+ }
243+
244+ private static class StringHeaderRuntimeDelegate extends RuntimeDelegate {
245+ private final RuntimeDelegate original ;
246+
247+ private StringHeaderRuntimeDelegate (RuntimeDelegate original ) {
248+ this .original = original ;
249+ }
250+
251+ @ Override
252+ public UriBuilder createUriBuilder () {
253+ return original .createUriBuilder ();
254+ }
255+
256+ @ Override
257+ public Response .ResponseBuilder createResponseBuilder () {
258+ return original .createResponseBuilder ();
259+ }
260+
261+ @ Override
262+ public Variant .VariantListBuilder createVariantListBuilder () {
263+ return original .createVariantListBuilder ();
264+ }
265+
266+ @ Override
267+ public <T > T createEndpoint (Application application , Class <T > endpointType )
268+ throws IllegalArgumentException , UnsupportedOperationException {
269+ return original .createEndpoint (application , endpointType );
270+ }
271+
272+ @ Override
273+ @ SuppressWarnings ("unchecked" )
274+ public <T > HeaderDelegate <T > createHeaderDelegate (Class <T > type ) throws IllegalArgumentException {
275+ if (StringHeader .class .equals (type )) {
276+ return (HeaderDelegate <T >) new StringHeaderDelegate ();
277+ }
278+ return original .createHeaderDelegate (type );
279+ }
280+
281+ @ Override
282+ public Link .Builder createLinkBuilder () {
283+ return original .createLinkBuilder ();
284+ }
285+ }
286+
102287}
0 commit comments