rnmapbox / rnmapbox/maps

[Bug]: addCustomHeader URL regex matching fails on Android for partial/substring matches

Open Beginner friendly
#4,257 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

bug 🪲
Dominant language
Kotlin
Stars
2.9k
Forks
947
Avg merge
6d 37m
Merged PRs (30d)
1

Description

Mapbox Version

default

React Native Version

0.83.6

Platform

Android, iOS

@rnmapbox/maps version

10.3.0

Standalone component to reproduce
import React from 'react';
import { View } from 'react-native';
import Mapbox from '@rnmapbox/maps';

// Substring regex (works on iOS, fails on Android due to strict matching)
const tileUrlRegex = /https:\/\/tiles\.example\.com\/v1\/map/;

class BugReportExample extends React.Component {
  componentDidMount() {
    // Inject custom header using the partial-match regex
    Mapbox.addCustomHeader(
      'X-Test-Header',
      'test-value-123',
      { urlMatching: tileUrlRegex }
    );
  }

  render() {
    return (
      <View style={{ flex: 1 }}>
        <Mapbox.MapView style={{ flex: 1 }}>
          <Mapbox.Camera centerCoordinate={[-74.00597, 40.71427]} zoomLevel={14} />
          
          {/* A RasterSource pointing to the dummy tile server to trigger requests containing extra parameters */}
          <Mapbox.RasterSource
            id="exampleTileSource"
            tileUrlTemplates={[
              'https://tiles.example.com/v1/map?style=streets&zoom={z}&x={x}&y={y}&format=png'
            ]}
            tileSize={256}
          >
            <Mapbox.RasterLayer id="exampleTileLayer" />
          </Mapbox.RasterSource>
        </Mapbox.MapView>
      </View>
    );
  }
}

export default BugReportExample;
Observed behavior and steps to reproduce

When calling Mapbox.addCustomHeader with a regular expression in urlMatching, the behavior diverges between platforms:

  • iOS successfully matches the URL and injects the header if the regex matches any substring of the request URL.
  • Android silently fails to inject the header unless the regular expression matches the entire URL string from start to finish.
Steps to Reproduce:
  1. Call Mapbox.addCustomHeader("X-Test-Header", "test-value-123", { urlMatching: /https:\/\/tiles\.example\.com\/v1\/map/ }) using a basic substring regex.
  2. Load a map layer pointing to a target URL that contains trailing parameters, such as:
    https://tiles.example.com/v1/map?style=streets&zoom=14&x=4823&y=6122&format=png
  3. Run the app on an iOS device/simulator. Observe that the network request successfully includes X-Test-Header: test-value-123.
  4. Run the exact same code on an Android device/emulator. Observe the network traffic. The custom header is missing because the trailing query and coordinate parameters (?style=streets&zoom=14...) prevent the regex from achieving a full-string match on the native layer.
Expected behavior

The regular expression evaluation should be unified across both platforms. Both iOS and Android should evaluate urlMatching using a substring/partial match algorithm.

If a pattern matches a portion of the incoming request URL, the header should be successfully appended on both platforms without forcing developers to write cross-platform "hacks" like appending .*$ to their regular expressions.

Notes / preliminary analysis

The bug is caused by a fundamental difference in how the native platforms evaluate regular expressions within their custom network interceptors.

iOS Native Side (Substring Match)

iOS utilizes NSRegularExpression.firstMatch, which checks for the first occurrence of a matching substring in the URL:

if pattern.firstMatch(in: urlString, options: [], range: range) != nil {
    headers[key] = entry.headerValue
}
  • This acts like JavaScript's .test() or a substring search. It is highly flexible.
Android Native Side (Strict Full Match)

Android utilizes Kotlin's Regex.matches(), which evaluates whether the entire URL matches the pattern:

if (urlRegexp.matches(destination)) {
    headers[entry.key] = entry.value.headerValue
}
  • Kotlin's Regex.matches(input) compiles down to Java's Matcher.matches().
  • According to Java specifications, matches() checks if the entire region sequence conforms to the regex (essentially wrapping your pattern in implicit ^...$ anchors). If there are trailing query parameters (which there always are on tile/map servers), the check fails.
Suggested Fix

To align Android with the iOS implementation, the Android native network interceptor should be updated to use containsMatchIn instead of matches:

// Change this line in the Android interceptor to support substring matches:
if (urlRegexp.containsMatchIn(destination)) { 
    headers[entry.key] = entry.value.headerValue
}
Additional links and references

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with android/src/main/java/com/mapbox/rnmbx/modules/CustomHttpHeaders.kt around line 37 and compare its URL matching with ios/RNMBX/CustomHttpHeaders.swift around line 50. Verify the Android interceptor handles a regex matching only part of a URL with trailing query parameters, while preserving the existing custom-header behavior and matching iOS.

Written by the indexing model from the issue text.

Assessment

Tech stack
kotlin, react-native
Domain
mobile-dev, networking
Issue type
Bug
Difficulty
1/5
Estimated time
Under an hour
Activity status
Quiet
Clarity
Clearly specified
Newbie friendliness
86/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.