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.
// Use of this source code is governed by an MIT license
// that can be found in the LICENSE file.
import http
import json
import sync
@ -64,10 +63,13 @@ fn main() {
}
ids = tmp
}
wg := sync.new_waitgroup()
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.wg = &wg
fetcher.wg.add(ids.len)

View File

@ -181,9 +181,10 @@ fn (p mut Parser) fnext() {
comment := comment_token.lit
// Newline before the comment, but not between two // comments,
// 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 &&
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()
}
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.
// Use of this source code is governed by an MIT license
// that can be found in the LICENSE file.
module http
import strings
// On linux, prefer a localy build openssl, because it is
// much more likely for it to be newer, than the system
// openssl from libssl-dev. If there is no local openssl,
// the next flag is harmless, since it will still use the
// (older) system openssl.
#flag linux -I/usr/local/include/openssl -L/usr/local/lib
#flag -l ssl -l crypto
// MacPorts
#flag darwin -I/opt/local/include
@ -20,33 +17,67 @@ import strings
// Brew
#flag darwin -I/usr/local/opt/openssl/include
#flag darwin -L/usr/local/opt/openssl/lib
#include <openssl/ssl.h>
struct C.SSL {
}
fn C.SSL_library_init()
fn C.TLSv1_2_method() voidptr
fn C.SSL_CTX_set_options()
fn C.SSL_CTX_new() voidptr
fn C.SSL_CTX_set_verify_depth()
fn C.SSL_CTX_load_verify_locations() int
fn C.BIO_new_ssl_connect() voidptr
fn C.BIO_set_conn_hostname() int
fn C.BIO_get_ssl()
fn C.SSL_set_cipher_list() int
fn C.BIO_do_connect() int
fn C.BIO_do_handshake() int
fn C.SSL_get_peer_certificate() int
fn C.SSL_get_verify_result() int
fn C.SSL_set_tlsext_host_name() int
fn C.BIO_puts()
fn C.BIO_read()
fn C.BIO_free_all()
fn C.SSL_CTX_free()
fn init() int {
C.SSL_library_init()
return 1
@ -109,6 +140,6 @@ fn (req &Request) ssl_do(port int, method, host_name, path string) ?Response {
if !isnil(ctx) {
C.SSL_CTX_free(ctx)
}
return parse_response(sb.str())
}

View File

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

View File

@ -1,14 +1,16 @@
// Copyright (c) 2019 Alexander Medvednikov. All rights reserved.
// Use of this source code is governed by an MIT license
// that can be found in the LICENSE file.
module http
import os
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)
return true
// download_file_with_progress(url, out, empty, empty)
}

View File

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

View File

@ -1,7 +1,6 @@
// Copyright (c) 2019 Alexander Medvednikov. All rights reserved.
// Use of this source code is governed by an MIT license
// that can be found in the LICENSE file.
module http
import net.urllib
@ -79,7 +78,9 @@ pub fn new_request(typ, _url, _data string) ?Request {
}
pub fn get_text(url string) string {
resp := get(url) or { return '' }
resp := get(url) or {
return ''
}
return resp.text
}
@ -116,18 +117,29 @@ pub fn (req &Request) do() ?Response {
if req.typ == 'POST' {
// 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 resp := Response{}
mut resp := Response{
}
mut no_redirects := 0
for {
if no_redirects == max_redirects { 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) }
if no_redirects == max_redirects {
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
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
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
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' }
mut nport := url.port().int()
if nport == 0 {
if scheme == 'http' { nport = 80 }
if scheme == 'https' { nport = 443 }
if scheme == 'http' {
nport = 80
}
if scheme == 'https' {
nport = 443
}
}
// println('fetch $method, $scheme, $host_name, $nport, $path ')
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 res
} else if scheme == 'http' {
}
else if scheme == 'http' {
// println('http_do( $nport, $method, $host_name, $path )')
res := req.http_do(nport, method, host_name, path) or {
return error(err)
@ -196,11 +213,9 @@ fn parse_response(resp string) Response {
val := h[pos + 2..]
headers[key] = val.trim_space()
}
if headers['Transfer-Encoding'] == 'chunked' {
text = chunked.decode(text)
}
return Response{
status_code: status_code
headers: headers
@ -217,12 +232,7 @@ fn (req &Request) build_request_headers(method, host_name, path string) string {
if req.data.len > 0 {
uheaders << 'Content-Length: ${req.data.len}\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
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
}
pub fn unescape_url(s string) string {
@ -242,3 +252,4 @@ pub fn escape(s string) string {
}
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
mut sb := strings.new_builder(100)
s := req.build_request_headers(method, host_name, path)
client := net.dial( host_name, port) or { return error(err) }
client.send( s.str, s.len ) or {}
client := net.dial(host_name, port) or {
return error(err)
}
client.send(s.str, s.len) or {
}
for {
readbytes := client.crecv(rbuffer, bufsize)
if readbytes < 0 { return error('http.request.http_do: error reading response. readbytes=$readbytes') }
if readbytes == 0 { break }
if readbytes < 0 {
return error('http.request.http_do: error reading response. readbytes=$readbytes')
}
if readbytes == 0 {
break
}
sb.write(tos(rbuffer, readbytes))
}
client.close() or {}
client.close() or {
}
return parse_response(sb.str())
}

View File

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

View File

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

View File

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

View File

@ -10,7 +10,6 @@ pub:
proto int
}
struct C.in_addr {
mut:
s_addr int
@ -35,27 +34,57 @@ mut:
ai_next voidptr
}
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
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
// create socket
pub fn new_socket(family int, _type int, proto int) ?Socket {
sockfd := C.socket(family, _type, proto)
one := 1
// 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
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_port = C.htons(port)
addr.sin_addr.s_addr = C.htonl(C.INADDR_ANY)
@ -148,7 +178,8 @@ pub fn (s Socket) accept() ?Socket {
$if debug {
println('accept()')
}
addr := C.sockaddr_storage{}
addr := C.sockaddr_storage{
}
size := 128 // sizeof(sockaddr_storage)
sockfd := C.accept(s.sockfd, &addr, &size)
if sockfd < 0 {
@ -165,7 +196,8 @@ pub fn (s Socket) accept() ?Socket {
// connect to given addrress and port
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_socktype = s._type
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_addr = C.NULL
hints.ai_next = C.NULL
info := &C.addrinfo(0)
sport := '$port'
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
for {
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
if dlen <= 0 { break }
if dlen <= 0 {
break
}
dptr += sbytes
}
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 {
return C.read(s.sockfd, buffer, buffersize)
}
// 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.
pub fn (s Socket) crecv(buffer byteptr, buffersize int) int {
@ -243,8 +278,7 @@ pub fn (s Socket) close() ?int {
mut shutdown_res := 0
$if windows {
shutdown_res = C.shutdown(s.sockfd, C.SD_BOTH)
}
$else {
} $else {
shutdown_res = C.shutdown(s.sockfd, C.SHUT_RDWR)
}
// TODO: should shutdown throw an error? close will
@ -252,18 +286,15 @@ pub fn (s Socket) close() ?int {
// if shutdown_res < 0 {
// return error('net.close: shutdown failed with $shutdown_res')
// }
mut res := 0
$if windows {
res = C.closesocket(s.sockfd)
}
$else {
} $else {
res = C.close(s.sockfd)
}
if res < 0 {
return error('net.close: failed with $res')
}
return 0
}
@ -272,12 +303,13 @@ pub const (
MAX_READ = 400
MSG_PEEK = 0x02
)
// write - write a string with CRLF after it over the socket s
pub fn (s Socket) write(str string) ?int {
line := '$str$CRLF'
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
}
@ -288,8 +320,12 @@ pub fn (s Socket) read_line() string {
for {
mut line := '' // The current line. Can be a partial without \n in it.
n := C.recv(s.sockfd, buf, MAX_READ - 1, MSG_PEEK)
if n == -1 { return res }
if n == 0 { return res }
if n == -1 {
return res
}
if n == 0 {
return res
}
buf[n] = `\0`
mut eol_idx := -1
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.
for {
n := C.recv(s.sockfd, buf, MAX_READ - 1, 0)
if n == -1 { return res }
if n == 0 { return res }
if n == -1 {
return res
}
if n == 0 {
return res
}
res += tos_clone(buf)
}
return res
}
pub fn (s Socket) get_port() int {
mut addr := C.sockaddr_in {}
mut addr := C.sockaddr_in{
}
size := 16 // sizeof(sockaddr_in)
C.getsockname(s.sockfd, &addr, &size)
return C.ntohs(addr.sin_port)
}

View File

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

View File

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

View File

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

View File

@ -1,23 +1,24 @@
// Copyright (c) 2019 Alexander Medvednikov. All rights reserved.
// Use of this source code is governed by an MIT license
// that can be found in the LICENSE file.
module sync
#include <pthread.h>
fn C.pthread_mutex_init()
fn C.pthread_mutex_lock()
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.
pub struct Mutex {
mutex C.pthread_mutex_t
}
pub fn new_mutex() Mutex {
m := Mutex{}
m := Mutex{
}
C.pthread_mutex_init(&m.mutex, C.NULL)
return m
}

View File

@ -1,9 +1,7 @@
// Copyright (c) 2019 Alexander Medvednikov. All rights reserved.
// Use of this source code is governed by an MIT license
// that can be found in the LICENSE file.
module sync
// [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 {
mut:
@ -12,7 +10,8 @@ mut:
}
pub fn new_waitgroup() WaitGroup {
mut w := WaitGroup{}
mut w := WaitGroup{
}
w.mu = sync.new_mutex()
return w
}
@ -41,3 +40,4 @@ pub fn (wg &WaitGroup) wait() {
}
}
}