summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKevin (Kun) "Kassimo" Qian <kevinkassimo@gmail.com>2019-01-03 06:41:20 -0500
committerRyan Dahl <ry@tinyclouds.org>2019-01-03 06:41:20 -0500
commit8d452d74fa6f63eb0bce0567083d375b50329de4 (patch)
tree0f60fa86fd52a69e30b54d5bad5a7942cf9ae221
parent5b9c4889215a5c8e5a3681af0b3586ff4e0a15fe (diff)
Support more fetch init body types (#1449)
-rw-r--r--js/blob.ts2
-rw-r--r--js/fetch.ts23
-rw-r--r--js/fetch_test.ts51
-rwxr-xr-xtools/http_server.py18
4 files changed, 91 insertions, 3 deletions
diff --git a/js/blob.ts b/js/blob.ts
index 96aae496e..7510c9cef 100644
--- a/js/blob.ts
+++ b/js/blob.ts
@@ -3,7 +3,7 @@ import * as domTypes from "./dom_types";
import { containsOnlyASCII } from "./util";
import { TextEncoder } from "./text_encoding";
-const bytesSymbol = Symbol("bytes");
+export const bytesSymbol = Symbol("bytes");
export class DenoBlob implements domTypes.Blob {
private readonly [bytesSymbol]: Uint8Array;
diff --git a/js/fetch.ts b/js/fetch.ts
index 34afccd24..fd6299c8f 100644
--- a/js/fetch.ts
+++ b/js/fetch.ts
@@ -5,12 +5,13 @@ import { sendAsync } from "./dispatch";
import * as msg from "gen/msg_generated";
import * as domTypes from "./dom_types";
import { TextDecoder, TextEncoder } from "./text_encoding";
-import { DenoBlob } from "./blob";
+import { DenoBlob, bytesSymbol as blobBytesSymbol } from "./blob";
import { Headers } from "./headers";
import * as io from "./io";
import { read, close } from "./files";
import { Buffer } from "./buffer";
import { FormData } from "./form_data";
+import { URLSearchParams } from "./url_search_params";
function getHeaderValueParams(value: string): Map<string, string> {
const params = new Map();
@@ -165,7 +166,7 @@ class Body implements domTypes.Body, domTypes.ReadableStream, io.ReadCloser {
// TODO: based on spec
// https://xhr.spec.whatwg.org/#dom-formdata-append
// https://xhr.spec.whatwg.org/#create-an-entry
- // Currently it does not meantion how I could pass content-type
+ // Currently it does not mention how I could pass content-type
// to the internally created file object...
formData.append(dispositionName, blob, filename);
} else {
@@ -358,14 +359,32 @@ export async function fetch(
headers = null;
}
+ // ref: https://fetch.spec.whatwg.org/#body-mixin
+ // Body should have been a mixin
+ // but we are treating it as a separate class
if (init.body) {
+ if (!headers) {
+ headers = new Headers();
+ }
+ let contentType = "";
if (typeof init.body === "string") {
body = new TextEncoder().encode(init.body);
+ contentType = "text/plain;charset=UTF-8";
} else if (isTypedArray(init.body)) {
body = init.body;
+ } else if (init.body instanceof URLSearchParams) {
+ body = new TextEncoder().encode(init.body.toString());
+ contentType = "application/x-www-form-urlencoded;charset=UTF-8";
+ } else if (init.body instanceof DenoBlob) {
+ body = init.body[blobBytesSymbol];
+ contentType = init.body.type;
} else {
+ // TODO: FormData, ReadableStream
notImplemented();
}
+ if (contentType && !headers.has("content-type")) {
+ headers.set("content-type", contentType);
+ }
}
}
} else {
diff --git a/js/fetch_test.ts b/js/fetch_test.ts
index 773b2eebd..582f92839 100644
--- a/js/fetch_test.ts
+++ b/js/fetch_test.ts
@@ -84,6 +84,57 @@ testPerm({ net: true }, async function fetchURLEncodedFormDataSuccess() {
assertEqual(formData.get("field_2").toString(), "<Deno>");
});
+testPerm({ net: true }, async function fetchInitStringBody() {
+ const data = "Hello World";
+ const response = await fetch("http://localhost:4545/echo_server", {
+ method: "POST",
+ body: data
+ });
+ const text = await response.text();
+ assertEqual(text, data);
+ assert(response.headers.get("content-type").startsWith("text/plain"));
+});
+
+testPerm({ net: true }, async function fetchInitTypedArrayBody() {
+ const data = "Hello World";
+ const response = await fetch("http://localhost:4545/echo_server", {
+ method: "POST",
+ body: new TextEncoder().encode(data)
+ });
+ const text = await response.text();
+ assertEqual(text, data);
+});
+
+testPerm({ net: true }, async function fetchInitURLSearchParamsBody() {
+ const data = "param1=value1&param2=value2";
+ const params = new URLSearchParams(data);
+ const response = await fetch("http://localhost:4545/echo_server", {
+ method: "POST",
+ body: params
+ });
+ const text = await response.text();
+ assertEqual(text, data);
+ assert(
+ response.headers
+ .get("content-type")
+ .startsWith("application/x-www-form-urlencoded")
+ );
+});
+
+testPerm({ net: true }, async function fetchInitBlobBody() {
+ const data = "const a = 1";
+ const blob = new Blob([data], {
+ type: "text/javascript"
+ });
+ const response = await fetch("http://localhost:4545/echo_server", {
+ method: "POST",
+ body: blob
+ });
+ const text = await response.text();
+ assertEqual(text, data);
+ assert(response.headers.get("content-type").startsWith("text/javascript"));
+});
+
// TODO(ry) The following tests work but are flaky. There's a race condition
// somewhere. Here is what one of these flaky failures looks like:
//
diff --git a/tools/http_server.py b/tools/http_server.py
index 7c4b1fe25..74402f536 100755
--- a/tools/http_server.py
+++ b/tools/http_server.py
@@ -39,6 +39,24 @@ class ContentTypeHandler(SimpleHTTPServer.SimpleHTTPRequestHandler):
return
return SimpleHTTPServer.SimpleHTTPRequestHandler.do_GET(self)
+ def do_POST(self):
+ # Simple echo server for request reflection
+ if "echo_server" in self.path:
+ self.protocol_version = 'HTTP/1.1'
+ self.send_response(200, 'OK')
+ if self.headers.has_key('content-type'):
+ self.send_header('content-type',
+ self.headers.getheader('content-type'))
+ self.end_headers()
+ data_string = self.rfile.read(int(self.headers['Content-Length']))
+ self.wfile.write(bytes(data_string))
+ return
+ self.protocol_version = 'HTTP/1.1'
+ self.send_response(501)
+ self.send_header('content-type', 'text/plain')
+ self.end_headers()
+ self.wfile.write(bytes('Server does not support this operation'))
+
def guess_type(self, path):
if ".t1." in path:
return "text/typescript"