Confusion about HTTP transferring non-text (binary) data - javascript

I was looking at this MDN tutorial https://developer.mozilla.org/en-US/docs/Web/HTTP/Messages
where it says
HTTP messages are composed of textual information encoded in ASCII.
I thought it means that HTTP can only transfer textual info aka strings, assuming the HTTP message here refers to header + body in responses.
But later I found out that HTTP response body can have multiple MIME types outside of text, such as image, video, application/json etc. Doesn't that mean HTTP can also transfer non-textual information, which contradicts what that MDN page says about HTTP messages?
I am aware of encoding methods like utf-8 and base64, I guess you can use Base64 Encoding for the binary data so that it is transformed into text — and then can be sent with an application/json content type as another property of the JSON payload. But when you choose not to do encoding, instead using correct content-type you can just transfer the binary data? I am still trying to figure this out.
Also I have some experience consuming REST APIs from the front end. My impression is that you typically don't transfer any binary data e.g. images, files, audios with RESTful APIs. They often serve JSON or XML as the response. I wonder why is that? Is it because REST APIs is not suitable for transferring binary data directly? What are some of the common practice for transferring images or audios files to the front end?

The line you quoted is talking about the start line, status line, and headers, which use only ASCII.
The body of a request or response is an arbitrary sequence of bytes. It's mainly intepreted by the application, not by the HTTP layer. It doesn't need to be in any particular encoding. The header has a Content-Length field, and the client simply reads that many bytes after the header (there's also chunked encoding, which breaks the content up into chunks, but each one starts with a byte length, and the client simply concatenates them).
In addition, HTTP includes Transfer-Encoding types that specify the encoding of the data. This includes a number of compression formats that produce binary data.
While it's possible to use textual encodings such as base64, this is not usually done in HTTP because it increases the size of the message and it's not necessary.

Related

How to send image to backend to store in mysql in js? [duplicate]

What does enctype='multipart/form-data' mean in an HTML form and when should we use it?
When you make a POST request, you have to encode the data that forms the body of the request in some way.
HTML forms provide three methods of encoding.
application/x-www-form-urlencoded (the default)
multipart/form-data
text/plain
Work was being done on adding application/json, but that has been abandoned.
(Other encodings are possible with HTTP requests generated using other means than an HTML form submission. JSON is a common format for use with web services and some still use SOAP.)
The specifics of the formats don't matter to most developers. The important points are:
Never use text/plain.
When you are writing client-side code:
use multipart/form-data when your form includes any <input type="file"> elements
otherwise you can use multipart/form-data or application/x-www-form-urlencoded but application/x-www-form-urlencoded will be more efficient
When you are writing server-side code:
Use a prewritten form handling library
Most (such as Perl's CGI->param or the one exposed by PHP's $_POST superglobal) will take care of the differences for you. Don't bother trying to parse the raw input received by the server.
Sometimes you will find a library that can't handle both formats. Node.js's most popular library for handling form data is body-parser which cannot handle multipart requests (but has documentation that recommends some alternatives which can).
If you are writing (or debugging) a library for parsing or generating the raw data, then you need to start worrying about the format. You might also want to know about it for interest's sake.
application/x-www-form-urlencoded is more or less the same as a query string on the end of the URL.
multipart/form-data is significantly more complicated but it allows entire files to be included in the data. An example of the result can be found in the HTML 4 specification.
text/plain is introduced by HTML 5 and is useful only for debugging — from the spec: They are not reliably interpretable by computer — and I'd argue that the others combined with tools (like the Network Panel in the developer tools of most browsers) are better for that).
when should we use it?
Quentin's answer is right: use multipart/form-data if the form contains a file upload, and application/x-www-form-urlencoded otherwise, which is the default if you omit enctype.
I'm going to:
add some more HTML5 references
explain why he is right with a form submit example
HTML5 references
There are three possibilities for enctype:
application/x-www-form-urlencoded
multipart/form-data (spec points to RFC7578)
text/plain. This is "not reliably interpretable by computer", so it should never be used in production, and we will not look further into it.
How to generate the examples
Once you see an example of each method, it becomes obvious how they work, and when you should use each one.
You can produce examples using:
nc -l or an ECHO server: HTTP test server accepting GET/POST requests
a user agent like a browser or cURL
Save the form to a minimal .html file:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8"/>
<title>upload</title>
</head>
<body>
<form action="http://localhost:8000" method="post" enctype="multipart/form-data">
<p><input type="text" name="text1" value="text default">
<p><input type="text" name="text2" value="aωb">
<p><input type="file" name="file1">
<p><input type="file" name="file2">
<p><input type="file" name="file3">
<p><button type="submit">Submit</button>
</form>
</body>
</html>
We set the default text value to aωb, which means aωb because ω is U+03C9, which are the bytes 61 CF 89 62 in UTF-8.
Create files to upload:
echo 'Content of a.txt.' > a.txt
echo '<!DOCTYPE html><title>Content of a.html.</title>' > a.html
# Binary file containing 4 bytes: 'a', 1, 2 and 'b'.
printf 'a\xCF\x89b' > binary
Run our little echo server:
while true; do printf '' | nc -l localhost 8000; done
Open the HTML on your browser, select the files and click on submit and check the terminal.
nc prints the request received.
Tested on: Ubuntu 14.04.3, nc BSD 1.105, Firefox 40.
multipart/form-data
Firefox sent:
POST / HTTP/1.1
[[ Less interesting headers ... ]]
Content-Type: multipart/form-data; boundary=---------------------------735323031399963166993862150
Content-Length: 834
-----------------------------735323031399963166993862150
Content-Disposition: form-data; name="text1"
text default
-----------------------------735323031399963166993862150
Content-Disposition: form-data; name="text2"
aωb
-----------------------------735323031399963166993862150
Content-Disposition: form-data; name="file1"; filename="a.txt"
Content-Type: text/plain
Content of a.txt.
-----------------------------735323031399963166993862150
Content-Disposition: form-data; name="file2"; filename="a.html"
Content-Type: text/html
<!DOCTYPE html><title>Content of a.html.</title>
-----------------------------735323031399963166993862150
Content-Disposition: form-data; name="file3"; filename="binary"
Content-Type: application/octet-stream
aωb
-----------------------------735323031399963166993862150--
For the binary file and text field, the bytes 61 CF 89 62 (aωb in UTF-8) are sent literally. You could verify that with nc -l localhost 8000 | hd, which says that the bytes:
61 CF 89 62
were sent (61 == 'a' and 62 == 'b').
Therefore it is clear that:
Content-Type: multipart/form-data; boundary=---------------------------735323031399963166993862150 sets the content type to multipart/form-data and says that the fields are separated by the given boundary string.
But note that the:
boundary=---------------------------735323031399963166993862150
has two less dashes -- than the actual barrier
-----------------------------735323031399963166993862150
This is because the standard requires the boundary to start with two dashes --. The other dashes appear to be just how Firefox chose to implement the arbitrary boundary. RFC 7578 clearly mentions that those two leading dashes -- are required:
4.1. "Boundary" Parameter of multipart/form-data
As with other multipart types, the parts are delimited with a
boundary delimiter, constructed using CRLF, "--", and the value of
the "boundary" parameter.
every field gets some sub headers before its data: Content-Disposition: form-data;, the field name, the filename, followed by the data.
The server reads the data until the next boundary string. The browser must choose a boundary that will not appear in any of the fields, so this is why the boundary may vary between requests.
Because we have the unique boundary, no encoding of the data is necessary: binary data is sent as is.
TODO: what is the optimal boundary size (log(N) I bet), and name / running time of the algorithm that finds it? Asked at: https://cs.stackexchange.com/questions/39687/find-the-shortest-sequence-that-is-not-a-sub-sequence-of-a-set-of-sequences
Content-Type is automatically determined by the browser.
How it is determined exactly was asked at: How is mime type of an uploaded file determined by browser?
application/x-www-form-urlencoded
Now change the enctype to application/x-www-form-urlencoded, reload the browser, and resubmit.
Firefox sent:
POST / HTTP/1.1
[[ Less interesting headers ... ]]
Content-Type: application/x-www-form-urlencoded
Content-Length: 51
text1=text+default&text2=a%CF%89b&file1=a.txt&file2=a.html&file3=binary
Clearly the file data was not sent, only the basenames. So this cannot be used for files.
As for the text field, we see that usual printable characters like a and b were sent in one byte, while non-printable ones like 0xCF and 0x89 took up 3 bytes each: %CF%89!
Comparison
File uploads often contain lots of non-printable characters (e.g. images), while text forms almost never do.
From the examples we have seen that:
multipart/form-data: adds a few bytes of boundary overhead to the message, and must spend some time calculating it, but sends each byte in one byte.
application/x-www-form-urlencoded: has a single byte boundary per field (&), but adds a linear overhead factor of 3x for every non-printable character.
Therefore, even if we could send files with application/x-www-form-urlencoded, we wouldn't want to, because it is so inefficient.
But for printable characters found in text fields, it does not matter and generates less overhead, so we just use it.
enctype='multipart/form-data is an encoding type that allows files to be sent through a POST. Quite simply, without this encoding the files cannot be sent through POST.
If you want to allow a user to upload a file via a form, you must use this enctype.
When submitting a form, you tell your browser to send, via the HTTP protocol, a message on the network, properly enveloped in a TCP/IP protocol message structure. An HTML page has a way to send data to the server: by using <form>s.
When a form is submitted, an HTTP Request is created and sent to the server, the message will contain the field names in the form and the values filled in by the user. This transmission can happen with POST or GET HTTP methods.
POST tells your browser to build an HTTP message and put all content in the body of the message (a very useful way of doing things, more safe and also flexible).
GET will submit the form data in the querystring. It has some constraints about data representation and length.
Stating how to send your form to the server
Attribute enctype has sense only when using POST method. When specified, it instructs the browser to send the form by encoding its content in a specific way. From MDN - Form enctype:
When the value of the method attribute is post, enctype is the MIME
type of content that is used to submit the form to the server.
application/x-www-form-urlencoded: This is the default. When the form is sent, all names and values are collected and URL Encoding is performed on the final string.
multipart/form-data: Characters are NOT encoded. This is important when the form has a file upload control. You want to send the file binary and this ensures that bitstream is not altered.
text/plain: Spaces get converted, but no more encoding is performed.
Security
When submitting forms, some security concerns can arise as stated in RFC 7578 Section 7: Multipart form data - Security considerations:
All form-processing software should treat user supplied form-data
with sensitivity, as it often contains confidential or personally
identifying information. There is widespread use of form "auto-fill"
features in web browsers; these might be used to trick users to
unknowingly send confidential information when completing otherwise
innocuous tasks. multipart/form-data does not supply any features
for checking integrity, ensuring confidentiality, avoiding user
confusion, or other security features; those concerns must be
addressed by the form-filling and form-data-interpreting applications.
Applications that receive forms and process them must be careful
not to supply data back to the requesting form-processing site that
was not intended to be sent.
It is important when interpreting the filename of the Content-
Disposition header field to not inadvertently overwrite files in the
recipient's file space.
This concerns you if you are a developer and your server will process forms submitted by users which might end up containing sensitive information.
enctype='multipart/form-data' means that no characters will be encoded. that is why this type is used while uploading files to server.
So multipart/form-data is used when a form requires binary data, like the contents of a file, to be uploaded
Set the method attribute to POST because file content can't be put inside a URL parameter using a form.
Set the value of enctype to multipart/form-data because the data will be split into multiple parts, one for each file plus one for the text of the form body that may be sent with them.
enctype(ENCode TYPE) attribute specifies how the form-data should be encoded when submitting it to the server.
multipart/form-data is one of the value of enctype attribute, which is used in form element that have a file upload. multi-part means form data divides into multiple parts and send to server.
Usually this is when you have a POST form which needs to take a file upload as data... this will tell the server how it will encode the data transferred, in such case it won't get encoded because it will just transfer and upload the files to the server, Like for example when uploading an image or a pdf
The enctype attribute specifies how the form-data should be encoded when submitting it to the server.
The enctype attribute can be used only if method="post".
No characters are encoded. This value is required when you are using forms that have a file upload control
From W3Schools

Fetch raw gzip-encoded web page into Uint8Array

I'm using fetch to retrieve a URL. This is in code that is acting as a proxy and if it gets a response with content-encoding: gzip, I want to get the raw data so I can send it back to the consumer without decoding it.
But I see no way to do this. Response.blob, Response.arrayBuffer, Response.body.getReader(), .... these all seem to decode the content.
So, for example, in my test case, I end up with a 15K array of bytes instead of the actual response body which was only 4k.
Is there a way to get the raw contents of the response without it being decoded?
The browser automatically decodes compressed, gzip-encoded HTTP responses in its low-level networking layer before surfacing response data to the programatic Javascript layer. Given that the compressed bytes are already transmitted over the network and available in users' browsers, that data has already been "sent to the customer".
The Compression Streams API can performantly create a Gzip-encoded, ArrayBuffer, Blob, etc:
const [ab, abGzip] = await Promise.all((await fetch(url)).body.tee().map(
(stream, i) => new Response(i === 0
? stream
: stream.pipeThrough(new CompressionStream('gzip'))
).arrayBuffer()
))
Full example
https://batman.dev/static/74634392/
A gzip-encoded SVG image is provided to users as a downloadable file link. The original SVG image is retrived using fetch() from a gzip-encoded response.

encodeURIComponent and content-type: 'charset: utf-8'

I'm building a react application and I use Node (with Express) as a proxy-server. I send data from react app to node-express, then in Node I use that data to form URI and to make requests to another server.
My question is this: Shouldn't 'content-type': 'charset: utf-8' be enough when I send data including greek characters to Node? For example, I make a post request (using Fetch) to Node and I send code 'ΠΕ0001' using the header I already mentioned. Why do I get the error 'Path contains unescaped characters'? When I use encodeURIComponent it does work, but why 'charset: utf-8' is not enough?
Just setting the header 'content-type': 'charset: utf-8' is not enough. Essentially with this Header you're just telling the server (Node in this instance), that the data you send is in utf-8 format, which it should expect anyway.
Your string, however, is in UTF-16 format, because the letter Π needs two bytes to be represented..
You can read more about character encoding here.
Hence you need encodeURIComponent first. In our case, Π is then represented as %CE%A0, which are its byte's representations in UTF-8.
use this method JSON.stringify() to convert data to json object.
then pass that json object into encodeURIComponent()
then call fetch method

How to handle multipart/form-data on client side?

I am building a frontend application in which I'm going to retrieve files via the API provided from backend.
The API consumes json request like most restful APIs whereas responses file in multipart/form-data type. Therefore when I tried to get the body of the response with axios, data appears like this.
--77d4f4ac-bcb2-4457-ad81-810cf8c3ce47
Content-Disposition: attachment; filename=20170822.txt
Content-Type: text/plain
...data...
--77d4f4ac-bcb2-4457-ad81-810cf8c3ce47--
It confused me quite a lot since I'm used to deal with raw data with blob object. However it seems that I have to parse the response by myself here in order to get the raw data. I searched around but found almost all of the articles and questions are discussing about server-side handling. So my question can be separated into 2 pieces.
Is it okay/possible to handle multipart/form-data on client side?
If it is, how can I handle it? (Of course, it will be really appreciated if there's a library for it)

How can I compress the response of an ajax request?

I return the whole HTML as the response of an ajax request (not just an array as JSON). So as you know, the response will be larger a bit (than JSON). Because it contains some more things like html tags, html attributes etc ... So to
tncreased scalability (reduced server load).
Less network bandwidth (lower cost).
Better user experience (faster).
I want to compress the response. Something like .gzip format. Based on so tests, this is the size of an ajax response:
And this is the size of an ajax response which is zipped:
See? There is a huge different between them in theirs sizes.
All I want to know, is it possible to compress the response of an ajax request on the way and convert it to a regular text on the client side? For doing that do I need to do some changes in the web service (like nginx) configuration? Or it does that automatically?
You should use Lz-string method to compress data and send using ajax call and at the time of response, again use it by decompressing it
GITHUB links : https://github.com/pieroxy/lz-string/
Documentation & usage : https://coderwall.com/p/mekopw/jsonc-compress-your-json-data-up-to-80
You may want to check out some of the other StackOverflow posts, plus Wikipedia links & articles on the web regarding Deflate & Gzip. Here are some links for you to peruse. They have some interesting charts & information in them:
StackOverflow: Why Use Deflate Instead of Gzip for Text Files Served by Apache
StackOverflow: Is There Any Performance Hit Involved In Choosing Gzip Over Deflate For-
HTTP.com
Wikpedia: Deflate
Wikipedia: Gzip
Article: How to Optimize Your Site with GZip Compression
RFC: Deflate

Categories