From 56566ba3d0c91fb340a49e3b1d74f1b05b85115f Mon Sep 17 00:00:00 2001 From: joe-conigliaro Date: Sat, 17 Aug 2019 22:50:47 +1000 Subject: [PATCH] http: follow redirects (openssl & schannel) + fix url params --- vlib/http/backend_nix.v | 2 +- vlib/http/backend_win.v | 25 ++++--------------------- vlib/http/http.v | 30 ++++++++++++++++++++++++++---- 3 files changed, 31 insertions(+), 26 deletions(-) diff --git a/vlib/http/backend_nix.v b/vlib/http/backend_nix.v index ab1aad4496..706fa28106 100644 --- a/vlib/http/backend_nix.v +++ b/vlib/http/backend_nix.v @@ -93,5 +93,5 @@ fn ssl_do(method, host_name, path string) Response { C.SSL_CTX_free(ctx) } - return parse_response(sb.str() ) + return parse_response(sb.str()) } diff --git a/vlib/http/backend_win.v b/vlib/http/backend_win.v index 5ec18340e6..6bcd54877c 100644 --- a/vlib/http/backend_win.v +++ b/vlib/http/backend_win.v @@ -14,10 +14,6 @@ import net.urllib #include "vschannel.c" -const ( - max_redirects = 4 -) - fn init_module() {} fn ssl_do(method, host_name, path string) Response { @@ -26,22 +22,9 @@ fn ssl_do(method, host_name, path string) Response { // dynamically increase in vschannel.c if needed mut buff := malloc(44000) - mut p := if path == '' { '/' } else { path } - mut req := build_request_headers('', method, host_name, p) - mut length := int(C.request(host_name.str, req.str, buff)) - mut resp := parse_response(string(buff, length)) + p := if path == '' { '/' } else { path } + req := build_request_headers('', method, host_name, p) + length := int(C.request(host_name.str, req.str, buff)) - mut no_redirects := 0 - for resp.status_code == 301 && no_redirects <= max_redirects { - u := urllib.parse(resp.headers['Location']) or { break } - p = if u.path == '' { '/' } else { u.path } - req = build_request_headers('', method, u.hostname(), p) - length = int(C.request(u.hostname().str, req.str, buff)) - resp = parse_response(string(buff, length)) - no_redirects++ - } - - free(buff) - C.vschannel_cleanup() - return resp + return parse_response(string(buff, length)) } diff --git a/vlib/http/http.v b/vlib/http/http.v index 69ca0b68c0..5404c690cb 100644 --- a/vlib/http/http.v +++ b/vlib/http/http.v @@ -7,6 +7,10 @@ module http import net.urllib import http.chunked +const ( + max_redirects = 4 +) + struct Request { pub: headers2 []string @@ -100,15 +104,33 @@ pub fn (req &Request) do() Response { //h := '$key: $val' } url := urllib.parse(req.url) or { - // panic('http.request.do: invalid URL $req.url' - return Response{} //error('ff')} + panic('http.request.do: invalid URL $req.url') + // return Response{} //error('ff')} } is_ssl := url.scheme == 'https' if !is_ssl { panic('non https requests are not supported right now') } - - return ssl_do(req.typ, url.hostname(), url.path) + + // first request + mut u := if url.query().size > 0 { '$url.path?${url.query().encode()}' } else { url.path } + mut resp := ssl_do(req.typ, url.hostname(), u) + // follow any redirects + mut no_redirects := 0 + for resp.status_code in [301, 302, 303, 307 ,308] { + if no_redirects == max_redirects { + panic('http.request.do: maximum number of redirects reached ($max_redirects)') + } + h_loc := resp.headers['Location'] + r_url := urllib.parse(h_loc) or { + panic('http.request.do: cannot follow redirect, location header has invalid url $h_loc') + } + u = if r_url.query().size > 0 { '$r_url.path?${r_url.query().encode()}' } else { r_url.path } + resp = ssl_do(req.typ, r_url.hostname(), u) + no_redirects++ + } + + return resp } fn parse_response(resp string) Response {