private Callback<Long> fetchStitchRequestBodyCallback(RetainingAsyncWritableChannel channel, BlobInfo blobInfo) {
      return buildCallback(frontendMetrics.putReadStitchRequestMetrics, bytesRead -> {
        PutBlobOptions options = new PutBlobOptionsBuilder().restRequest(restRequest).build();
        BlobProperties propertiesForRouterUpload = getPropertiesForRouterUpload(blobInfo);
        router.stitchBlob(propertiesForRouterUpload, blobInfo.getUserMetadata(),
            getChunksToStitch(parseCompleteMultipartUploadBody(channel)), options,
            routerStitchBlobCallback(blobInfo, propertiesForRouterUpload),
            QuotaUtils.buildQuotaChargeCallback(restRequest, quotaManager, true));
      }, uri, LOGGER, finalCallback);
    }
After reading the body of the stitch request, parse the request body,
and make a call to {@link Router#stitchBlob}.
@param channel the {@link RetainingAsyncWritableChannel} that will contain the request body.
@param blobInfo the {@link BlobInfo} to make the router call with.
@return a {@link Callback} to be used with {@link RestRequest#readInto}.
ambry-frontend/src/main/java/com/github/ambry/frontend/s3/S3MultipartCompleteUploadHandler.java