13
13
import androidx .annotation .Nullable ;
14
14
import com .facebook .common .logging .FLog ;
15
15
import com .facebook .fbreact .specs .NativeNetworkingAndroidSpec ;
16
+ import com .facebook .infer .annotation .Assertions ;
17
+ import com .facebook .infer .annotation .Nullsafe ;
16
18
import com .facebook .react .bridge .Arguments ;
17
19
import com .facebook .react .bridge .ReactApplicationContext ;
18
20
import com .facebook .react .bridge .ReactMethod ;
48
50
import okio .Okio ;
49
51
50
52
/** Implements the XMLHttpRequest JavaScript interface. */
53
+ @ Nullsafe (Nullsafe .Mode .LOCAL )
51
54
@ ReactModule (name = NativeNetworkingAndroidSpec .NAME )
52
55
public final class NetworkingModule extends NativeNetworkingAndroidSpec {
53
56
@@ -69,7 +72,7 @@ public interface RequestBodyHandler {
69
72
boolean supports (ReadableMap map );
70
73
71
74
/** Returns the {@link RequestBody} for the JS body payload. */
72
- RequestBody toRequestBody (ReadableMap map , String contentType );
75
+ RequestBody toRequestBody (ReadableMap map , @ Nullable String contentType );
73
76
}
74
77
75
78
/** Allows adding custom handling to build the JS body payload from the {@link ResponseBody}. */
@@ -238,7 +241,7 @@ public void sendRequest(
238
241
String url ,
239
242
double requestIdAsDouble ,
240
243
ReadableArray headers ,
241
- ReadableMap data ,
244
+ @ Nullable ReadableMap data ,
242
245
String responseType ,
243
246
boolean useIncrementalUpdates ,
244
247
double timeoutAsDouble ,
@@ -272,7 +275,7 @@ public void sendRequestInternal(
272
275
String url ,
273
276
final int requestId ,
274
277
ReadableArray headers ,
275
- ReadableMap data ,
278
+ @ Nullable ReadableMap data ,
276
279
final String responseType ,
277
280
final boolean useIncrementalUpdates ,
278
281
int timeout ,
@@ -322,10 +325,11 @@ public void sendRequestInternal(
322
325
clientBuilder .addNetworkInterceptor (
323
326
chain -> {
324
327
Response originalResponse = chain .proceed (chain .request ());
328
+ ResponseBody originalResponseBody = originalResponse .body ();
329
+ Assertions .assertNotNull (originalResponseBody );
325
330
ProgressResponseBody responseBody =
326
331
new ProgressResponseBody (
327
- // NULLSAFE_FIXME[Parameter Not Nullable]
328
- originalResponse .body (),
332
+ originalResponseBody ,
329
333
new ProgressListener () {
330
334
long last = System .nanoTime ();
331
335
@@ -386,7 +390,6 @@ public void onProgress(long bytesWritten, long contentLength, boolean done) {
386
390
|| method .toLowerCase (Locale .ROOT ).equals ("head" )) {
387
391
requestBody = RequestBodyUtil .getEmptyBody (method );
388
392
} else if (handler != null ) {
389
- // NULLSAFE_FIXME[Parameter Not Nullable]
390
393
requestBody = handler .toRequestBody (data , contentType );
391
394
} else if (data .hasKey (REQUEST_BODY_KEY_STRING )) {
392
395
if (contentType == null ) {
@@ -400,8 +403,10 @@ public void onProgress(long bytesWritten, long contentLength, boolean done) {
400
403
String body = data .getString (REQUEST_BODY_KEY_STRING );
401
404
MediaType contentMediaType = MediaType .parse (contentType );
402
405
if (RequestBodyUtil .isGzipEncoding (contentEncoding )) {
403
- // NULLSAFE_FIXME[Parameter Not Nullable]
404
- requestBody = RequestBodyUtil .createGzip (contentMediaType , body );
406
+ requestBody = null ;
407
+ if (contentMediaType != null && body != null ) {
408
+ requestBody = RequestBodyUtil .createGzip (contentMediaType , body );
409
+ }
405
410
if (requestBody == null ) {
406
411
ResponseUtil .onRequestError (
407
412
reactApplicationContext , requestId , "Failed to gzip request body" , null );
@@ -415,7 +420,12 @@ public void onProgress(long bytesWritten, long contentLength, boolean done) {
415
420
contentMediaType == null
416
421
? StandardCharsets .UTF_8
417
422
: contentMediaType .charset (StandardCharsets .UTF_8 );
418
- // NULLSAFE_FIXME[Parameter Not Nullable, Nullable Dereference]
423
+ Assertions .assertNotNull (charset );
424
+ if (body == null ) {
425
+ ResponseUtil .onRequestError (
426
+ reactApplicationContext , requestId , "Received request but body was empty" , null );
427
+ return ;
428
+ }
419
429
requestBody = RequestBody .create (contentMediaType , body .getBytes (charset ));
420
430
}
421
431
} else if (data .hasKey (REQUEST_BODY_KEY_BASE64 )) {
@@ -428,9 +438,24 @@ public void onProgress(long bytesWritten, long contentLength, boolean done) {
428
438
return ;
429
439
}
430
440
String base64String = data .getString (REQUEST_BODY_KEY_BASE64 );
441
+ Assertions .assertNotNull (base64String );
442
+
431
443
MediaType contentMediaType = MediaType .parse (contentType );
432
- // NULLSAFE_FIXME[Parameter Not Nullable]
433
- requestBody = RequestBody .create (contentMediaType , ByteString .decodeBase64 (base64String ));
444
+ if (contentMediaType == null ) {
445
+ ResponseUtil .onRequestError (
446
+ reactApplicationContext ,
447
+ requestId ,
448
+ "Invalid content type specified: " + contentType ,
449
+ null );
450
+ return ;
451
+ }
452
+ ByteString base64DecodedString = ByteString .decodeBase64 (base64String );
453
+ if (base64DecodedString == null ) {
454
+ ResponseUtil .onRequestError (
455
+ reactApplicationContext , requestId , "Request body base64 string was invalid" , null );
456
+ return ;
457
+ }
458
+ requestBody = RequestBody .create (contentMediaType , base64DecodedString );
434
459
} else if (data .hasKey (REQUEST_BODY_KEY_URI )) {
435
460
if (contentType == null ) {
436
461
ResponseUtil .onRequestError (
@@ -441,8 +466,12 @@ public void onProgress(long bytesWritten, long contentLength, boolean done) {
441
466
return ;
442
467
}
443
468
String uri = data .getString (REQUEST_BODY_KEY_URI );
469
+ if (uri == null ) {
470
+ ResponseUtil .onRequestError (
471
+ reactApplicationContext , requestId , "Request body URI field was set but null" , null );
472
+ return ;
473
+ }
444
474
InputStream fileInputStream =
445
- // NULLSAFE_FIXME[Parameter Not Nullable]
446
475
RequestBodyUtil .getFileInputStream (getReactApplicationContext (), uri );
447
476
if (fileInputStream == null ) {
448
477
ResponseUtil .onRequestError (
@@ -455,8 +484,12 @@ public void onProgress(long bytesWritten, long contentLength, boolean done) {
455
484
contentType = "multipart/form-data" ;
456
485
}
457
486
ReadableArray parts = data .getArray (REQUEST_BODY_KEY_FORMDATA );
487
+ if (parts == null ) {
488
+ ResponseUtil .onRequestError (
489
+ reactApplicationContext , requestId , "Received request but form data was empty" , null );
490
+ return ;
491
+ }
458
492
MultipartBody .Builder multipartBuilder =
459
- // NULLSAFE_FIXME[Parameter Not Nullable]
460
493
constructMultipartBody (parts , contentType , requestId );
461
494
if (multipartBuilder == null ) {
462
495
return ;
@@ -467,7 +500,6 @@ public void onProgress(long bytesWritten, long contentLength, boolean done) {
467
500
requestBody = RequestBodyUtil .getEmptyBody (method );
468
501
}
469
502
470
- // NULLSAFE_FIXME[Parameter Not Nullable]
471
503
requestBuilder .method (method , wrapRequestBodyWithProgressEmitter (requestBody , requestId ));
472
504
473
505
addRequest (requestId );
@@ -518,8 +550,12 @@ public void onResponse(Call call, Response response) throws IOException {
518
550
// See
519
551
// https://github.com/square/okhttp/blob/5b37cda9e00626f43acf354df145fd452c3031f1/okhttp/src/main/java/okhttp3/internal/http/BridgeInterceptor.java#L76-L111
520
552
ResponseBody responseBody = response .body ();
521
- if ("gzip" .equalsIgnoreCase (response .header ("Content-Encoding" ))
522
- && responseBody != null ) {
553
+ if (responseBody == null ) {
554
+ ResponseUtil .onRequestError (
555
+ reactApplicationContext , requestId , "Response body is null" , null );
556
+ return ;
557
+ }
558
+ if ("gzip" .equalsIgnoreCase (response .header ("Content-Encoding" ))) {
523
559
GzipSource gzipSource = new GzipSource (responseBody .source ());
524
560
String contentType = response .header ("Content-Type" );
525
561
responseBody =
@@ -532,7 +568,6 @@ public void onResponse(Call call, Response response) throws IOException {
532
568
// Check if a handler is registered
533
569
for (ResponseHandler handler : mResponseHandlers ) {
534
570
if (handler .supports (responseType )) {
535
- // NULLSAFE_FIXME[Parameter Not Nullable]
536
571
WritableMap res = handler .toResponseData (responseBody );
537
572
ResponseUtil .onDataReceived (reactApplicationContext , requestId , res );
538
573
ResponseUtil .onRequestSuccess (reactApplicationContext , requestId );
@@ -544,7 +579,6 @@ public void onResponse(Call call, Response response) throws IOException {
544
579
// response,
545
580
// periodically send response data updates to JS.
546
581
if (useIncrementalUpdates && responseType .equals ("text" )) {
547
- // NULLSAFE_FIXME[Parameter Not Nullable]
548
582
readWithProgress (requestId , responseBody );
549
583
ResponseUtil .onRequestSuccess (reactApplicationContext , requestId );
550
584
return ;
@@ -554,7 +588,6 @@ public void onResponse(Call call, Response response) throws IOException {
554
588
String responseString = "" ;
555
589
if (responseType .equals ("text" )) {
556
590
try {
557
- // NULLSAFE_FIXME[Nullable Dereference]
558
591
responseString = responseBody .string ();
559
592
} catch (IOException e ) {
560
593
if (response .request ().method ().equalsIgnoreCase ("HEAD" )) {
@@ -569,7 +602,6 @@ public void onResponse(Call call, Response response) throws IOException {
569
602
}
570
603
}
571
604
} else if (responseType .equals ("base64" )) {
572
- // NULLSAFE_FIXME[Nullable Dereference]
573
605
responseString = Base64 .encodeToString (responseBody .bytes (), Base64 .NO_WRAP );
574
606
}
575
607
ResponseUtil .onDataReceived (reactApplicationContext , requestId , responseString );
@@ -582,10 +614,9 @@ public void onResponse(Call call, Response response) throws IOException {
582
614
});
583
615
}
584
616
585
- private RequestBody wrapRequestBodyWithProgressEmitter (
586
- final RequestBody requestBody , final int requestId ) {
617
+ private @ Nullable RequestBody wrapRequestBodyWithProgressEmitter (
618
+ @ Nullable final RequestBody requestBody , final int requestId ) {
587
619
if (requestBody == null ) {
588
- // NULLSAFE_FIXME[Return Not Nullable]
589
620
return null ;
590
621
}
591
622
final ReactApplicationContext reactApplicationContext =
@@ -622,8 +653,9 @@ private void readWithProgress(int requestId, ResponseBody responseBody) throws I
622
653
responseBody .contentType () == null
623
654
? StandardCharsets .UTF_8
624
655
: responseBody .contentType ().charset (StandardCharsets .UTF_8 );
656
+ Assertions .assertNotNull (
657
+ charset , "Null character set for Content-Type: " + responseBody .contentType ());
625
658
626
- // NULLSAFE_FIXME[Parameter Not Nullable]
627
659
ProgressiveStringDecoder streamDecoder = new ProgressiveStringDecoder (charset );
628
660
InputStream inputStream = responseBody .byteStream ();
629
661
try {
@@ -702,18 +734,25 @@ public void removeListeners(double count) {}
702
734
703
735
private @ Nullable MultipartBody .Builder constructMultipartBody (
704
736
ReadableArray body , String contentType , int requestId ) {
705
- MultipartBody .Builder multipartBuilder = new MultipartBody .Builder ();
706
- // NULLSAFE_FIXME[Parameter Not Nullable]
707
- multipartBuilder .setType (MediaType .parse (contentType ));
708
-
709
737
final ReactApplicationContext reactApplicationContext =
710
738
getReactApplicationContextIfActiveOrWarn ();
739
+ MultipartBody .Builder multipartBuilder = new MultipartBody .Builder ();
740
+ MediaType mediaType = MediaType .parse (contentType );
741
+ if (mediaType == null ) {
742
+ ResponseUtil .onRequestError (reactApplicationContext , requestId , "Invalid media type." , null );
743
+ return null ;
744
+ }
745
+ multipartBuilder .setType (mediaType );
711
746
712
747
for (int i = 0 , size = body .size (); i < size ; i ++) {
713
748
ReadableMap bodyPart = body .getMap (i );
749
+ if (bodyPart == null ) {
750
+ ResponseUtil .onRequestError (
751
+ reactApplicationContext , requestId , "Unrecognized FormData part." , null );
752
+ return null ;
753
+ }
714
754
715
755
// Determine part's content type.
716
- // NULLSAFE_FIXME[Nullable Dereference]
717
756
ReadableArray headersArray = bodyPart .getArray ("headers" );
718
757
Headers headers = extractHeaders (headersArray , null );
719
758
if (headers == null ) {
@@ -733,14 +772,12 @@ public void removeListeners(double count) {}
733
772
headers = headers .newBuilder ().removeAll (CONTENT_TYPE_HEADER_NAME ).build ();
734
773
}
735
774
736
- // NULLSAFE_FIXME[Nullable Dereference]
737
- if (bodyPart .hasKey (REQUEST_BODY_KEY_STRING )) {
738
- // NULLSAFE_FIXME[Nullable Dereference]
775
+ if (bodyPart .hasKey (REQUEST_BODY_KEY_STRING )
776
+ && bodyPart .getString (REQUEST_BODY_KEY_STRING ) != null ) {
739
777
String bodyValue = bodyPart .getString (REQUEST_BODY_KEY_STRING );
740
- // NULLSAFE_FIXME[Parameter Not Nullable]
741
778
multipartBuilder .addPart (headers , RequestBody .create (partContentType , bodyValue ));
742
- // NULLSAFE_FIXME[Nullable Dereference]
743
- } else if ( bodyPart .hasKey (REQUEST_BODY_KEY_URI )) {
779
+ } else if ( bodyPart . hasKey ( REQUEST_BODY_KEY_URI )
780
+ && bodyPart .getString (REQUEST_BODY_KEY_URI ) != null ) {
744
781
if (partContentType == null ) {
745
782
ResponseUtil .onRequestError (
746
783
reactApplicationContext ,
@@ -749,10 +786,8 @@ public void removeListeners(double count) {}
749
786
null );
750
787
return null ;
751
788
}
752
- // NULLSAFE_FIXME[Nullable Dereference]
753
789
String fileContentUriStr = bodyPart .getString (REQUEST_BODY_KEY_URI );
754
790
InputStream fileInputStream =
755
- // NULLSAFE_FIXME[Parameter Not Nullable]
756
791
RequestBodyUtil .getFileInputStream (getReactApplicationContext (), fileContentUriStr );
757
792
if (fileInputStream == null ) {
758
793
ResponseUtil .onRequestError (
@@ -785,8 +820,10 @@ public void removeListeners(double count) {}
785
820
if (header == null || header .size () != 2 ) {
786
821
return null ;
787
822
}
788
- // NULLSAFE_FIXME[Parameter Not Nullable]
789
- String headerName = HeaderUtil .stripHeaderName (header .getString (0 ));
823
+ String headerName = null ;
824
+ if (header .getString (0 ) != null ) {
825
+ headerName = HeaderUtil .stripHeaderName (header .getString (0 ));
826
+ }
790
827
String headerValue = header .getString (1 );
791
828
if (headerName == null || headerValue == null ) {
792
829
return null ;
0 commit comments