12module 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 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}