1
0
mirror of https://github.com/vlang/v.git synced 2023-08-10 21:13:21 +03:00

http: support plain http protocol

This commit is contained in:
Delyan Angelov 2019-08-21 20:04:06 +03:00 committed by Alexander Medvednikov
parent e35ef3b83e
commit 51818346df
8 changed files with 103 additions and 35 deletions

View File

@ -134,7 +134,7 @@ void vschannel_init() {
tls_ctx.creds_initialized = TRUE;
}
INT request(CHAR *host, CHAR *req, CHAR *out)
INT request(INT iport, CHAR *host, CHAR *req, CHAR *out)
{
SecBuffer ExtraData;
SECURITY_STATUS Status;
@ -147,6 +147,8 @@ INT request(CHAR *host, CHAR *req, CHAR *out)
protocol = SP_PROT_TLS1_2_CLIENT;
port_number = iport;
// Connect to server.
if(connect_to_server(host, port_number)) {
printf("Error connecting to server\n");

View File

@ -25,7 +25,7 @@ static void vschannel_init();
static void vschannel_cleanup();
static INT request(CHAR *host, CHAR *req, CHAR *out);
static INT request(INT iport, CHAR *host, CHAR *req, CHAR *out);
static SECURITY_STATUS https_make_request(
CHAR *req, CHAR *out, int *length);

View File

@ -38,7 +38,7 @@ fn init_module() {
//C.OPENSSL_config(0)
}
fn ssl_do(method, host_name, path string) Response {
fn ssl_do(port int, method, host_name, path string) Response {
//ssl_method := C.SSLv23_method()
ssl_method := C.TLSv1_2_method()
if isnil(method) {
@ -55,7 +55,7 @@ fn ssl_do(method, host_name, path string) Response {
web := C.BIO_new_ssl_connect(ctx)
if isnil(ctx) {
}
addr := host_name + ':443'
addr := host_name + ':' + port.str()
res = C.BIO_set_conn_hostname(web, addr.str)
if res != 1 {
}

View File

@ -16,15 +16,15 @@ import net.urllib
fn init_module() {}
fn ssl_do(method, host_name, path string) Response {
fn ssl_do(port int, method, host_name, path string) Response {
C.vschannel_init()
// TODO: joe-c
// dynamically increase in vschannel.c if needed
mut buff := malloc(44000)
addr := host_name
req := build_request_headers('', method, host_name, path)
length := int(C.request(host_name.str, req.str, buff))
length := int(C.request(port, addr.str, req.str, buff))
C.vschannel_cleanup()
return parse_response(string(buff, length))
}

View File

@ -103,38 +103,45 @@ pub fn (req &Request) do() ?Response {
for key, val in req.headers {
//h := '$key: $val'
}
url := urllib.parse(req.url) or {
return error('http.request.do: invalid URL $req.url')
// return Response{} //error('ff')}
}
is_ssl := url.scheme == 'https'
if !is_ssl {
return error('non https requests are not supported right now')
}
// first request
mut p := url.path.trim_left('/')
mut u := if url.query().size > 0 { '/$p?${url.query().encode()}' } else { '/$p' }
mut resp := ssl_do(req.typ, url.hostname(), u)
// follow any redirects
url := urllib.parse(req.url) or { return error('http.request.do: invalid URL $req.url') }
mut rurl := url
mut resp := Response{}
mut no_redirects := 0
for resp.status_code in [301, 302, 303, 307, 308] {
if no_redirects == max_redirects {
return error('http.request.do: maximum number of redirects reached ($max_redirects)')
}
h_loc := resp.headers['Location']
r_url := urllib.parse(h_loc) or {
return error('http.request.do: cannot follow redirect, location header has invalid url $h_loc')
}
p = r_url.path.trim_left('/')
u = if r_url.query().size > 0 { '/$p?${r_url.query().encode()}' } else { '/$p' }
resp = ssl_do(req.typ, r_url.hostname(), u)
for {
if no_redirects == max_redirects { return error('http.request.do: maximum number of redirects reached ($max_redirects)') }
qresp := method_and_url_to_response( req.typ, rurl ) or { return error(err) }
resp = qresp
if ! (resp.status_code in [301, 302, 303, 307, 308]) { break }
// follow any redirects
redirect_url := resp.headers['Location']
qrurl := urllib.parse( redirect_url ) or { return error('http.request.do: invalid URL in redirect $redirect_url') }
rurl = qrurl
no_redirects++
}
return resp
}
fn method_and_url_to_response(method string, url net_dot_urllib.URL) ?Response {
host_name := url.hostname()
scheme := url.scheme
mut p := url.path.trim_left('/')
mut path := if url.query().size > 0 { '/$p?${url.query().encode()}' } else { '/$p' }
mut nport := url.port().int()
if nport == 0 {
if scheme == 'http' { nport = 80 }
if scheme == 'https' { nport = 443 }
}
//println('fetch $method, $scheme, $host_name, $nport, $path ')
if scheme == 'https' {
//println('ssl_do( $nport, $method, $host_name, $path )')
return ssl_do( nport, method, host_name, path )
} else if scheme == 'http' {
//println('http_do( $nport, $method, $host_name, $path )')
return http_do( nport, method, host_name, path )
}
return error('http.request.do: unsupported scheme: $scheme')
}
fn parse_response(resp string) Response {
mut headers := map[string]string
first_header := resp.all_before('\n')

22
vlib/http/http_client.v Normal file
View File

@ -0,0 +1,22 @@
module http
import net
import strings
fn http_do(port int, method, host_name, path string) ?Response {
bufsize := 512
rbuffer := [512]byte
mut sb := strings.new_builder(100)
s := build_request_headers('', method, host_name, path)
client := net.dial( host_name, port) or { return error(err) }
client.send( s.str, s.len )
for {
readbytes := client.crecv( rbuffer, bufsize )
if readbytes < 0 { return error('http_do error reading response. readbytes: $readbytes') }
if readbytes == 0 { break }
sb.write( tos(rbuffer, readbytes) )
}
client.close()
return parse_response(sb.str())
}

View File

@ -1,5 +1,5 @@
import net.urllib
//import http
import http
fn test_escape_unescape() {
/*
@ -19,3 +19,32 @@ fn test_http_get() {
*/
}
fn test_http_get_from_vlang_utc_now() {
urls := ['http://vlang.io/utc_now', 'https://vlang.io/utc_now']
for url in urls {
println('Test getting current time from $url by http.get')
res := http.get(url) or { panic(err) }
assert 200 == res.status_code
assert res.text.len > 0
assert res.text.int() > 1566403696
println('Current time is: ${res.text.int()}')
}
}
fn test_public_servers() {
urls := [
'http://github.com/robots.txt',
'http://google.com/robots.txt',
'http://yahoo.com/robots.txt',
'https://github.com/robots.txt',
'https://google.com/robots.txt',
'https://yahoo.com/robots.txt',
]
for url in urls {
println('Testing http.get on public url: $url ')
res := http.get( url ) or { panic(err) }
assert 200 == res.status_code
assert res.text.len > 0
}
}

View File

@ -231,6 +231,14 @@ pub fn (s Socket) recv(bufsize int) byteptr {
return buf
}
// TODO: remove cread/2 and crecv/2 when the Go net interface is done
pub fn (s Socket) cread( buffer byteptr, buffersize int ) int {
return int( C.read(s.sockfd, buffer, buffersize) )
}
pub fn (s Socket) crecv( buffer byteptr, buffersize int ) int {
return int( C.recv(s.sockfd, buffer, buffersize, 0) )
}
// shutdown and close socket
pub fn (s Socket) close() ?int {
mut shutdown_res := 0