Skip to content

Commit

Permalink
fix: something
Browse files Browse the repository at this point in the history
  • Loading branch information
keroxp committed Mar 16, 2019
1 parent 8e3ffdc commit 76f3895
Show file tree
Hide file tree
Showing 6 changed files with 154 additions and 264 deletions.
30 changes: 14 additions & 16 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
# deno-request

![https://travis-ci.org/keroxp/deno-request.svg?branch=master](https://travis-ci.org/keroxp/deno-request.svg?branch=master)

An experimental implementation of http request client based on `dial` for deno
Expand All @@ -12,43 +13,40 @@ Http requester based on [dial](https://deno.land/typedoc/index.html#dial). Curre
## GET

```main.ts
import {request} from "https://denopkg.com/keroxp/deno-request/request.ts"
import {readString, StringReader} from "https://denopkg.com/keroxp/deno-request/strings.ts"
import {request} from "https://denopkg.com/keroxp/deno-request@v0.2.0/request.ts"
// GET
const {status, headers, body} = await request("http://httpbin.org/get?deno=land");
const str = await readString(new StringReader(body))
const json = JSON.parse(str)
const buf = new Deno.Buffer();
await Deno.copy(buf, body);
const json = JSON.parse(buf.toString())

```

## POST

```main.ts
import {request} from "https://denopkg.com/keroxp/deno-request/request.ts"
import {readString, StringReader} from "https://denopkg.com/keroxp/deno-request/strings.ts"
import {request} from "https://denopkg.com/keroxp/deno-request@v0.2.0/request.ts"
// POST
const bodyText = "wayway"
const {status, headers, body} = await request({
url: "http://httpbin.org/post",
method: "POST",
body: new StringReader(bodyText);,
bodySize: bodyText.length, // optional
body: new TextEncoder().encode("wayway");,
headers: new Headers({
"Content-Type": "application/json"
})
});
const str = await readString(new StringReader(body))
const json = JSON.parse(str)
const buf = new Deno.Buffer();
await Deno.copy(buf, body);
const json = JSON.parse(buf.toString())
json["data"]; // wayway
```

## Download file

```main.ts
import {request} from "https://denopkg.com/keroxp/deno-request/request.ts"
import {open, copy} from "deno"
import {request} from "https://denopkg.com/keroxp/deno-request@v0.2.0/request.ts"
const {body} = await request("http://httpbin.org/get?deno=land");
const f = await open("out.json")
await copy(f, body)
f.close()
const f = await Deno.open("out.json")
await Deno.copy(f, body)
f.close()
```
104 changes: 0 additions & 104 deletions reader.ts

This file was deleted.

142 changes: 133 additions & 9 deletions request.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,42 @@
import { dial } from "deno";
import { HttpRequest, writeHttpRequest } from "./writer.ts";
import { HttpResponse, readHttpResponse } from "./reader.ts";
import Buffer = Deno.Buffer;

const { dial } = Deno;
import { BufReader, BufWriter } from "https://deno.land/std@v0.3.1/io/bufio.ts";
import { TextProtoReader } from "https://deno.land/std@v0.3.1/textproto/mod.ts";
import {
BodyReader,
ChunkedBodyReader
} from "https://denopkg.com/keroxp/servest@v0.1.1/readers.ts";
import Reader = Deno.Reader;
import Writer = Deno.Writer;
import Conn = Deno.Conn;

const kPortMap = {
"http:": "80",
"https:": "443"
};

const encoder = new TextEncoder();

export type HttpRequest = {
method: string;
url: string;
headers?: Headers;
body?: Reader | Uint8Array;
basicAuth?: {
username: string;
password: string;
};
};

export type HttpResponse = {
status: number;
statusText: string;
headers: Headers;
body: Reader;
conn: Conn;
};

function normalizeRequest(params: string | HttpRequest) {
let req: HttpRequest;
let url: URL;
Expand All @@ -29,11 +59,105 @@ export async function request(
): Promise<HttpResponse> {
const { url, req } = normalizeRequest(params);
const conn = await dial("tcp", `${url.hostname}:${url.port}`);
try {
await writeHttpRequest(conn, req);
return readHttpResponse(conn, conn);
} catch (e) {
conn.close();
throw e;
await writeHttpRequest(conn, req);
const res = await readHttpResponse(conn);
return Object.assign(res, { conn });
}

async function writeHttpRequest(w: Writer, opts: HttpRequest) {
const writer = new BufWriter(w);
const { method, basicAuth, body } = opts;
const url = new URL(opts.url);
let { headers } = opts;
if (!headers) {
headers = new Headers();
}
// start line
const lines = [`${method} ${url.pathname}${url.search || ""} HTTP/1.1`];
// header
if (!headers.has("Host")) {
headers.set("Host", url.host);
}
if (basicAuth && !headers.has("Authorization")) {
const { username, password } = basicAuth;
const base64 = btoa(`${username}:${password}`);
headers.set("Authorization", `Basic ${base64}`);
}
let hasContentLength = body instanceof Uint8Array;
if (body) {
if (body instanceof Uint8Array) {
if (!headers.has("Content-Length")) {
headers.set("Content-Length", `${body.byteLength}`);
} else if (headers.get("Content-Length") !== `${body.byteLength}`) {
throw new RangeError("");
}
} else {
headers.set("Transfer-Encoding", "chunked");
}
}
for (const [key, value] of headers) {
lines.push(`${key}: ${value}`);
}
lines.push("\r\n");
const headerText = lines.join("\r\n");
await writer.write(encoder.encode(headerText));
await writer.flush();
if (body) {
const reader = body instanceof Uint8Array ? new Buffer(body) : body;
const buf = new Uint8Array(1024);
while (true) {
const { nread, eof } = await reader.read(buf);
if (nread > 0) {
const chunk = buf.slice(0, nread);
if (hasContentLength) {
await writer.write(chunk);
} else {
const size = chunk.byteLength.toString(16);
await writer.write(encoder.encode(`${size}\r\n`));
await writer.write(chunk);
await writer.write(encoder.encode("\r\n"));
}
await writer.flush();
}
if (eof) {
if (!hasContentLength) {
await writer.write(encoder.encode("0\r\n\r\n"));
await writer.flush();
}
break;
}
}
}
}

async function readHttpResponse(
r: Reader
): Promise<{
status: number;
statusText: string;
headers: Headers;
body: Reader;
}> {
const reader = new BufReader(r);
const tpReader = new TextProtoReader(reader);
// read status line
const [resLine, state] = await tpReader.readLine();
const [m, _, status, statusText] = resLine.match(/^([^ ]+)? (\d{3}) (.+?)$/);
// read header
const [headers] = await tpReader.readMIMEHeader();
// read body
const resContentLength = headers.get("content-length");
const contentLength = parseInt(resContentLength);
let bodyReader: Reader;
if (headers.get("transfer-encoding") !== "chunked") {
bodyReader = new BodyReader(reader, contentLength);
} else {
bodyReader = new ChunkedBodyReader(reader);
}
return {
status: parseInt(status),
statusText,
headers,
body: bodyReader
};
}
20 changes: 7 additions & 13 deletions request_test.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,11 @@
import {
assert,
assertEquals
} from "https://deno.land/std@v0.3.1/testing/asserts.ts";
import {
runIfMain,
runTests,
test
} from "https://deno.land/std@v0.3.1/testing/mod.ts";
import { StringReader } from "https://deno.land/std@v0.3.1/io/readers.ts";
import { request } from "./request.ts";
import {assert, assertEquals} from "https://deno.land/std@v0.3.1/testing/asserts.ts";
import {runIfMain, test} from "https://deno.land/std@v0.3.1/testing/mod.ts";
import {StringReader} from "https://deno.land/std@v0.3.1/io/readers.ts";
import {request} from "./request.ts";
import {encode} from "https://deno.land/std@v0.3.1/strings/strings.ts";
import Buffer = Deno.Buffer;
import Reader = Deno.Reader;

async function readString(r: Reader) {
const buf = new Buffer();
await Deno.copy(buf, r);
Expand All @@ -36,8 +31,7 @@ test(async function testRequestPost() {
const { status, headers, body } = await request({
url: "http://httpbin.org/post",
method: "POST",
body: new StringReader("wayway"),
bodySize: 6
body: encode("wayway"),
});
assertEquals(status, 200);
assertEquals(headers.has("content-type"), true);
Expand Down
39 changes: 0 additions & 39 deletions strings.ts

This file was deleted.

Loading

0 comments on commit 76f3895

Please sign in to comment.