http: support plain http protocol
parent
e35ef3b83e
commit
51818346df
|
@ -134,7 +134,7 @@ void vschannel_init() {
|
||||||
tls_ctx.creds_initialized = TRUE;
|
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;
|
SecBuffer ExtraData;
|
||||||
SECURITY_STATUS Status;
|
SECURITY_STATUS Status;
|
||||||
|
@ -147,6 +147,8 @@ INT request(CHAR *host, CHAR *req, CHAR *out)
|
||||||
|
|
||||||
protocol = SP_PROT_TLS1_2_CLIENT;
|
protocol = SP_PROT_TLS1_2_CLIENT;
|
||||||
|
|
||||||
|
port_number = iport;
|
||||||
|
|
||||||
// Connect to server.
|
// Connect to server.
|
||||||
if(connect_to_server(host, port_number)) {
|
if(connect_to_server(host, port_number)) {
|
||||||
printf("Error connecting to server\n");
|
printf("Error connecting to server\n");
|
||||||
|
|
|
@ -25,7 +25,7 @@ static void vschannel_init();
|
||||||
|
|
||||||
static void vschannel_cleanup();
|
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(
|
static SECURITY_STATUS https_make_request(
|
||||||
CHAR *req, CHAR *out, int *length);
|
CHAR *req, CHAR *out, int *length);
|
||||||
|
|
|
@ -38,7 +38,7 @@ fn init_module() {
|
||||||
//C.OPENSSL_config(0)
|
//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.SSLv23_method()
|
||||||
ssl_method := C.TLSv1_2_method()
|
ssl_method := C.TLSv1_2_method()
|
||||||
if isnil(method) {
|
if isnil(method) {
|
||||||
|
@ -55,7 +55,7 @@ fn ssl_do(method, host_name, path string) Response {
|
||||||
web := C.BIO_new_ssl_connect(ctx)
|
web := C.BIO_new_ssl_connect(ctx)
|
||||||
if isnil(ctx) {
|
if isnil(ctx) {
|
||||||
}
|
}
|
||||||
addr := host_name + ':443'
|
addr := host_name + ':' + port.str()
|
||||||
res = C.BIO_set_conn_hostname(web, addr.str)
|
res = C.BIO_set_conn_hostname(web, addr.str)
|
||||||
if res != 1 {
|
if res != 1 {
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,15 +16,15 @@ import net.urllib
|
||||||
|
|
||||||
fn init_module() {}
|
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()
|
C.vschannel_init()
|
||||||
// TODO: joe-c
|
// TODO: joe-c
|
||||||
// dynamically increase in vschannel.c if needed
|
// dynamically increase in vschannel.c if needed
|
||||||
mut buff := malloc(44000)
|
mut buff := malloc(44000)
|
||||||
|
addr := host_name
|
||||||
req := build_request_headers('', method, host_name, path)
|
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()
|
C.vschannel_cleanup()
|
||||||
return parse_response(string(buff, length))
|
return parse_response(string(buff, length))
|
||||||
}
|
}
|
||||||
|
|
|
@ -103,38 +103,45 @@ pub fn (req &Request) do() ?Response {
|
||||||
for key, val in req.headers {
|
for key, val in req.headers {
|
||||||
//h := '$key: $val'
|
//h := '$key: $val'
|
||||||
}
|
}
|
||||||
url := urllib.parse(req.url) or {
|
url := urllib.parse(req.url) or { return error('http.request.do: invalid URL $req.url') }
|
||||||
return error('http.request.do: invalid URL $req.url')
|
mut rurl := url
|
||||||
// return Response{} //error('ff')}
|
mut resp := Response{}
|
||||||
}
|
|
||||||
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
|
|
||||||
mut no_redirects := 0
|
mut no_redirects := 0
|
||||||
for resp.status_code in [301, 302, 303, 307, 308] {
|
for {
|
||||||
if no_redirects == max_redirects {
|
if no_redirects == max_redirects { return error('http.request.do: maximum number of redirects reached ($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
|
||||||
h_loc := resp.headers['Location']
|
if ! (resp.status_code in [301, 302, 303, 307, 308]) { break }
|
||||||
r_url := urllib.parse(h_loc) or {
|
// follow any redirects
|
||||||
return error('http.request.do: cannot follow redirect, location header has invalid url $h_loc')
|
redirect_url := resp.headers['Location']
|
||||||
}
|
qrurl := urllib.parse( redirect_url ) or { return error('http.request.do: invalid URL in redirect $redirect_url') }
|
||||||
p = r_url.path.trim_left('/')
|
rurl = qrurl
|
||||||
u = if r_url.query().size > 0 { '/$p?${r_url.query().encode()}' } else { '/$p' }
|
|
||||||
resp = ssl_do(req.typ, r_url.hostname(), u)
|
|
||||||
no_redirects++
|
no_redirects++
|
||||||
}
|
}
|
||||||
|
|
||||||
return resp
|
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 {
|
fn parse_response(resp string) Response {
|
||||||
mut headers := map[string]string
|
mut headers := map[string]string
|
||||||
first_header := resp.all_before('\n')
|
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 net.urllib
|
||||||
//import http
|
import http
|
||||||
|
|
||||||
fn test_escape_unescape() {
|
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
|
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
|
// shutdown and close socket
|
||||||
pub fn (s Socket) close() ?int {
|
pub fn (s Socket) close() ?int {
|
||||||
mut shutdown_res := 0
|
mut shutdown_res := 0
|
||||||
|
|
Loading…
Reference in New Issue