diff --git a/js/deno.ts b/js/deno.ts
index 6f71ee4991..187d0f27f7 100644
--- a/js/deno.ts
+++ b/js/deno.ts
@@ -1,11 +1,7 @@
// Copyright 2018 the Deno authors. All rights reserved. MIT license.
// Public deno module.
///
-export {
- env,
- exit,
- makeTempDirSync
-} from "./os";
+export { env, exit, makeTempDirSync } from "./os";
export { mkdirSync, mkdir } from "./mkdir";
export { removeSync, remove, removeAllSync, removeAll } from "./remove";
export { readFileSync, readFile } from "./read_file";
diff --git a/js/fetch.ts b/js/fetch.ts
index e3aa052f92..24905694e7 100644
--- a/js/fetch.ts
+++ b/js/fetch.ts
@@ -16,19 +16,75 @@ import {
Response,
Blob,
RequestInit,
+ HeadersInit,
FormData
} from "./fetch_types";
import { TextDecoder } from "./text_encoding";
-class DenoHeaders implements Headers {
- append(name: string, value: string): void {
- assert(false, "Implement me");
+interface Header {
+ name: string;
+ value: string;
+}
+
+export class DenoHeaders implements Headers {
+ private readonly headerList: Header[] = [];
+
+ constructor(init?: HeadersInit) {
+ if (init) {
+ this._fill(init);
+ }
}
+
+ private _append(header: Header): void {
+ // TODO(qti3e) Check header based on the fetch spec.
+ this._appendToHeaderList(header);
+ }
+
+ private _appendToHeaderList(header: Header): void {
+ const lowerCaseName = header.name.toLowerCase();
+ for (let i = 0; i < this.headerList.length; ++i) {
+ if (this.headerList[i].name.toLowerCase() === lowerCaseName) {
+ header.name = this.headerList[i].name;
+ }
+ }
+ this.headerList.push(header);
+ }
+
+ private _fill(init: HeadersInit): void {
+ if (Array.isArray(init)) {
+ for (let i = 0; i < init.length; ++i) {
+ const header = init[i];
+ if (header.length !== 2) {
+ throw new TypeError("Failed to construct 'Headers': Invalid value");
+ }
+ this._append({
+ name: header[0],
+ value: header[1]
+ });
+ }
+ } else {
+ for (const key in init) {
+ this._append({
+ name: key,
+ value: init[key]
+ });
+ }
+ }
+ }
+
+ append(name: string, value: string): void {
+ this._appendToHeaderList({ name, value });
+ }
+
delete(name: string): void {
assert(false, "Implement me");
}
get(name: string): string | null {
- assert(false, "Implement me");
+ for (const header of this.headerList) {
+ if (header.name.toLowerCase() === name.toLowerCase()) {
+ return header.value;
+ }
+ }
return null;
}
has(name: string): boolean {
@@ -54,15 +110,20 @@ class FetchResponse implements Response {
statusText = "FIXME"; // TODO
readonly type = "basic"; // TODO
redirected = false; // TODO
- headers = new DenoHeaders();
+ headers: DenoHeaders;
readonly trailer: Promise;
//private bodyChunks: Uint8Array[] = [];
private first = true;
private bodyWaiter: Resolvable;
- constructor(readonly status: number, readonly body_: ArrayBuffer) {
+ constructor(
+ readonly status: number,
+ readonly body_: ArrayBuffer,
+ headersList: Array<[string, string]>
+ ) {
this.bodyWaiter = createResolvable();
this.trailer = createResolvable();
+ this.headers = new DenoHeaders(headersList);
setTimeout(() => {
this.bodyWaiter.resolve(body_);
}, 0);
@@ -149,6 +210,14 @@ export async function fetch(
assert(bodyArray != null);
const body = typedArrayToArrayBuffer(bodyArray!);
- const response = new FetchResponse(status, body);
+ const headersList: Array<[string, string]> = [];
+ const len = msg.headerKeyLength();
+ for (let i = 0; i < len; ++i) {
+ const key = msg.headerKey(i);
+ const value = msg.headerValue(i);
+ headersList.push([key, value]);
+ }
+
+ const response = new FetchResponse(status, body, headersList);
return response;
}
diff --git a/js/fetch_test.ts b/js/fetch_test.ts
index 1af3bc2eb7..2b7f32099f 100644
--- a/js/fetch_test.ts
+++ b/js/fetch_test.ts
@@ -18,3 +18,20 @@ test(async function fetchPerm() {
assertEqual(err.kind, deno.ErrorKind.PermissionDenied);
assertEqual(err.name, "PermissionDenied");
});
+
+testPerm({ net: true }, async function fetchHeaders() {
+ const response = await fetch("http://localhost:4545/package.json");
+ const headers = response.headers;
+ assertEqual(headers.get("Content-Type"), "application/json");
+ assert(headers.get("Server").startsWith("SimpleHTTP"));
+});
+
+test(async function headersAppend() {
+ let err;
+ try {
+ const headers = new Headers([["foo", "bar", "baz"]]);
+ } catch (e) {
+ err = e;
+ }
+ assert(err instanceof TypeError);
+});
diff --git a/js/fetch_types.d.ts b/js/fetch_types.d.ts
index f00bdd3c58..9d4082c359 100644
--- a/js/fetch_types.d.ts
+++ b/js/fetch_types.d.ts
@@ -13,7 +13,7 @@ See the Apache Version 2.0 License for specific language governing permissions
and limitations under the License.
*******************************************************************************/
-type HeadersInit = Headers | string[][] | Record;
+type HeadersInit = string[][] | Record;
type BodyInit =
| Blob
| BufferSource
diff --git a/js/globals.ts b/js/globals.ts
index cd29d34c79..3805f12f57 100644
--- a/js/globals.ts
+++ b/js/globals.ts
@@ -6,6 +6,7 @@ import * as textEncoding from "./text_encoding";
import * as fetch_ from "./fetch";
import { libdeno } from "./libdeno";
import { globalEval } from "./global-eval";
+import { DenoHeaders } from "./fetch";
declare global {
interface Window {
@@ -23,6 +24,8 @@ declare global {
TextEncoder: typeof TextEncoder;
TextDecoder: typeof TextDecoder;
+
+ Headers: typeof Headers;
}
const clearTimeout: typeof timers.clearTimer;
@@ -38,6 +41,7 @@ declare global {
// tslint:disable:variable-name
const TextEncoder: typeof textEncoding.TextEncoder;
const TextDecoder: typeof textEncoding.TextDecoder;
+ const Headers: typeof DenoHeaders;
// tslint:enable:variable-name
}
@@ -57,3 +61,5 @@ window.TextEncoder = textEncoding.TextEncoder;
window.TextDecoder = textEncoding.TextDecoder;
window.fetch = fetch_.fetch;
+
+window.Headers = DenoHeaders;
diff --git a/js/rename.ts b/js/rename.ts
index 241e274908..5917912f5b 100644
--- a/js/rename.ts
+++ b/js/rename.ts
@@ -4,8 +4,8 @@ import { flatbuffers } from "flatbuffers";
import * as dispatch from "./dispatch";
/**
- * Synchronously renames (moves) oldpath to newpath. If newpath already exists
- * and is not a directory, Rename replaces it. OS-specific restrictions may
+ * Synchronously renames (moves) oldpath to newpath. If newpath already exists
+ * and is not a directory, Rename replaces it. OS-specific restrictions may
* apply when oldpath and newpath are in different directories.
*
* import { renameSync } from "deno";
@@ -16,8 +16,8 @@ export function renameSync(oldpath: string, newpath: string): void {
}
/**
- * Renames (moves) oldpath to newpath. If newpath already exists
- * and is not a directory, Rename replaces it. OS-specific restrictions may
+ * Renames (moves) oldpath to newpath. If newpath already exists
+ * and is not a directory, Rename replaces it. OS-specific restrictions may
* apply when oldpath and newpath are in different directories.
*
* import { rename } from "deno";
diff --git a/src/handlers.rs b/src/handlers.rs
index 09b4e299a8..326c2f2dd4 100644
--- a/src/handlers.rs
+++ b/src/handlers.rs
@@ -323,27 +323,54 @@ fn handle_fetch_req(d: *const DenoC, base: &msg::Base) -> Box {
let url = url.parse::().unwrap();
let client = Client::new();
- let future = client.get(url).and_then(|res| {
+ let future = client.get(url).and_then(move |res| {
let status = res.status().as_u16() as i32;
+
+ let headers = {
+ let map = res.headers();
+ let keys = map
+ .keys()
+ .map(|s| s.as_str().to_string())
+ .collect::>();
+ let values = map
+ .values()
+ .map(|s| s.to_str().unwrap().to_string())
+ .collect::>();
+ (keys, values)
+ };
+
// TODO Handle streaming body.
- res.into_body().concat2().map(move |body| (status, body))
+ res
+ .into_body()
+ .concat2()
+ .map(move |body| (status, body, headers))
});
let future = future.map_err(|err| -> DenoError { err.into() }).and_then(
- move |(status, body)| {
+ move |(status, body, headers)| {
let builder = &mut FlatBufferBuilder::new();
// Send the first message without a body. This is just to indicate
// what status code.
let body_off = builder.create_vector(body.as_ref());
+ let header_keys: Vec<&str> = headers.0.iter().map(|s| &**s).collect();
+ let header_keys_off =
+ builder.create_vector_of_strings(header_keys.as_slice());
+ let header_values: Vec<&str> = headers.1.iter().map(|s| &**s).collect();
+ let header_values_off =
+ builder.create_vector_of_strings(header_values.as_slice());
+
let msg = msg::FetchRes::create(
builder,
&msg::FetchResArgs {
id,
status,
body: Some(body_off),
+ header_key: Some(header_keys_off),
+ header_value: Some(header_values_off),
..Default::default()
},
);
+
Ok(serialize_response(
cmd_id,
builder,
diff --git a/src/msg.fbs b/src/msg.fbs
index c630fec9d9..e807132366 100644
--- a/src/msg.fbs
+++ b/src/msg.fbs
@@ -154,7 +154,8 @@ table FetchReq {
table FetchRes {
id: uint;
status: int;
- header_line: [string];
+ header_key: [string];
+ header_value: [string];
body: [ubyte];
}