web.v

2.6 KB · raw

1//! The web backend: read-only access to a public Koh mirror.2module remote34import net.http5import net.urllib67pub struct Web {8pub mut:9	base string10}1112pub fn new_web(base string) !Web {13	return Web{ base: kepr_host(base)! }14}1516pub fn kepr_host(value string) !string {17	for letter in value {18		if letter <= ` ` {19			return error('"${value}" is not a Kepr address — try kepr.uk')20		}21	}22	text := if value.starts_with('http://') || value.starts_with('https://') {23		value24	} else {25		'https://${value}'26	}27	parsed := urllib.parse(text) or { return error('"${value}" is not a Kepr address — try kepr.uk') }28	if parsed.scheme !in ['http', 'https'] || parsed.host == '' || parsed.path.trim_right('/') != '' || parsed.raw_query != '' || parsed.fragment != '' {29		return error('"${value}" is not a Kepr address — try kepr.uk')30	}31	if text.all_after('//').contains('@') {32		return error('"${value}" is not a Kepr address — try kepr.uk')33	}34	// A hostname is case-insensitive, so the stored form is the lower-case one.35	return '${parsed.scheme}://${parsed.host.to_lower()}'36}3738pub fn (mut w Web) get(key string) !Object {39	response := http.fetch(40		url:            w.url(key)41		method:         .get42		enable_http2:   false43		allow_redirect: false44	)!45	if response.status_code == 404 {46		return Object{}47	}48	if response.status_code < 200 || response.status_code >= 300 {49		return error('web GET ${key} failed: ${response.status_code}')50	}51	return Object{ found: true, body: response.body.bytes(), tag: response.header.get_custom('etag', http.HeaderQueryConfig{}) or { '' } }52}5354pub fn (mut w Web) list(prefix string) ![]string {55	response := http.fetch(56		url:            w.url(prefix)57		method:         .get58		enable_http2:   false59		allow_redirect: false60	)!61	if response.status_code == 404 {62		return []string{}63	}64	if response.status_code < 200 || response.status_code >= 300 {65		return error('web GET ${prefix} failed: ${response.status_code}')66	}67	mut result := []string{}68	for id in response.body.split_into_lines() {69		if id.trim_space() != '' { result << '${prefix}/${id.trim_space()}' }70	}71	return result72}7374pub fn (mut w Web) put_if_absent(key string, body []u8) !bool {75	return error('${w.repo(key)} is read-only.')76}7778pub fn (mut w Web) put_if_match(key string, body []u8, tag string) !bool {79	return error('${w.repo(key)} is read-only.')80}8182pub fn (mut w Web) delete(key string) ! {83	return error('${w.repo(key)} is read-only.')84}8586fn (w &Web) url(key string) string {87	parts := key.split('/')88	return '${w.base}/${parts[0]}/koh/${parts[1..].join('/')}'89}9091fn (w &Web) repo(key string) string {92	return '${w.base}/${key.all_before('/')}'93}