The Internet is scattershot with uninformed commentary on this issue, which I now supplant with a single clearinghouse of uninformed commentary. Mozilla developers know about this problem but for some reason, presumably bug-compatibility, added a sendAsBinary method rather than fixing send. Maybe in five years that will be a standard, but it doesn't help me now. (Also, in experiments I couldn't get sendAsBinary to send binary data! It crashed with NS_ERROR_DOM_INVALID_CHARACTER_ERR!)
One solution is to create a MIME multipart message with one part and a Content-Transfer-Encoding of "base64". Bandwidth-wasty, but effective. Except, thought I, why bother with the multipart message? Just encode the binary data as base64 and send a Transfer-Encoding of "base64". (Content-Transfer-Encoding is explicitly prohibited by RFC2616; it was more or less split into Content-Encoding and Transfer-Encoding.)
Now, "base64" is not an officially registered transfer coding, but using it seems to be in the spirit of the Transfer-Encoding spec, which wants 'to ensure "safe transport" through the network.' The only difference is that the unsafe thing (stupid Javascript) is on one end of the network rather than in the middle.
Here's another solution that I don't really understand. It uses Mozilla-specific hacks to create an input stream containing binary data, which input stream is passed to send() instead of a string which will inevitably be chopped or parsed as a DOM object or something.
For now I'm going to hope that the problem goes away when I start using a different Ajax toolkit, which we were going to do anyway, but as always I'm very disappointed when I try to make an HTTP request and something totally random happens that's not my HTTP request.
Update: Because of this problem it's apparently common to send binary files using a hidden IFRAME that contains an HTML form. The binary file is sent as a MIME multipart message over POST, but it's not base64 encoded--it's a regular browser file upload.
Update 2: Have fun arguing over the legitimacy of my desire to do this instead of investigating the problem, Reddit dudes. The %00 trick sounds feasible, other Reddit dudes. I'll try it. PS: switching to another toolkit didn't solve the problem because all the toolkits use XHR.
(1) Tue Oct 07 2008 12:47 I Just Want To Make An XMLHttpOmelette:
I've always hated Javascript, and now that I'm working on a Javascript client for Launchpad's web service, I know I was right to hate it. Recently I lost half a day to a mysterious problem that turned out to be XMLHttpRequest truncating my PUT request at the first null character. Because why would you want to send binary data over the Internet? It's not like some kind of... tube... that you can put just anything in.