run vfmt on http, net, sync, strconv

pull/3182/head
Alexander Medvednikov 2019-12-22 01:41:42 +03:00
parent 28ecfb231d
commit 848cd3cb3e
18 changed files with 523 additions and 404 deletions

View File

@ -1,7 +1,6 @@
// Copyright (c) 2019 Alexander Medvednikov. All rights reserved. // Copyright (c) 2019 Alexander Medvednikov. All rights reserved.
// Use of this source code is governed by an MIT license // Use of this source code is governed by an MIT license
// that can be found in the LICENSE file. // that can be found in the LICENSE file.
import http import http
import json import json
import sync import sync
@ -64,10 +63,13 @@ fn main() {
} }
ids = tmp ids = tmp
} }
wg := sync.new_waitgroup() wg := sync.new_waitgroup()
mtx := sync.new_mutex() mtx := sync.new_mutex()
mut fetcher := &Fetcher{ids: ids mu: 0 wg: 0} mut fetcher := &Fetcher{
ids: ids
mu: 0
wg: 0
}
fetcher.mu = &mtx fetcher.mu = &mtx
fetcher.wg = &wg fetcher.wg = &wg
fetcher.wg.add(ids.len) fetcher.wg.add(ids.len)

View File

@ -181,9 +181,10 @@ fn (p mut Parser) fnext() {
comment := comment_token.lit comment := comment_token.lit
// Newline before the comment, but not between two // comments, // Newline before the comment, but not between two // comments,
// and not right after `{`, there's already a newline there // and not right after `{`, there's already a newline there
if i > 0 && p.tokens[i-1].tok != .line_comment && if i > 0 && ((p.tokens[i-1].tok != .line_comment &&
p.tokens[i-1].tok != .lcbr && p.tokens[i-1].tok != .lcbr &&
comment_token.line_nr > p.tokens[i-1].line_nr { comment_token.line_nr > p.tokens[i-1].line_nr) ||
p.tokens[i-1].tok == .hash) { // TODO not sure why this is needed, newline wasn't added after a hash
p.fgen_nl() p.fgen_nl()
} }
if i > 0 && p.tokens[i-1].tok == .rcbr && p.scanner.fmt_indent == 0 { if i > 0 && p.tokens[i-1].tok == .rcbr && p.scanner.fmt_indent == 0 {

View File

@ -1,18 +1,15 @@
// Copyright (c) 2019 Alexander Medvednikov. All rights reserved. // Copyright (c) 2019 Alexander Medvednikov. All rights reserved.
// Use of this source code is governed by an MIT license // Use of this source code is governed by an MIT license
// that can be found in the LICENSE file. // that can be found in the LICENSE file.
module http module http
import strings import strings
// On linux, prefer a localy build openssl, because it is // On linux, prefer a localy build openssl, because it is
// much more likely for it to be newer, than the system // much more likely for it to be newer, than the system
// openssl from libssl-dev. If there is no local openssl, // openssl from libssl-dev. If there is no local openssl,
// the next flag is harmless, since it will still use the // the next flag is harmless, since it will still use the
// (older) system openssl. // (older) system openssl.
#flag linux -I/usr/local/include/openssl -L/usr/local/lib #flag linux -I/usr/local/include/openssl -L/usr/local/lib
#flag -l ssl -l crypto #flag -l ssl -l crypto
// MacPorts // MacPorts
#flag darwin -I/opt/local/include #flag darwin -I/opt/local/include
@ -20,33 +17,67 @@ import strings
// Brew // Brew
#flag darwin -I/usr/local/opt/openssl/include #flag darwin -I/usr/local/opt/openssl/include
#flag darwin -L/usr/local/opt/openssl/lib #flag darwin -L/usr/local/opt/openssl/lib
#include <openssl/ssl.h> #include <openssl/ssl.h>
struct C.SSL { struct C.SSL {
} }
fn C.SSL_library_init() fn C.SSL_library_init()
fn C.TLSv1_2_method() voidptr fn C.TLSv1_2_method() voidptr
fn C.SSL_CTX_set_options() fn C.SSL_CTX_set_options()
fn C.SSL_CTX_new() voidptr fn C.SSL_CTX_new() voidptr
fn C.SSL_CTX_set_verify_depth() fn C.SSL_CTX_set_verify_depth()
fn C.SSL_CTX_load_verify_locations() int fn C.SSL_CTX_load_verify_locations() int
fn C.BIO_new_ssl_connect() voidptr fn C.BIO_new_ssl_connect() voidptr
fn C.BIO_set_conn_hostname() int fn C.BIO_set_conn_hostname() int
fn C.BIO_get_ssl() fn C.BIO_get_ssl()
fn C.SSL_set_cipher_list() int fn C.SSL_set_cipher_list() int
fn C.BIO_do_connect() int fn C.BIO_do_connect() int
fn C.BIO_do_handshake() int fn C.BIO_do_handshake() int
fn C.SSL_get_peer_certificate() int fn C.SSL_get_peer_certificate() int
fn C.SSL_get_verify_result() int fn C.SSL_get_verify_result() int
fn C.SSL_set_tlsext_host_name() int fn C.SSL_set_tlsext_host_name() int
fn C.BIO_puts() fn C.BIO_puts()
fn C.BIO_read() fn C.BIO_read()
fn C.BIO_free_all() fn C.BIO_free_all()
fn C.SSL_CTX_free() fn C.SSL_CTX_free()
fn init() int { fn init() int {
C.SSL_library_init() C.SSL_library_init()
return 1 return 1
@ -109,6 +140,6 @@ fn (req &Request) ssl_do(port int, method, host_name, path string) ?Response {
if !isnil(ctx) { if !isnil(ctx) {
C.SSL_CTX_free(ctx) C.SSL_CTX_free(ctx)
} }
return parse_response(sb.str()) return parse_response(sb.str())
} }

View File

@ -1,14 +1,12 @@
module chunked module chunked
import strings import strings
// See: https://en.wikipedia.org/wiki/Chunked_transfer_encoding // See: https://en.wikipedia.org/wiki/Chunked_transfer_encoding
// ///////////////////////////////////////////////////////////// // /////////////////////////////////////////////////////////////
// The chunk size is transferred as a hexadecimal number // The chunk size is transferred as a hexadecimal number
// followed by \r\n as a line separator, // followed by \r\n as a line separator,
// followed by a chunk of data of the given size. // followed by a chunk of data of the given size.
// The end is marked with a chunk with size 0. // The end is marked with a chunk with size 0.
struct ChunkScanner { struct ChunkScanner {
mut: mut:
pos int pos int
@ -18,9 +16,13 @@ mut:
fn (s mut ChunkScanner) read_chunk_size() int { fn (s mut ChunkScanner) read_chunk_size() int {
mut n := 0 mut n := 0
for { for {
if s.pos >= s.text.len { break } if s.pos >= s.text.len {
break
}
c := s.text[s.pos] c := s.text[s.pos]
if !c.is_hex_digit() { break } if !c.is_hex_digit() {
break
}
n = n<<4 n = n<<4
n += int(unhex(c)) n += int(unhex(c))
s.pos++ s.pos++
@ -29,9 +31,15 @@ fn (s mut ChunkScanner) read_chunk_size() int {
} }
fn unhex(c byte) byte { fn unhex(c byte) byte {
if `0` <= c && c <= `9` { return c - `0` } if `0` <= c && c <= `9` {
else if `a` <= c && c <= `f` { return c - `a` + 10 } return c - `0`
else if `A` <= c && c <= `F` { return c - `A` + 10 } }
else if `a` <= c && c <= `f` {
return c - `a` + 10
}
else if `A` <= c && c <= `F` {
return c - `A` + 10
}
return 0 return 0
} }
@ -53,7 +61,9 @@ pub fn decode(text string) string {
} }
for { for {
csize := cscanner.read_chunk_size() csize := cscanner.read_chunk_size()
if 0 == csize { break } if 0 == csize {
break
}
cscanner.skip_crlf() cscanner.skip_crlf()
sb.write(cscanner.read_chunk(csize)) sb.write(cscanner.read_chunk(csize))
cscanner.skip_crlf() cscanner.skip_crlf()
@ -61,3 +71,4 @@ pub fn decode(text string) string {
cscanner.skip_crlf() cscanner.skip_crlf()
return sb.str() return sb.str()
} }

View File

@ -1,14 +1,16 @@
// Copyright (c) 2019 Alexander Medvednikov. All rights reserved. // Copyright (c) 2019 Alexander Medvednikov. All rights reserved.
// Use of this source code is governed by an MIT license // Use of this source code is governed by an MIT license
// that can be found in the LICENSE file. // that can be found in the LICENSE file.
module http module http
import os import os
pub fn download_file(url, out string) bool { pub fn download_file(url, out string) bool {
s := http.get(url) or { return false } s := http.get(url) or {
return false
}
os.write_file(out, s.text) os.write_file(out, s.text)
return true return true
// download_file_with_progress(url, out, empty, empty) // download_file_with_progress(url, out, empty, empty)
} }

View File

@ -1,12 +1,9 @@
// Copyright (c) 2019 Alexander Medvednikov. All rights reserved. // Copyright (c) 2019 Alexander Medvednikov. All rights reserved.
// Use of this source code is governed by an MIT license // Use of this source code is governed by an MIT license
// that can be found in the LICENSE file. // that can be found in the LICENSE file.
module http module http
type downloadfn fn (written int) type downloadfn fn(written int)type download_finished_fn fn()
type download_finished_fn fn ()
/* /*
struct DownloadStruct { struct DownloadStruct {
mut: mut:
@ -15,7 +12,6 @@ mut:
cb downloadfn cb downloadfn
} }
*/ */
fn download_cb(ptr voidptr, size, nmemb size_t, userp voidptr) { fn download_cb(ptr voidptr, size, nmemb size_t, userp voidptr) {
/* /*
mut data := &DownloadStruct(userp) mut data := &DownloadStruct(userp)
@ -52,6 +48,5 @@ pub fn download_file_with_progress(url, out string, cb downloadfn, cb_finished f
} }
fn empty() { fn empty() {
} }

View File

@ -1,7 +1,6 @@
// Copyright (c) 2019 Alexander Medvednikov. All rights reserved. // Copyright (c) 2019 Alexander Medvednikov. All rights reserved.
// Use of this source code is governed by an MIT license // Use of this source code is governed by an MIT license
// that can be found in the LICENSE file. // that can be found in the LICENSE file.
module http module http
import net.urllib import net.urllib
@ -79,7 +78,9 @@ pub fn new_request(typ, _url, _data string) ?Request {
} }
pub fn get_text(url string) string { pub fn get_text(url string) string {
resp := get(url) or { return '' } resp := get(url) or {
return ''
}
return resp.text return resp.text
} }
@ -116,18 +117,29 @@ pub fn (req &Request) do() ?Response {
if req.typ == 'POST' { if req.typ == 'POST' {
// req.headers << 'Content-Type: application/x-www-form-urlencoded' // req.headers << 'Content-Type: application/x-www-form-urlencoded'
} }
url := urllib.parse(req.url) or { return error('http.request.do: invalid URL "$req.url"') } url := urllib.parse(req.url) or {
return error('http.request.do: invalid URL "$req.url"')
}
mut rurl := url mut rurl := url
mut resp := Response{} mut resp := Response{
}
mut no_redirects := 0 mut no_redirects := 0
for { for {
if no_redirects == max_redirects { return error('http.request.do: maximum number of redirects reached ($max_redirects)') } if no_redirects == max_redirects {
qresp := req.method_and_url_to_response( req.typ, rurl ) or { return error(err) } return error('http.request.do: maximum number of redirects reached ($max_redirects)')
}
qresp := req.method_and_url_to_response(req.typ, rurl) or {
return error(err)
}
resp = qresp resp = qresp
if ! (resp.status_code in [301, 302, 303, 307, 308]) { break } if !(resp.status_code in [301, 302, 303, 307, 308]) {
break
}
// follow any redirects // follow any redirects
redirect_url := resp.headers['Location'] redirect_url := resp.headers['Location']
qrurl := urllib.parse( redirect_url ) or { return error('http.request.do: invalid URL in redirect "$redirect_url"') } qrurl := urllib.parse(redirect_url) or {
return error('http.request.do: invalid URL in redirect "$redirect_url"')
}
rurl = qrurl rurl = qrurl
no_redirects++ no_redirects++
} }
@ -141,8 +153,12 @@ fn (req &Request) method_and_url_to_response(method string, url net_dot_urllib.U
path := if url.query().size > 0 { '/$p?${url.query().encode()}' } else { '/$p' } path := if url.query().size > 0 { '/$p?${url.query().encode()}' } else { '/$p' }
mut nport := url.port().int() mut nport := url.port().int()
if nport == 0 { if nport == 0 {
if scheme == 'http' { nport = 80 } if scheme == 'http' {
if scheme == 'https' { nport = 443 } nport = 80
}
if scheme == 'https' {
nport = 443
}
} }
// println('fetch $method, $scheme, $host_name, $nport, $path ') // println('fetch $method, $scheme, $host_name, $nport, $path ')
if scheme == 'https' { if scheme == 'https' {
@ -151,7 +167,8 @@ fn (req &Request) method_and_url_to_response(method string, url net_dot_urllib.U
return error(err) return error(err)
} }
return res return res
} else if scheme == 'http' { }
else if scheme == 'http' {
// println('http_do( $nport, $method, $host_name, $path )') // println('http_do( $nport, $method, $host_name, $path )')
res := req.http_do(nport, method, host_name, path) or { res := req.http_do(nport, method, host_name, path) or {
return error(err) return error(err)
@ -196,11 +213,9 @@ fn parse_response(resp string) Response {
val := h[pos + 2..] val := h[pos + 2..]
headers[key] = val.trim_space() headers[key] = val.trim_space()
} }
if headers['Transfer-Encoding'] == 'chunked' { if headers['Transfer-Encoding'] == 'chunked' {
text = chunked.decode(text) text = chunked.decode(text)
} }
return Response{ return Response{
status_code: status_code status_code: status_code
headers: headers headers: headers
@ -217,12 +232,7 @@ fn (req &Request) build_request_headers(method, host_name, path string) string {
if req.data.len > 0 { if req.data.len > 0 {
uheaders << 'Content-Length: ${req.data.len}\r\n' uheaders << 'Content-Length: ${req.data.len}\r\n'
} }
return '$method $path HTTP/1.1\r\n' + return '$method $path HTTP/1.1\r\n' + 'Host: $host_name\r\n' + 'User-Agent: $ua\r\n' + uheaders.join('') + 'Connection: close\r\n\r\n' + req.data
'Host: $host_name\r\n' +
'User-Agent: $ua\r\n' +
uheaders.join('') +
'Connection: close\r\n\r\n' +
req.data
} }
pub fn unescape_url(s string) string { pub fn unescape_url(s string) string {
@ -242,3 +252,4 @@ pub fn escape(s string) string {
} }
type wsfn fn(s string, ptr voidptr) type wsfn fn(s string, ptr voidptr)

View File

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

View File

@ -1,13 +1,11 @@
// Copyright (c) 2019 Alexander Medvednikov. All rights reserved. // Copyright (c) 2019 Alexander Medvednikov. All rights reserved.
// Use of this source code is governed by an MIT license // Use of this source code is governed by an MIT license
// that can be found in the LICENSE file. // that can be found in the LICENSE file.
module json module json
#flag -I @VROOT/thirdparty/cJSON #flag -I @VROOT/thirdparty/cJSON
#flag @VROOT/thirdparty/cJSON/cJSON.o #flag @VROOT/thirdparty/cJSON/cJSON.o
#include "cJSON.h" #include "cJSON.h"
struct C.cJSON { struct C.cJSON {
valueint int valueint int
valuedouble f32 valuedouble f32
@ -84,7 +82,6 @@ fn jsdecode_f64(root &C.cJSON) f64 {
return f64(root.valuedouble) return f64(root.valuedouble)
} }
fn jsdecode_string(root &C.cJSON) string { fn jsdecode_string(root &C.cJSON) string {
if isnil(root) { if isnil(root) {
return '' return ''
@ -98,12 +95,23 @@ fn jsdecode_string(root &C.cJSON) string {
} }
fn C.cJSON_IsTrue() bool fn C.cJSON_IsTrue() bool
fn C.cJSON_CreateNumber() &C.cJSON fn C.cJSON_CreateNumber() &C.cJSON
fn C.cJSON_CreateBool() &C.cJSON fn C.cJSON_CreateBool() &C.cJSON
fn C.cJSON_CreateString() &C.cJSON fn C.cJSON_CreateString() &C.cJSON
fn C.cJSON_Parse() &C.cJSON fn C.cJSON_Parse() &C.cJSON
fn C.cJSON_PrintUnformatted() byteptr fn C.cJSON_PrintUnformatted() byteptr
fn jsdecode_bool(root &C.cJSON) bool { fn jsdecode_bool(root &C.cJSON) bool {
if isnil(root) { if isnil(root) {
return false return false
@ -161,7 +169,6 @@ fn jsencode_string(val string) &C.cJSON {
return C.cJSON_CreateString(clone.str) return C.cJSON_CreateString(clone.str)
// return C.cJSON_CreateString2(val.str, val.len) // return C.cJSON_CreateString2(val.str, val.len)
} }
// /////////////////////// // ///////////////////////
// user := decode_User(json_parse(js_string_var)) // user := decode_User(json_parse(js_string_var))
fn json_parse(s string) &C.cJSON { fn json_parse(s string) &C.cJSON {
@ -178,3 +185,4 @@ fn json_print(json &C.cJSON) string {
// fn json_array_for_each(val, root &C.cJSON) { // fn json_array_for_each(val, root &C.cJSON) {
// #cJSON_ArrayForEach (val ,root) // #cJSON_ArrayForEach (val ,root)
// } // }

View File

@ -5,7 +5,6 @@ module net
#include <netinet/in.h> #include <netinet/in.h>
#include <netdb.h> #include <netdb.h>
#include <errno.h> #include <errno.h>
fn error_code() int { fn error_code() int {
return C.errno return C.errno
} }
@ -13,3 +12,4 @@ fn error_code() int {
pub const ( pub const (
MSG_NOSIGNAL = 0x4000 MSG_NOSIGNAL = 0x4000
) )

View File

@ -1,7 +1,6 @@
module net module net
fn C.gethostname() int fn C.gethostname() int
// hostname returns the host name reported by the kernel. // hostname returns the host name reported by the kernel.
pub fn hostname() ?string { pub fn hostname() ?string {
mut name := [256]byte mut name := [256]byte

View File

@ -10,7 +10,6 @@ pub:
proto int proto int
} }
struct C.in_addr { struct C.in_addr {
mut: mut:
s_addr int s_addr int
@ -35,27 +34,57 @@ mut:
ai_next voidptr ai_next voidptr
} }
struct C.sockaddr_storage {} struct C.sockaddr_storage {
fn C.socket() int }
fn C.setsockopt() int
fn C.htonl() int
fn C.htons() int
fn C.bind() int
fn C.listen() int
fn C.accept() int
fn C.getaddrinfo() int
fn C.connect() int
fn C.send() int
fn C.recv() int
fn C.read() int
fn C.shutdown() int
fn C.close() int
fn C.ntohs() int
fn C.getsockname() int
fn C.socket() int
fn C.setsockopt() int
fn C.htonl() int
fn C.htons() int
fn C.bind() int
fn C.listen() int
fn C.accept() int
fn C.getaddrinfo() int
fn C.connect() int
fn C.send() int
fn C.recv() int
fn C.read() int
fn C.shutdown() int
fn C.close() int
fn C.ntohs() int
fn C.getsockname() int
// create socket // create socket
pub fn new_socket(family int, _type int, proto int) ?Socket { pub fn new_socket(family int, _type int, proto int) ?Socket {
sockfd := C.socket(family, _type, proto) sockfd := C.socket(family, _type, proto)
one := 1 one := 1
// This is needed so that there are no problems with reusing the // This is needed so that there are no problems with reusing the
@ -88,7 +117,8 @@ pub fn (s Socket) setsockopt(level int, optname int, optvalue &int) ?int {
// bind socket to port // bind socket to port
pub fn (s Socket) bind(port int) ?int { pub fn (s Socket) bind(port int) ?int {
mut addr := C.sockaddr_in{} mut addr := C.sockaddr_in{
}
addr.sin_family = s.family addr.sin_family = s.family
addr.sin_port = C.htons(port) addr.sin_port = C.htons(port)
addr.sin_addr.s_addr = C.htonl(C.INADDR_ANY) addr.sin_addr.s_addr = C.htonl(C.INADDR_ANY)
@ -148,7 +178,8 @@ pub fn (s Socket) accept() ?Socket {
$if debug { $if debug {
println('accept()') println('accept()')
} }
addr := C.sockaddr_storage{} addr := C.sockaddr_storage{
}
size := 128 // sizeof(sockaddr_storage) size := 128 // sizeof(sockaddr_storage)
sockfd := C.accept(s.sockfd, &addr, &size) sockfd := C.accept(s.sockfd, &addr, &size)
if sockfd < 0 { if sockfd < 0 {
@ -165,7 +196,8 @@ pub fn (s Socket) accept() ?Socket {
// connect to given addrress and port // connect to given addrress and port
pub fn (s Socket) connect(address string, port int) ?int { pub fn (s Socket) connect(address string, port int) ?int {
mut hints := C.addrinfo{} mut hints := C.addrinfo{
}
hints.ai_family = s.family hints.ai_family = s.family
hints.ai_socktype = s._type hints.ai_socktype = s._type
hints.ai_flags = C.AI_PASSIVE hints.ai_flags = C.AI_PASSIVE
@ -174,8 +206,6 @@ pub fn (s Socket) connect(address string, port int) ?int {
hints.ai_canonname = C.NULL hints.ai_canonname = C.NULL
hints.ai_addr = C.NULL hints.ai_addr = C.NULL
hints.ai_next = C.NULL hints.ai_next = C.NULL
info := &C.addrinfo(0) info := &C.addrinfo(0)
sport := '$port' sport := '$port'
info_res := C.getaddrinfo(address.str, sport.str, &hints, &info) info_res := C.getaddrinfo(address.str, sport.str, &hints, &info)
@ -208,9 +238,13 @@ pub fn (s Socket) send(buf byteptr, len int) ?int {
mut dlen := len mut dlen := len
for { for {
sbytes := C.send(s.sockfd, dptr, dlen, MSG_NOSIGNAL) sbytes := C.send(s.sockfd, dptr, dlen, MSG_NOSIGNAL)
if sbytes < 0 { return error('net.send: failed with $sbytes') } if sbytes < 0 {
return error('net.send: failed with $sbytes')
}
dlen -= sbytes dlen -= sbytes
if dlen <= 0 { break } if dlen <= 0 {
break
}
dptr += sbytes dptr += sbytes
} }
return len return len
@ -232,6 +266,7 @@ pub fn (s Socket) recv(bufsize int) (byteptr, int) {
pub fn (s Socket) cread(buffer byteptr, buffersize int) int { pub fn (s Socket) cread(buffer byteptr, buffersize int) int {
return C.read(s.sockfd, buffer, buffersize) return C.read(s.sockfd, buffer, buffersize)
} }
// Receive a message from the socket, and place it in a preallocated buffer buf, // Receive a message from the socket, and place it in a preallocated buffer buf,
// with maximum message size bufsize. Returns the length of the received message. // with maximum message size bufsize. Returns the length of the received message.
pub fn (s Socket) crecv(buffer byteptr, buffersize int) int { pub fn (s Socket) crecv(buffer byteptr, buffersize int) int {
@ -243,8 +278,7 @@ pub fn (s Socket) close() ?int {
mut shutdown_res := 0 mut shutdown_res := 0
$if windows { $if windows {
shutdown_res = C.shutdown(s.sockfd, C.SD_BOTH) shutdown_res = C.shutdown(s.sockfd, C.SD_BOTH)
} } $else {
$else {
shutdown_res = C.shutdown(s.sockfd, C.SHUT_RDWR) shutdown_res = C.shutdown(s.sockfd, C.SHUT_RDWR)
} }
// TODO: should shutdown throw an error? close will // TODO: should shutdown throw an error? close will
@ -252,18 +286,15 @@ pub fn (s Socket) close() ?int {
// if shutdown_res < 0 { // if shutdown_res < 0 {
// return error('net.close: shutdown failed with $shutdown_res') // return error('net.close: shutdown failed with $shutdown_res')
// } // }
mut res := 0 mut res := 0
$if windows { $if windows {
res = C.closesocket(s.sockfd) res = C.closesocket(s.sockfd)
} } $else {
$else {
res = C.close(s.sockfd) res = C.close(s.sockfd)
} }
if res < 0 { if res < 0 {
return error('net.close: failed with $res') return error('net.close: failed with $res')
} }
return 0 return 0
} }
@ -272,12 +303,13 @@ pub const (
MAX_READ = 400 MAX_READ = 400
MSG_PEEK = 0x02 MSG_PEEK = 0x02
) )
// write - write a string with CRLF after it over the socket s // write - write a string with CRLF after it over the socket s
pub fn (s Socket) write(str string) ?int { pub fn (s Socket) write(str string) ?int {
line := '$str$CRLF' line := '$str$CRLF'
res := C.send(s.sockfd, line.str, line.len, MSG_NOSIGNAL) res := C.send(s.sockfd, line.str, line.len, MSG_NOSIGNAL)
if res < 0 { return error('net.write: failed with $res') } if res < 0 {
return error('net.write: failed with $res')
}
return res return res
} }
@ -288,8 +320,12 @@ pub fn (s Socket) read_line() string {
for { for {
mut line := '' // The current line. Can be a partial without \n in it. mut line := '' // The current line. Can be a partial without \n in it.
n := C.recv(s.sockfd, buf, MAX_READ - 1, MSG_PEEK) n := C.recv(s.sockfd, buf, MAX_READ - 1, MSG_PEEK)
if n == -1 { return res } if n == -1 {
if n == 0 { return res } return res
}
if n == 0 {
return res
}
buf[n] = `\0` buf[n] = `\0`
mut eol_idx := -1 mut eol_idx := -1
for i := 0; i < n; i++ { for i := 0; i < n; i++ {
@ -328,18 +364,22 @@ pub fn (s Socket) read_all() string {
mut res := '' // The final result, including the ending \n. mut res := '' // The final result, including the ending \n.
for { for {
n := C.recv(s.sockfd, buf, MAX_READ - 1, 0) n := C.recv(s.sockfd, buf, MAX_READ - 1, 0)
if n == -1 { return res } if n == -1 {
if n == 0 { return res } return res
}
if n == 0 {
return res
}
res += tos_clone(buf) res += tos_clone(buf)
} }
return res return res
} }
pub fn (s Socket) get_port() int { pub fn (s Socket) get_port() int {
mut addr := C.sockaddr_in {} mut addr := C.sockaddr_in{
}
size := 16 // sizeof(sockaddr_in) size := 16 // sizeof(sockaddr_in)
C.getsockname(s.sockfd, &addr, &size) C.getsockname(s.sockfd, &addr, &size)
return C.ntohs(addr.sin_port) return C.ntohs(addr.sin_port)
} }

View File

@ -1,14 +1,11 @@
// urllib parses URLs and implements query escaping. // urllib parses URLs and implements query escaping.
// See RFC 3986. This module generally follows RFC 3986, except where // See RFC 3986. This module generally follows RFC 3986, except where
// it deviates for compatibility reasons. // it deviates for compatibility reasons.
// Based off: https://github.com/golang/go/blob/master/src/net/url/url.go // Based off: https://github.com/golang/go/blob/master/src/net/url/url.go
// Last commit: https://github.com/golang/go/commit/fe2ed5054176935d4adcf13e891715ccf2ee3cce // Last commit: https://github.com/golang/go/commit/fe2ed5054176935d4adcf13e891715ccf2ee3cce
// Copyright 2009 The Go Authors. All rights reserved. // Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style // Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
module urllib module urllib
import strings import strings
@ -30,7 +27,9 @@ const (
fn error_msg(message, val string) string { fn error_msg(message, val string) string {
mut msg := 'net.urllib.$message' mut msg := 'net.urllib.$message'
if val != '' { msg = '$msg ($val)' } if val != '' {
msg = '$msg ($val)'
}
return msg return msg
} }
@ -44,7 +43,6 @@ fn should_escape(c byte, mode EncodingMode) bool {
if (`a` <= c && c <= `z`) || (`A` <= c && c <= `Z`) || (`0` <= c && c <= `9`) { if (`a` <= c && c <= `z`) || (`A` <= c && c <= `Z`) || (`0` <= c && c <= `9`) {
return false return false
} }
if mode == .encode_host || mode == .encode_zone { if mode == .encode_host || mode == .encode_zone {
// §3.2.2 host allows // §3.2.2 host allows
// sub-delims = `!` / `$` / `&` / ``` / `(` / `)` / `*` / `+` / `,` / `;` / `=` // sub-delims = `!` / `$` / `&` / ``` / `(` / `)` / `*` / `+` / `,` / `;` / `=`
@ -55,59 +53,58 @@ fn should_escape(c byte, mode EncodingMode) bool {
// we could possibly allow, and parse will reject them if we // we could possibly allow, and parse will reject them if we
// escape them (because hosts can`t use %-encoding for // escape them (because hosts can`t use %-encoding for
// ASCII bytes). // ASCII bytes).
if c in [`!`, `$`, `&`, `\\`, `(`, `)`, `*`, `+`, `,`, `;`, `=`, if c in [`!`, `$`, `&`, `\\`, `(`, `)`, `*`, `+`, `,`, `;`, `=`, `:`, `[`, `]`, `<`, `>`, `"`] {
`:`, `[`, `]`, `<`, `>`, `"`]
{
return false return false
} }
} }
match c { match c {
`-`, `_`, `.`, `~` { // §2.3 Unreserved characters (mark) `-`, `_`, `.`, `~` {
// §2.3 Unreserved characters (mark)
return false return false
} }
`$`, `&`, `+`, `,`, `/`, `:`, `;`, `=`, `?`, `@` {
`$`, `&`, `+`, `,`, `/`, `:`, `;`, `=`, `?`, `@` { // §2.2 Reserved characters (reserved) // §2.2 Reserved characters (reserved)
// Different sections of the URL allow a few of // Different sections of the URL allow a few of
// the reserved characters to appear unescaped. // the reserved characters to appear unescaped.
match mode { match mode {
.encode_path { // §3.3 .encode_path {
// §3.3
// The RFC allows : @ & = + $ but saves / ; , for assigning // The RFC allows : @ & = + $ but saves / ; , for assigning
// meaning to individual path segments. This package // meaning to individual path segments. This package
// only manipulates the path as a whole, so we allow those // only manipulates the path as a whole, so we allow those
// last three as well. That leaves only ? to escape. // last three as well. That leaves only ? to escape.
return c == `?` return c == `?`
} }
.encode_path_segment {
.encode_path_segment { // §3.3 // §3.3
// The RFC allows : @ & = + $ but saves / ; , for assigning // The RFC allows : @ & = + $ but saves / ; , for assigning
// meaning to individual path segments. // meaning to individual path segments.
return c == `/` || c == `;` || c == `,` || c == `?` return c == `/` || c == `;` || c == `,` || c == `?`
} }
.encode_user_password {
.encode_user_password { // §3.2.1 // §3.2.1
// The RFC allows `;`, `:`, `&`, `=`, `+`, `$`, and `,` in // The RFC allows `;`, `:`, `&`, `=`, `+`, `$`, and `,` in
// userinfo, so we must escape only `@`, `/`, and `?`. // userinfo, so we must escape only `@`, `/`, and `?`.
// The parsing of userinfo treats `:` as special so we must escape // The parsing of userinfo treats `:` as special so we must escape
// that too. // that too.
return c == `@` || c == `/` || c == `?` || c == `:` return c == `@` || c == `/` || c == `?` || c == `:`
} }
.encode_query_component {
.encode_query_component { // §3.4 // §3.4
// The RFC reserves (so we must escape) everything. // The RFC reserves (so we must escape) everything.
return true return true
} }
.encode_fragment {
.encode_fragment { // §4.1 // §4.1
// The RFC text is silent but the grammar allows // The RFC text is silent but the grammar allows
// everything, so escape nothing. // everything, so escape nothing.
return false return false
} }
else {} else {
}}
} }
} else {} else {
} }}
if mode == .encode_fragment { if mode == .encode_fragment {
// RFC 3986 §2.2 allows not escaping sub-delims. A subset of sub-delims are // RFC 3986 §2.2 allows not escaping sub-delims. A subset of sub-delims are
// included in reserved from RFC 2396 §2.2. The remaining sub-delims do not // included in reserved from RFC 2396 §2.2. The remaining sub-delims do not
@ -119,10 +116,9 @@ fn should_escape(c byte, mode EncodingMode) bool {
`!`, `(`, `)`, `*` { `!`, `(`, `)`, `*` {
return false return false
} }
else {} else {
}}
} }
}
// Everything else must be escaped. // Everything else must be escaped.
return true return true
} }
@ -196,19 +192,17 @@ fn unescape(s_ string, mode EncodingMode) ?string {
`+` { `+` {
has_plus = mode == .encode_query_component has_plus = mode == .encode_query_component
i++ i++
} else { }
else {
if (mode == .encode_host || mode == .encode_zone) && s[i] < 0x80 && should_escape(s[i], mode) { if (mode == .encode_host || mode == .encode_zone) && s[i] < 0x80 && should_escape(s[i], mode) {
error(error_msg('unescape: invalid character in host name', s[i..i + 1])) error(error_msg('unescape: invalid character in host name', s[i..i + 1]))
} }
i++ i++
}}
} }
}
}
if n == 0 && !has_plus { if n == 0 && !has_plus {
return s return s
} }
mut t := strings.new_builder(s.len - 2 * n) mut t := strings.new_builder(s.len - 2 * n)
for i := 0; i < s.len; i++ { for i := 0; i < s.len; i++ {
x := s[i] x := s[i]
@ -220,13 +214,14 @@ fn unescape(s_ string, mode EncodingMode) ?string {
`+` { `+` {
if mode == .encode_query_component { if mode == .encode_query_component {
t.write(' ') t.write(' ')
} else { }
else {
t.write('+') t.write('+')
} }
} else { }
else {
t.write(s[i].str()) t.write(s[i].str())
} }}
}
} }
return t.str() return t.str()
} }
@ -252,26 +247,24 @@ fn escape(s string, mode EncodingMode) string {
if should_escape(c, mode) { if should_escape(c, mode) {
if c == ` ` && mode == .encode_query_component { if c == ` ` && mode == .encode_query_component {
space_count++ space_count++
} else { }
else {
hex_count++ hex_count++
} }
} }
} }
if space_count == 0 && hex_count == 0 { if space_count == 0 && hex_count == 0 {
return s return s
} }
buf := [byte(0)].repeat(64) buf := [byte(0)].repeat(64)
mut t := []byte mut t := []byte
required := s.len + 2 * hex_count required := s.len + 2 * hex_count
if required <= buf.len { if required <= buf.len {
t = buf[..required] t = buf[..required]
} else { }
else {
t = [byte(0)].repeat(required) t = [byte(0)].repeat(required)
} }
if hex_count == 0 { if hex_count == 0 {
copy(t, s.bytes()) copy(t, s.bytes())
for i := 0; i < s.len; i++ { for i := 0; i < s.len; i++ {
@ -281,7 +274,6 @@ fn escape(s string, mode EncodingMode) string {
} }
return string(t,t.len) return string(t,t.len)
} }
upperhex := '0123456789ABCDEF' upperhex := '0123456789ABCDEF'
mut j := 0 mut j := 0
for i := 0; i < s.len; i++ { for i := 0; i < s.len; i++ {
@ -289,12 +281,14 @@ fn escape(s string, mode EncodingMode) string {
if c1 == ` ` && mode == .encode_query_component { if c1 == ` ` && mode == .encode_query_component {
t[j] = `+` t[j] = `+`
j++ j++
} else if should_escape(c1, mode) { }
else if should_escape(c1, mode) {
t[j] = `%` t[j] = `%`
t[j + 1] = upperhex[c1>>4] t[j + 1] = upperhex[c1>>4]
t[j + 2] = upperhex[c1 & 15] t[j + 2] = upperhex[c1 & 15]
j += 3 j += 3
} else { }
else {
t[j] = s[i] t[j] = s[i]
j++ j++
} }
@ -332,13 +326,12 @@ pub mut:
raw_query string // encoded query values, without '?' raw_query string // encoded query values, without '?'
fragment string // fragment for references, without '#' fragment string // fragment for references, without '#'
} }
// user returns a Userinfo containing the provided username // user returns a Userinfo containing the provided username
// and no password set. // and no password set.
pub fn user(username string) &Userinfo { pub fn user(username string) &Userinfo {
return &Userinfo{ return &Userinfo{
username: username, username: username
password: '', password: ''
password_set: false password_set: false
} }
} }
@ -352,7 +345,8 @@ pub fn user(username string) &Userinfo {
// information in clear text (such as URI) has proven to be a // information in clear text (such as URI) has proven to be a
// security risk in almost every case where it has been used.'' // security risk in almost every case where it has been used.''
fn user_password(username, password string) &Userinfo { fn user_password(username, password string) &Userinfo {
return &Userinfo{username, password, true} return &Userinfo{
username,password,true}
} }
// The Userinfo type is an immutable encapsulation of username and // The Userinfo type is an immutable encapsulation of username and
@ -472,17 +466,16 @@ fn parse_url(rawurl string, via_request bool) ?URL {
if string_contains_ctl_byte(rawurl) { if string_contains_ctl_byte(rawurl) {
return error(error_msg('parse_url: invalid control character in URL', rawurl)) return error(error_msg('parse_url: invalid control character in URL', rawurl))
} }
if rawurl == '' && via_request { if rawurl == '' && via_request {
return error(error_msg('parse_url: empty URL', rawurl)) return error(error_msg('parse_url: empty URL', rawurl))
} }
mut url := URL{user:0} mut url := URL{
user: 0
}
if rawurl == '*' { if rawurl == '*' {
url.path = '*' url.path = '*'
return url return url
} }
// Split off possible leading 'http:', 'mailto:', etc. // Split off possible leading 'http:', 'mailto:', etc.
// Cannot contain escaped characters. // Cannot contain escaped characters.
p := split_by_scheme(rawurl) or { p := split_by_scheme(rawurl) or {
@ -491,17 +484,16 @@ fn parse_url(rawurl string, via_request bool) ?URL {
url.scheme = p[0] url.scheme = p[0]
mut rest := p[1] mut rest := p[1]
url.scheme = url.scheme.to_lower() url.scheme = url.scheme.to_lower()
// if rest.ends_with('?') && strings.count(rest, '?') == 1 { // if rest.ends_with('?') && strings.count(rest, '?') == 1 {
if rest.ends_with('?') && !rest[..1].contains('?') { if rest.ends_with('?') && !rest[..1].contains('?') {
url.force_query = true url.force_query = true
rest = rest[..rest.len - 1] rest = rest[..rest.len - 1]
} else { }
else {
r,raw_query := split(rest, `?`, true) r,raw_query := split(rest, `?`, true)
rest = r rest = r
url.raw_query = raw_query url.raw_query = raw_query
} }
if !rest.starts_with('/') { if !rest.starts_with('/') {
if url.scheme != '' { if url.scheme != '' {
// We consider rootless paths per RFC 3986 as opaque. // We consider rootless paths per RFC 3986 as opaque.
@ -511,21 +503,23 @@ fn parse_url(rawurl string, via_request bool) ?URL {
if via_request { if via_request {
return error(error_msg('parse_url: invalid URI for request', '')) return error(error_msg('parse_url: invalid URI for request', ''))
} }
// Avoid confusion with malformed schemes, like cache_object:foo/bar. // Avoid confusion with malformed schemes, like cache_object:foo/bar.
// See golang.org/issue/16822. // See golang.org/issue/16822.
// //
// RFC 3986, §3.3: // RFC 3986, §3.3:
// In addition, a URI reference (Section 4.1) may be a relative-path reference, // In addition, a URI reference (Section 4.1) may be a relative-path reference,
// in which case the first path segment cannot contain a colon (':') character. // in which case the first path segment cannot contain a colon (':') character.
colon := rest.index(':') or { return error('there should be a : in the URL') } colon := rest.index(':') or {
slash := rest.index('/') or { return error('there should be a / in the URL') } return error('there should be a : in the URL')
}
slash := rest.index('/') or {
return error('there should be a / in the URL')
}
if colon >= 0 && (slash < 0 || colon < slash) { if colon >= 0 && (slash < 0 || colon < slash) {
// First path segment has colon. Not allowed in relative URL. // First path segment has colon. Not allowed in relative URL.
return error(error_msg('parse_url: first path segment in URL cannot contain colon', '')) return error(error_msg('parse_url: first path segment in URL cannot contain colon', ''))
} }
} }
if ((url.scheme != '' || !via_request) && !rest.starts_with('///')) && rest.starts_with('//') { if ((url.scheme != '' || !via_request) && !rest.starts_with('///')) && rest.starts_with('//') {
authority,r := split(rest[2..], `/`, false) authority,r := split(rest[2..], `/`, false)
rest = r rest = r
@ -551,7 +545,9 @@ struct ParseAuthorityRes {
} }
fn parse_authority(authority string) ?ParseAuthorityRes { fn parse_authority(authority string) ?ParseAuthorityRes {
i := authority.last_index('@') or { -1 } i := authority.last_index('@') or {
-1
}
mut host := '' mut host := ''
mut zuser := user('') mut zuser := user('')
if i < 0 { if i < 0 {
@ -559,14 +555,18 @@ fn parse_authority(authority string) ?ParseAuthorityRes {
return error(err) return error(err)
} }
host = h host = h
} else { }
else {
h := parse_host(authority[i + 1..]) or { h := parse_host(authority[i + 1..]) or {
return error(err) return error(err)
} }
host = h host = h
} }
if i < 0 { if i < 0 {
return ParseAuthorityRes{host: host, user: zuser} return ParseAuthorityRes{
host: host
user: zuser
}
} }
mut userinfo := authority[..i] mut userinfo := authority[..i]
if !valid_userinfo(userinfo) { if !valid_userinfo(userinfo) {
@ -578,7 +578,8 @@ fn parse_authority(authority string) ?ParseAuthorityRes {
} }
userinfo = u userinfo = u
zuser = user(userinfo) zuser = user(userinfo)
} else { }
else {
mut username,mut password := split(userinfo, `:`, true) mut username,mut password := split(userinfo, `:`, true)
u := unescape(username, .encode_user_password) or { u := unescape(username, .encode_user_password) or {
return error(err) return error(err)
@ -603,13 +604,12 @@ fn parse_host(host string) ?string {
// parse an IP-Literal in RFC 3986 and RFC 6874. // parse an IP-Literal in RFC 3986 and RFC 6874.
// E.g., '[fe80::1]', '[fe80::1%25en0]', '[fe80::1]:80'. // E.g., '[fe80::1]', '[fe80::1%25en0]', '[fe80::1]:80'.
mut i := host.last_index(']') or { mut i := host.last_index(']') or {
return error(error_msg('parse_host: missing \']\' in host', '')) return error(error_msg("parse_host: missing \']\' in host", ''))
} }
mut colon_port := host[i + 1..] mut colon_port := host[i + 1..]
if !valid_optional_port(colon_port) { if !valid_optional_port(colon_port) {
return error(error_msg('parse_host: invalid port $colon_port after host ', '')) return error(error_msg('parse_host: invalid port $colon_port after host ', ''))
} }
// RFC 6874 defines that %25 (%-encoded percent) introduces // RFC 6874 defines that %25 (%-encoded percent) introduces
// the zone identifier, and the zone identifier can use basically // the zone identifier, and the zone identifier can use basically
// any %-encoding it likes. That's different from the host, which // any %-encoding it likes. That's different from the host, which
@ -642,7 +642,6 @@ fn parse_host(host string) ?string {
// host = h // host = h
// return host // return host
} }
// set_path sets the path and raw_path fields of the URL based on the provided // set_path sets the path and raw_path fields of the URL based on the provided
// escaped path p. It maintains the invariant that raw_path is only specified // escaped path p. It maintains the invariant that raw_path is only specified
// when it differs from the default encoding of the path. // when it differs from the default encoding of the path.
@ -660,7 +659,8 @@ fn (u mut URL) set_path(p string) ?bool {
if p == escp { if p == escp {
// Default encoding is fine. // Default encoding is fine.
u.raw_path = '' u.raw_path = ''
} else { }
else {
u.raw_path = p u.raw_path = p
} }
return true return true
@ -677,7 +677,9 @@ fn (u mut URL) set_path(p string) ?bool {
// reading u.raw_path directly. // reading u.raw_path directly.
fn (u &URL) escaped_path() string { fn (u &URL) escaped_path() string {
if u.raw_path != '' && valid_encoded_path(u.raw_path) { if u.raw_path != '' && valid_encoded_path(u.raw_path) {
unescape(u.raw_path, .encode_path) or { return '' } unescape(u.raw_path, .encode_path) or {
return ''
}
return u.raw_path return u.raw_path
} }
if u.path == '*' { if u.path == '*' {
@ -705,12 +707,12 @@ fn valid_encoded_path(s string) bool {
} }
`%` { `%` {
// ok - percent encoded, will decode // ok - percent encoded, will decode
} else { }
else {
if should_escape(s[i], .encode_path) { if should_escape(s[i], .encode_path) {
return false return false
} }
} }}
}
} }
return true return true
} }
@ -761,7 +763,8 @@ pub fn (u &URL) str() string {
} }
if u.opaque != '' { if u.opaque != '' {
buf.write(u.opaque) buf.write(u.opaque)
} else { }
else {
if u.scheme != '' || u.host != '' || !u.user.empty() { if u.scheme != '' || u.host != '' || !u.user.empty() {
if u.host != '' || u.path != '' || !u.user.empty() { if u.host != '' || u.path != '' || !u.user.empty() {
buf.write('//') buf.write('//')
@ -807,8 +810,6 @@ pub fn (u &URL) str() string {
// It is typically used for query parameters and form values. // It is typically used for query parameters and form values.
// Unlike in the http.Header map, the keys in a Values map // Unlike in the http.Header map, the keys in a Values map
// are case-sensitive. // are case-sensitive.
// parseQuery parses the URL-encoded query string and returns // parseQuery parses the URL-encoded query string and returns
// a map listing the values specified for each key. // a map listing the values specified for each key.
// parseQuery always returns a non-nil map containing all the // parseQuery always returns a non-nil map containing all the
@ -843,7 +844,8 @@ fn parse_query_values(m mut Values, query string) ?bool {
if i >= 0 { if i >= 0 {
q = key[i + 1..] q = key[i + 1..]
key = key[..i] key = key[..i]
} else { }
else {
q = '' q = ''
} }
if key == '' { if key == '' {
@ -860,7 +862,6 @@ fn parse_query_values(m mut Values, query string) ?bool {
continue continue
} }
key = k key = k
v := query_unescape(value) or { v := query_unescape(value) or {
had_error = true had_error = true
continue continue
@ -907,10 +908,14 @@ fn resolve_path(base, ref string) string {
mut full := '' mut full := ''
if ref == '' { if ref == '' {
full = base full = base
} else if ref[0] != `/` { }
i := base.last_index('/') or { -1 } else if ref[0] != `/` {
i := base.last_index('/') or {
-1
}
full = base[..i + 1] + ref full = base[..i + 1] + ref
} else { }
else {
full = ref full = ref
} }
if full == '' { if full == '' {
@ -927,10 +932,10 @@ fn resolve_path(base, ref string) string {
if dst.len > 0 { if dst.len > 0 {
dst = dst[..dst.len - 1] dst = dst[..dst.len - 1]
} }
} else { }
else {
dst << elem dst << elem
} }}
}
} }
last := src[src.len - 1] last := src[src.len - 1]
if last == '.' || last == '..' { if last == '.' || last == '..' {
@ -971,7 +976,9 @@ pub fn (u &URL) resolve_reference(ref &URL) ?URL {
// The 'absoluteURI' or 'net_path' cases. // The 'absoluteURI' or 'net_path' cases.
// We can ignore the error from set_path since we know we provided a // We can ignore the error from set_path since we know we provided a
// validly-escaped path. // validly-escaped path.
url.set_path(resolve_path(ref.escaped_path(), '')) or {return error(err)} url.set_path(resolve_path(ref.escaped_path(), '')) or {
return error(err)
}
return url return url
} }
if ref.opaque != '' { if ref.opaque != '' {
@ -989,7 +996,9 @@ pub fn (u &URL) resolve_reference(ref &URL) ?URL {
// The 'abs_path' or 'rel_path' cases. // The 'abs_path' or 'rel_path' cases.
url.host = u.host url.host = u.host
url.user = u.user url.user = u.user
url.set_path(resolve_path(u.escaped_path(), ref.escaped_path())) or { return error(err) } url.set_path(resolve_path(u.escaped_path(), ref.escaped_path())) or {
return error(err)
}
return url return url
} }
@ -1010,7 +1019,8 @@ pub fn (u &URL) request_uri() string {
if result == '' { if result == '' {
result = '/' result = '/'
} }
} else { }
else {
if result.starts_with('//') { if result.starts_with('//') {
result = u.scheme + ':' + result result = u.scheme + ':' + result
} }
@ -1043,17 +1053,14 @@ pub fn (u &URL) port() string {
fn split_host_port(hostport string) (string,string) { fn split_host_port(hostport string) (string,string) {
mut host := hostport mut host := hostport
mut port := '' mut port := ''
colon := host.last_index_byte(`:`) colon := host.last_index_byte(`:`)
if colon != -1 && valid_optional_port(host[colon..]) { if colon != -1 && valid_optional_port(host[colon..]) {
port = host[colon + 1..] port = host[colon + 1..]
host = host[..colon] host = host[..colon]
} }
if host.starts_with('[') && host.ends_with(']') { if host.starts_with('[') && host.ends_with(']') {
host = host[1..host.len - 1] host = host[1..host.len - 1]
} }
return host,port return host,port
} }
@ -1077,13 +1084,12 @@ pub fn valid_userinfo(s string) bool {
continue continue
} }
match r { match r {
`-`, `.`, `_`, `:`, `~`, `!`, `$`, `&`, `\\`, `-`, `.`, `_`, `:`, `~`, `!`, `$`, `&`, `\\`, `(`, `)`, `*`, `+`, `,`, `;`, `=`, `%`, `@` {
`(`, `)`, `*`, `+`, `,`, `;`, `=`, `%`, `@` {
continue continue
} else { }
else {
return false return false
} }}
}
} }
return true return true
} }
@ -1102,9 +1108,11 @@ fn string_contains_ctl_byte(s string) bool {
pub fn ishex(c byte) bool { pub fn ishex(c byte) bool {
if `0` <= c && c <= `9` { if `0` <= c && c <= `9` {
return true return true
} else if `a` <= c && c <= `f` { }
else if `a` <= c && c <= `f` {
return true return true
} else if `A` <= c && c <= `F` { }
else if `A` <= c && c <= `F` {
return true return true
} }
return false return false
@ -1113,10 +1121,13 @@ pub fn ishex(c byte) bool {
fn unhex(c byte) byte { fn unhex(c byte) byte {
if `0` <= c && c <= `9` { if `0` <= c && c <= `9` {
return c - `0` return c - `0`
} else if `a` <= c && c <= `f` { }
else if `a` <= c && c <= `f` {
return c - `a` + 10 return c - `a` + 10
} else if `A` <= c && c <= `F` { }
else if `A` <= c && c <= `F` {
return c - `A` + 10 return c - `A` + 10
} }
return 0 return 0
} }

View File

@ -1,7 +1,6 @@
// Copyright (c) 2019 Alexander Medvednikov. All rights reserved. // Copyright (c) 2019 Alexander Medvednikov. All rights reserved.
// Use of this source code is governed by an MIT license // Use of this source code is governed by an MIT license
// that can be found in the LICENSE file. // that can be found in the LICENSE file.
module urllib module urllib
struct Value { struct Value {
@ -86,3 +85,4 @@ pub fn (v mut Values) del(key string) {
v.data.delete(key) v.data.delete(key)
v.size = v.data.size v.size = v.data.size
} }

View File

@ -489,7 +489,6 @@ fn converter(pn mut PrepNumber) u64 {
} }
s1 = s1 & check_round_mask s1 = s1 & check_round_mask
s0 = u32(0) s0 = u32(0)
// recheck normalization // recheck normalization
if s2 & (mask28<<u32(1)) != 0 { if s2 & (mask28<<u32(1)) != 0 {
// C.printf("Renormalize!!") // C.printf("Renormalize!!")
@ -500,7 +499,6 @@ fn converter(pn mut PrepNumber) u64 {
s0 = q0 s0 = q0
} }
} }
// tmp := ( u64(s2 & ~mask28) << 24) | ((u64(s1) + u64(128)) >> 8) // tmp := ( u64(s2 & ~mask28) << 24) | ((u64(s1) + u64(128)) >> 8)
// C.printf("mantissa after rounding : %08x%08x%08x binexp: %d \n", s2,s1,s0,binexp) // C.printf("mantissa after rounding : %08x%08x%08x binexp: %d \n", s2,s1,s0,binexp)
// C.printf("Tmp result: %016x\n",tmp) // C.printf("Tmp result: %016x\n",tmp)
@ -518,7 +516,8 @@ fn converter(pn mut PrepNumber) u64 {
else if binexp < 1 { else if binexp < 1 {
if pn.negative { if pn.negative {
result = DOUBLE_MINUS_ZERO result = DOUBLE_MINUS_ZERO
} else { }
else {
result = DOUBLE_PLUS_ZERO result = DOUBLE_PLUS_ZERO
} }
} }

View File

@ -1,23 +1,24 @@
// Copyright (c) 2019 Alexander Medvednikov. All rights reserved. // Copyright (c) 2019 Alexander Medvednikov. All rights reserved.
// Use of this source code is governed by an MIT license // Use of this source code is governed by an MIT license
// that can be found in the LICENSE file. // that can be found in the LICENSE file.
module sync module sync
#include <pthread.h> #include <pthread.h>
fn C.pthread_mutex_init() fn C.pthread_mutex_init()
fn C.pthread_mutex_lock() fn C.pthread_mutex_lock()
fn C.pthread_mutex_unlock() fn C.pthread_mutex_unlock()
// [init_with=new_mutex] // TODO: implement support for this struct attribute, and disallow Mutex{} from outside the sync.new_mutex() function. // [init_with=new_mutex] // TODO: implement support for this struct attribute, and disallow Mutex{} from outside the sync.new_mutex() function.
pub struct Mutex { pub struct Mutex {
mutex C.pthread_mutex_t mutex C.pthread_mutex_t
} }
pub fn new_mutex() Mutex { pub fn new_mutex() Mutex {
m := Mutex{} m := Mutex{
}
C.pthread_mutex_init(&m.mutex, C.NULL) C.pthread_mutex_init(&m.mutex, C.NULL)
return m return m
} }

View File

@ -1,9 +1,7 @@
// Copyright (c) 2019 Alexander Medvednikov. All rights reserved. // Copyright (c) 2019 Alexander Medvednikov. All rights reserved.
// Use of this source code is governed by an MIT license // Use of this source code is governed by an MIT license
// that can be found in the LICENSE file. // that can be found in the LICENSE file.
module sync module sync
// [init_with=new_waitgroup] // TODO: implement support for init_with struct attribute, and disallow WaitGroup{} from outside the sync.new_waitgroup() function. // [init_with=new_waitgroup] // TODO: implement support for init_with struct attribute, and disallow WaitGroup{} from outside the sync.new_waitgroup() function.
pub struct WaitGroup { pub struct WaitGroup {
mut: mut:
@ -12,7 +10,8 @@ mut:
} }
pub fn new_waitgroup() WaitGroup { pub fn new_waitgroup() WaitGroup {
mut w := WaitGroup{} mut w := WaitGroup{
}
w.mu = sync.new_mutex() w.mu = sync.new_mutex()
return w return w
} }
@ -41,3 +40,4 @@ pub fn (wg &WaitGroup) wait() {
} }
} }
} }