The following characters do not encode correctly when sent via fetch in Safari

Posted on Thursday, December 29, 2022

Insane title right

The Characters:

̀  (832)     turns into ̀    (768)
́  (833)     turns into ́    (769)
̓  (835)     turns into ̓    (787)
̈́  (836)     turns into ̈́    (776)
ʹ (884)     turns into ʹ    (697)
; (894)     turns into ;    (59)
· (903)     turns into ·    (183)
क़ (2392)   turns into क़    (2325)
ख़ (2393)   turns into ख़    (2326)
ग़ (2394)   turns into ग़    (2327)
ज़ (2395)   turns into ज़    (2332)
ड़ (2396)   turns into ड़    (2337)
ढ़ (2397)   turns into ढ़    (2338)
फ़ (2398)   turns into फ़    (2347)
य़ (2399)   turns into य़    (2351)
ড় (2524)   turns into ড়    (2465)
ঢ় (2525)   turns into ঢ়    (2466)
য় (2527)   turns into য়    (2479)
ਲ਼ (2611)   turns into ਲ਼    (2610)
ਸ਼ (2614)   turns into ਸ਼    (2616)
ਖ਼ (2649)   turns into ਖ਼    (2582)
ਗ਼ (2650)   turns into ਗ਼    (2583)
ਜ਼ (2651)   turns into ਜ਼    (2588)
ਫ਼ (2654)   turns into ਫ਼    (2603)
ଡ଼ (2908)   turns into ଡ଼    (2849)
ଢ଼ (2909)   turns into ଢ଼    (2850)
གྷ (3907)   turns into གྷ    (3906)
ཌྷ (3917)   turns into ཌྷ    (3916)
དྷ (3922)   turns into དྷ    (3921)
བྷ (3927)   turns into བྷ    (3926)
ཛྷ (3932)   turns into ཛྷ    (3931)
ཀྵ (3945)   turns into ཀྵ    (3904)
ཱི (3955)   turns into ཱི    (3953)
ཱུ (3957)   turns into ཱུ    (3953)
ྲྀ (3958)   turns into ྲྀ    (4018)
ླྀ (3960)   turns into ླྀ    (4019)
ཱྀ (3969)   turns into ཱྀ    (3953)
ྒྷ (3987)   turns into ྒྷ    (3986)
ྜྷ (3997)   turns into ྜྷ    (3996)
ྡྷ (4002)   turns into ྡྷ    (4001)
ྦྷ (4007)   turns into ྦྷ    (4006)
ྫྷ (4012)   turns into ྫྷ    (4011)
ྐྵ (4025)   turns into ྐྵ    (3984)
ά  (8049)  turns into ά    (940)
έ  (8051)  turns into έ    (941)
ή  (8053)  turns into ή    (942)
ί  (8055)  turns into ί    (943)
ό  (8057)  turns into ό    (972)
ύ  (8059)  turns into ύ    (973)
ώ  (8061)  turns into ώ    (974)
Ά  (8123)  turns into Ά    (902)
ι  (8126)  turns into ι    (953)
Έ  (8137)  turns into Έ    (904)
Ή  (8139)  turns into Ή    (905)
ΐ  (8147)  turns into ΐ    (912)
Ί  (8155)  turns into Ί    (906)
ΰ  (8163)  turns into ΰ    (944)
Ύ  (8171)  turns into Ύ    (910)
΅  (8174)  turns into ΅    (901)
`  (8175)  turns into `    (96)
Ό  (8185)  turns into Ό    (908)
Ώ  (8187)  turns into Ώ    (911)
´  (8189)  turns into ´    (180)
   (8192)  turns into      (8194)
   (8193)  turns into      (8195)
Ω  (8486)  turns into Ω    (937)
K  (8490)  turns into K    (75)
Å  (8491)  turns into Å    (197)
〈  (9001)  turns into 〈    (12296)
〉  (9002)  turns into 〉    (12297)
⫝̸ (10972)   turns into ⫝̸    (10973)

The test code

To test this out I wrote a really simple express server that echo’s out whatever is posted to it:

var app = express();
app.use(bodyParser.text({
  type: function(req) {
    return 'text';
  }
}));

app.post('/post', function (req, res) {
  res = res.status(200);
  if (req.get('Content-Type')) {
    res = res.type(req.get('Content-Type'));
  }
  res.send(req.body);
});

http.createServer(app).listen(7000);

On the client I used the following code:

async function runFetch(item) {
  const result = await fetch("http://localhost:7000/post", {
    method: "POST",
    body: String.fromCharCode(item),
  });
  const j = await result.text();
  if (j !== String.fromCharCode(item)) {
    console.log(`${String.fromCharCode(item)} (${item}) turns into ${item} (${j.charCodeAt(0)})`);
  }
}

async function testFetch() {
  for (let i = 0; i < 65536; i++) {
    await runFetch(i);
  }
}

There were some other problem codes, but they were outside the printable range.

When

I ran into this while compressing Uint8Array’s using a variation of lz-string, which produces a utf16 string, which I send to a node server for my project QuickGame World. The code works just fine on chrome and firefox, but bugged out sometimes on safari.

This lead to an entire day of end to end debugging.

Why


Honestly, I have no idea. This does not happen in chrome or firefox.


Conclusion

Sorry this post was more matter of fact and less helpful. This was the end result of hours of debugging and my hope that someone in the future finds this blog post without wasting hours trying to figure out why their code doesn’t work.

If you know why this happens, feel free to reach out to me on twitter!

See you next time!

Bye!