From 0ad5d53423f06d3fcad457f44ab92455543c1e94 Mon Sep 17 00:00:00 2001 From: Delyan Angelov Date: Wed, 12 Feb 2020 15:52:39 +0200 Subject: [PATCH] net.http: more robust handling of relative /path URL redirects --- vlib/net/ftp/ftp.v | 1 + vlib/net/http/http.v | 10 ++++++++-- vlib/net/http/http_test.v | 9 +++++++++ vlib/net/socket.v | 5 +++-- vlib/net/urllib/urllib.v | 7 ++++--- 5 files changed, 25 insertions(+), 7 deletions(-) diff --git a/vlib/net/ftp/ftp.v b/vlib/net/ftp/ftp.v index 10d10fdf54..d6a1efa806 100644 --- a/vlib/net/ftp/ftp.v +++ b/vlib/net/ftp/ftp.v @@ -50,6 +50,7 @@ fn (dtp DTP) read() []byte { for i := 0; i < len; i++ { data << buf[i] } + unsafe { free(buf) } } return data diff --git a/vlib/net/http/http.v b/vlib/net/http/http.v index 178659abff..7ecb12f135 100644 --- a/vlib/net/http/http.v +++ b/vlib/net/http/http.v @@ -193,7 +193,7 @@ pub fn parse_headers(lines []string) map[string]string { // do will send the HTTP request and returns `http.Response` as soon as the response is recevied pub fn (req &Request) do() ?Response { - url := urllib.parse(req.url) or { + mut url := urllib.parse(req.url) or { return error('http.Request.do: invalid url ${req.url}') } mut rurl := url @@ -211,7 +211,13 @@ pub fn (req &Request) do() ?Response { break } // follow any redirects - redirect_url := resp.headers['Location'] + mut redirect_url := resp.headers['Location'] + if redirect_url.len > 0 && redirect_url[0] == `/` { + url.set_path(redirect_url) or { + return error('http.request.do: invalid path in redirect: "$redirect_url"') + } + redirect_url = url.str() + } qrurl := urllib.parse(redirect_url) or { return error('http.request.do: invalid URL in redirect "$redirect_url"') } diff --git a/vlib/net/http/http_test.v b/vlib/net/http/http_test.v index ad76e8abe2..705c8e0a3d 100644 --- a/vlib/net/http/http_test.v +++ b/vlib/net/http/http_test.v @@ -36,3 +36,12 @@ fn test_public_servers() { assert res.text.len > 0 } } + +fn test_relative_redirects() { + $if !network ? { return } + res := http.get('https://httpbin.org/relative-redirect/3?abc=xyz') or { panic(err) } + assert 200 == res.status_code + assert res.text.len > 0 + assert res.text.contains('"abc": "xyz"') +} + diff --git a/vlib/net/socket.v b/vlib/net/socket.v index c216a31bdf..20ac95316d 100644 --- a/vlib/net/socket.v +++ b/vlib/net/socket.v @@ -255,9 +255,10 @@ pub fn (s Socket) send_string(sdata string) ?int { return s.send(sdata.str, sdata.len) } -// receive string data from socket +// receive string data from socket. NB: you are responsible for freeing the returned byteptr pub fn (s Socket) recv(bufsize int) (byteptr,int) { - buf := malloc(bufsize) + mut buf := byteptr(0) + unsafe { buf = malloc(bufsize) } res := C.recv(s.sockfd, buf, bufsize, 0) return buf,res } diff --git a/vlib/net/urllib/urllib.v b/vlib/net/urllib/urllib.v index 189f9171cd..e35e3cd453 100644 --- a/vlib/net/urllib/urllib.v +++ b/vlib/net/urllib/urllib.v @@ -326,6 +326,7 @@ pub mut: raw_query string // encoded query values, without '?' fragment string // fragment for references, without '#' } + // user returns a Userinfo containing the provided username // and no password set. pub fn user(username string) &Userinfo { @@ -650,7 +651,7 @@ fn parse_host(host string) ?string { // - set_path('/foo%2fbar') will set path='/foo/bar' and raw_path='/foo%2fbar' // set_path will return an error only if the provided path contains an invalid // escaping. -fn (u mut URL) set_path(p string) ?bool { +pub fn (u mut URL) set_path(p string) ?bool { path := unescape(p, .encode_path) or { return error(err) } @@ -755,7 +756,7 @@ fn valid_optional_port(port string) bool { // the form host/path does not add its own /. // - if u.raw_query is empty, ?query is omitted. // - if u.fragment is empty, #fragment is omitted. -pub fn (u &URL) str() string { +pub fn (u URL) str() string { mut buf := strings.new_builder(200) if u.scheme != '' { buf.write(u.scheme) @@ -765,7 +766,7 @@ pub fn (u &URL) str() string { buf.write(u.opaque) } else { - if u.scheme != '' || u.host != '' || !u.user.empty() { + if u.scheme != '' || u.host != '' || (u.user != 0 && !u.user.empty()) { if u.host != '' || u.path != '' || !u.user.empty() { buf.write('//') }