Skip to content

Commit

Permalink
Support more fetch init body types (denoland#1449)
Browse files Browse the repository at this point in the history
  • Loading branch information
kevinkassimo authored and ry committed Jan 3, 2019
1 parent 5b9c488 commit 8d452d7
Show file tree
Hide file tree
Showing 4 changed files with 91 additions and 3 deletions.
2 changes: 1 addition & 1 deletion js/blob.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
23 changes: 21 additions & 2 deletions js/fetch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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 {
Expand Down
51 changes: 51 additions & 0 deletions js/fetch_test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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:https://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:https://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:https://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:https://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:
//
Expand Down
18 changes: 18 additions & 0 deletions tools/http_server.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,24 @@ def do_GET(self):
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"
Expand Down

0 comments on commit 8d452d7

Please sign in to comment.