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 return '${parsed.scheme}://${parsed.host}'35}3637pub fn (mut w Web) get(key string) !Object {38 response := http.fetch(39 url: w.url(key)40 method: .get41 enable_http2: false42 allow_redirect: false43 )!44 if response.status_code == 404 {45 return Object{}46 }47 if response.status_code < 200 || response.status_code >= 300 {48 return error('web GET ${key} failed: ${response.status_code}')49 }50 return Object{ found: true, body: response.body.bytes(), tag: response.header.get_custom('etag', http.HeaderQueryConfig{}) or { '' } }51}5253pub fn (mut w Web) list(prefix string) ![]string {54 response := http.fetch(55 url: w.url(prefix)56 method: .get57 enable_http2: false58 allow_redirect: false59 )!60 if response.status_code == 404 {61 return []string{}62 }63 if response.status_code < 200 || response.status_code >= 300 {64 return error('web GET ${prefix} failed: ${response.status_code}')65 }66 mut result := []string{}67 for id in response.body.split_into_lines() {68 if id.trim_space() != '' { result << '${prefix}/${id.trim_space()}' }69 }70 return result71}7273pub fn (mut w Web) put_if_absent(key string, body []u8) !bool {74 return error('${w.repo(key)} is read-only.')75}7677pub fn (mut w Web) put_if_match(key string, body []u8, tag string) !bool {78 return error('${w.repo(key)} is read-only.')79}8081pub fn (mut w Web) delete(key string) ! {82 return error('${w.repo(key)} is read-only.')83}8485fn (w &Web) url(key string) string {86 parts := key.split('/')87 return '${w.base}/${parts[0]}/koh/${parts[1..].join('/')}'88}8990fn (w &Web) repo(key string) string {91 return '${w.base}/${key.all_before('/')}'92}