net::http: export new_request{,_body}
This commit is contained in:
parent
380d174beb
commit
2acf7fa873
|
@ -7,37 +7,44 @@ use net::uri;
|
||||||
use os;
|
use os;
|
||||||
use strings;
|
use strings;
|
||||||
|
|
||||||
|
const usage: [_]getopt::help = [
|
||||||
|
"HTTP client",
|
||||||
|
('H', "Name:value", "Sets an HTTP header"),
|
||||||
|
"url"
|
||||||
|
];
|
||||||
|
|
||||||
export fn main() void = {
|
export fn main() void = {
|
||||||
const client = http::newclient("Hare net::http test client");
|
const client = http::newclient("Hare net::http test client");
|
||||||
defer http::client_finish(&client);
|
defer http::client_finish(&client);
|
||||||
|
|
||||||
const cmd = getopt::parse(os::args,
|
const cmd = getopt::parse(os::args, usage...);
|
||||||
"HTTP client",
|
|
||||||
('H', "Name:value", "Sets an HTTP header"),
|
|
||||||
"url");
|
|
||||||
defer getopt::finish(&cmd);
|
defer getopt::finish(&cmd);
|
||||||
|
|
||||||
let head = http::client_default_header(&client);
|
if (len(cmd.args) != 1) {
|
||||||
for (let i = 0z; i < len(cmd.opts); i += 1) {
|
getopt::printusage(os::stderr, "http", usage)!;
|
||||||
const (opt, val) = cmd.opts[i];
|
os::exit(os::status::FAILURE);
|
||||||
switch (opt) {
|
|
||||||
case 'H' =>
|
|
||||||
const (name, val) = strings::cut(val, ":");
|
|
||||||
http::header_add(head, name, val);
|
|
||||||
case => abort();
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const target = cmd.args[0];
|
const targ = match (uri::parse(cmd.args[0])) {
|
||||||
const target = match (uri::parse(target)) {
|
|
||||||
case let u: uri::uri =>
|
case let u: uri::uri =>
|
||||||
yield u;
|
yield u;
|
||||||
case uri::invalid =>
|
case uri::invalid =>
|
||||||
log::fatal("Invalid URI");
|
log::fatal("Invalid URI");
|
||||||
};
|
};
|
||||||
defer uri::finish(&target);
|
defer uri::finish(&targ);
|
||||||
|
|
||||||
const resp = match (http::get(&client, &target)) {
|
let req = http::new_request(&client, "GET", &targ)!;
|
||||||
|
for (let i = 0z; i < len(cmd.opts); i += 1) {
|
||||||
|
const (opt, val) = cmd.opts[i];
|
||||||
|
switch (opt) {
|
||||||
|
case 'H' =>
|
||||||
|
const (name, val) = strings::cut(val, ":");
|
||||||
|
http::header_add(&req.header, name, val);
|
||||||
|
case => abort();
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
const resp = match (http::do(&client, &req)) {
|
||||||
case let err: http::error =>
|
case let err: http::error =>
|
||||||
log::fatal("HTTP error:", http::strerror(err));
|
log::fatal("HTTP error:", http::strerror(err));
|
||||||
case let resp: http::response =>
|
case let resp: http::response =>
|
||||||
|
|
|
@ -1,10 +1,5 @@
|
||||||
use errors;
|
|
||||||
use fmt;
|
|
||||||
use io;
|
use io;
|
||||||
use net::ip;
|
|
||||||
use net::uri;
|
use net::uri;
|
||||||
use strconv;
|
|
||||||
use strings;
|
|
||||||
|
|
||||||
export type client = struct {
|
export type client = struct {
|
||||||
default_header: header,
|
default_header: header,
|
||||||
|
@ -44,76 +39,6 @@ export fn client_default_transport(client: *client) *transport = {
|
||||||
return &client.default_transport;
|
return &client.default_transport;
|
||||||
};
|
};
|
||||||
|
|
||||||
fn new_request(
|
|
||||||
client: *client,
|
|
||||||
method: str,
|
|
||||||
target: *uri::uri,
|
|
||||||
) (request | errors::unsupported) = {
|
|
||||||
let req = request {
|
|
||||||
method = method,
|
|
||||||
target = alloc(uri::dup(target)),
|
|
||||||
header = header_dup(&client.default_header),
|
|
||||||
transport = null,
|
|
||||||
body = void,
|
|
||||||
};
|
|
||||||
switch (req.target.scheme) {
|
|
||||||
case "http" =>
|
|
||||||
if (req.target.port == 0) {
|
|
||||||
req.target.port = 80;
|
|
||||||
};
|
|
||||||
case "https" =>
|
|
||||||
if (req.target.port == 0) {
|
|
||||||
req.target.port = 443;
|
|
||||||
};
|
|
||||||
case =>
|
|
||||||
return errors::unsupported;
|
|
||||||
};
|
|
||||||
|
|
||||||
let host = match (req.target.host) {
|
|
||||||
case let host: str =>
|
|
||||||
yield host;
|
|
||||||
case let ip: ip::addr4 =>
|
|
||||||
yield ip::string(ip);
|
|
||||||
case let ip: ip::addr6 =>
|
|
||||||
static let buf: [64 + 2]u8 = [0...];
|
|
||||||
yield fmt::bsprintf(buf, "[{}]", ip::string(ip));
|
|
||||||
};
|
|
||||||
|
|
||||||
if (req.target.scheme == "http" && req.target.port != 80) {
|
|
||||||
host = fmt::asprintf("{}:{}", host, req.target.port);
|
|
||||||
} else if (target.scheme == "https" && target.port != 443) {
|
|
||||||
host = fmt::asprintf("{}:{}", host, req.target.port);
|
|
||||||
} else {
|
|
||||||
host = strings::dup(host);
|
|
||||||
};
|
|
||||||
defer free(host);
|
|
||||||
header_add(&req.header, "Host", host);
|
|
||||||
|
|
||||||
return req;
|
|
||||||
};
|
|
||||||
|
|
||||||
fn new_request_body(
|
|
||||||
client: *client,
|
|
||||||
method: str,
|
|
||||||
target: *uri::uri,
|
|
||||||
body: io::handle,
|
|
||||||
) (request | errors::unsupported) = {
|
|
||||||
let req = new_request(client, method, target)?;
|
|
||||||
req.body = body;
|
|
||||||
|
|
||||||
const offs = match (io::seek(body, 0, io::whence::CUR)) {
|
|
||||||
case let off: io::off =>
|
|
||||||
yield off;
|
|
||||||
case io::error =>
|
|
||||||
header_add(&req.header, "Transfer-Encoding", "chunked");
|
|
||||||
return req;
|
|
||||||
};
|
|
||||||
const ln = io::seek(body, 0, io::whence::END)!;
|
|
||||||
io::seek(body, offs, io::whence::SET)!;
|
|
||||||
header_add(&req.header, "Content-Length", strconv::ztos(ln: size));
|
|
||||||
return req;
|
|
||||||
};
|
|
||||||
|
|
||||||
fn uri_origin_form(target: *uri::uri) uri::uri = {
|
fn uri_origin_form(target: *uri::uri) uri::uri = {
|
||||||
let target = *target;
|
let target = *target;
|
||||||
target.scheme = "";
|
target.scheme = "";
|
||||||
|
|
|
@ -1,5 +1,10 @@
|
||||||
|
use errors;
|
||||||
|
use fmt;
|
||||||
use io;
|
use io;
|
||||||
|
use net::ip;
|
||||||
use net::uri;
|
use net::uri;
|
||||||
|
use strconv;
|
||||||
|
use strings;
|
||||||
|
|
||||||
// Stores state related to an HTTP request.
|
// Stores state related to an HTTP request.
|
||||||
//
|
//
|
||||||
|
@ -33,3 +38,78 @@ export fn request_finish(req: *request) void = {
|
||||||
uri::finish(req.target);
|
uri::finish(req.target);
|
||||||
free(req.target);
|
free(req.target);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Creates a new HTTP [[request]] using the given HTTP [[client]] defaults.
|
||||||
|
export fn new_request(
|
||||||
|
client: *client,
|
||||||
|
method: str,
|
||||||
|
target: *uri::uri,
|
||||||
|
) (request | errors::unsupported) = {
|
||||||
|
let req = request {
|
||||||
|
method = method,
|
||||||
|
target = alloc(uri::dup(target)),
|
||||||
|
header = header_dup(&client.default_header),
|
||||||
|
transport = null,
|
||||||
|
body = void,
|
||||||
|
};
|
||||||
|
switch (req.target.scheme) {
|
||||||
|
case "http" =>
|
||||||
|
if (req.target.port == 0) {
|
||||||
|
req.target.port = 80;
|
||||||
|
};
|
||||||
|
case "https" =>
|
||||||
|
if (req.target.port == 0) {
|
||||||
|
req.target.port = 443;
|
||||||
|
};
|
||||||
|
case =>
|
||||||
|
return errors::unsupported;
|
||||||
|
};
|
||||||
|
|
||||||
|
let host = match (req.target.host) {
|
||||||
|
case let host: str =>
|
||||||
|
yield host;
|
||||||
|
case let ip: ip::addr4 =>
|
||||||
|
yield ip::string(ip);
|
||||||
|
case let ip: ip::addr6 =>
|
||||||
|
static let buf: [64 + 2]u8 = [0...];
|
||||||
|
yield fmt::bsprintf(buf, "[{}]", ip::string(ip));
|
||||||
|
};
|
||||||
|
|
||||||
|
if (req.target.scheme == "http" && req.target.port != 80) {
|
||||||
|
host = fmt::asprintf("{}:{}", host, req.target.port);
|
||||||
|
} else if (target.scheme == "https" && target.port != 443) {
|
||||||
|
host = fmt::asprintf("{}:{}", host, req.target.port);
|
||||||
|
} else {
|
||||||
|
host = strings::dup(host);
|
||||||
|
};
|
||||||
|
defer free(host);
|
||||||
|
header_add(&req.header, "Host", host);
|
||||||
|
return req;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Creates a new HTTP [[request]] using the given HTTP [[client]] defaults and
|
||||||
|
// the provided request body.
|
||||||
|
//
|
||||||
|
// If the provided I/O handle is seekable, the Content-Length header is added
|
||||||
|
// automatically. Otherwise, Transfer-Encoding: chunked will be used.
|
||||||
|
export fn new_request_body(
|
||||||
|
client: *client,
|
||||||
|
method: str,
|
||||||
|
target: *uri::uri,
|
||||||
|
body: io::handle,
|
||||||
|
) (request | errors::unsupported) = {
|
||||||
|
let req = new_request(client, method, target)?;
|
||||||
|
req.body = body;
|
||||||
|
|
||||||
|
const offs = match (io::seek(body, 0, io::whence::CUR)) {
|
||||||
|
case let off: io::off =>
|
||||||
|
yield off;
|
||||||
|
case io::error =>
|
||||||
|
header_add(&req.header, "Transfer-Encoding", "chunked");
|
||||||
|
return req;
|
||||||
|
};
|
||||||
|
const ln = io::seek(body, 0, io::whence::END)!;
|
||||||
|
io::seek(body, offs, io::whence::SET)!;
|
||||||
|
header_add(&req.header, "Content-Length", strconv::ztos(ln: size));
|
||||||
|
return req;
|
||||||
|
};
|
||||||
|
|
Loading…
Reference in a new issue