http: support plain http protocol
parent
e35ef3b83e
commit
51818346df
|
@ -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");
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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 {
|
||||
}
|
||||
|
|
|
@ -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))
|
||||
}
|
||||
|
|
|
@ -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')
|
||||
|
|
|
@ -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())
|
||||
}
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue