diff options
author | bptato <nincsnevem662@gmail.com> | 2022-08-08 18:11:42 +0200 |
---|---|---|
committer | bptato <nincsnevem662@gmail.com> | 2022-08-08 18:11:54 +0200 |
commit | e2203257e07aada157be9d0a948273cb9d683072 (patch) | |
tree | 1b50351e0e9d0f6843a46c48aad3338a067384e8 /src/io/loader.nim | |
parent | e7ea9c408667a4fdfefc369e51d72c3cfb9c1ee9 (diff) | |
download | chawan-e2203257e07aada157be9d0a948273cb9d683072.tar.gz |
Refactor fileloader
Diffstat (limited to 'src/io/loader.nim')
-rw-r--r-- | src/io/loader.nim | 237 |
1 files changed, 33 insertions, 204 deletions
diff --git a/src/io/loader.nim b/src/io/loader.nim index 2efc15c2..f7ac06e6 100644 --- a/src/io/loader.nim +++ b/src/io/loader.nim @@ -1,35 +1,15 @@ import options import streams -import strutils import tables +when defined(posix): + import posix -import bindings/curl +import io/http +import io/loadertypes import types/mime import types/url -import utils/twtstr -type - HttpMethod* = enum - HTTP_CONNECT, HTTP_DELETE, HTTP_GET, HTTP_HEAD, HTTP_OPTIONS, HTTP_PATCH, - HTTP_POST, HTTP_PUT, HTTP_TRACE - -type - FileLoader* = ref object - headers*: HttpHeaderList - - HttpHeaderList = ref object - table: Table[string, seq[string]] - - LoadResult* = object - s*: Stream - contenttype*: string - status*: int - headers*: HttpHeaderList - redirect*: Option[Url] - - HeaderResult = ref object - statusline: bool - headers: HttpHeaderList +export loadertypes const DefaultHeaders = { "User-Agent": "chawan", @@ -37,192 +17,41 @@ const DefaultHeaders = { "Accept-Language": "en;q=1.0", "Pragma": "no-cache", "Cache-Control": "no-cache", -} - -proc newFileLoader*(headers: HttpHeaderList): FileLoader = +}.toTable().newHeaderList() + +proc doFork(): Pid = + result = fork() + if result == -1: + eprint "Failed to fork child process." + quit(1) + elif result != 0: + return result + discard setsid() + let pid = fork() + if pid != 0: + quit(0) + return 0 + +proc newFileLoader*(defaultHeaders: HeaderList): FileLoader = new(result) - result.headers = headers - -proc newHttpHeaderList*(): HttpHeaderList = - new(result) - -proc add(headers: HttpHeaderList, k, v: string) = - let k = k.toHeaderCase() - if k notin headers.table: - headers.table[k] = @[v] - else: - headers.table[k].add(v) - -proc `[]=`(headers: HttpHeaderList, k, v: string) = - headers.table[k.toHeaderCase()] = @[v] - -iterator pairs(headers: HttpHeaderList): (string, string) = - for k, vs in headers.table: - for v in vs: - yield (k, v) + result.defaultHeaders = defaultHeaders proc newFileLoader*(): FileLoader = - var headers = new(HttpHeaderList) - for header in DefaultHeaders: - headers[header[0]] = header[1] - newFileLoader(headers) - -proc getOrDefault*(headers: HttpHeaderList, k: string): string = - let k = k.toHeaderCase() - if k in headers.table: - headers.table[k][0] - else: - k - -# Originally from the stdlib -type - MimePart* = object - name, content: string - case isFile: bool - of true: - filename, contentType: string - fileSize: int64 - isStream: bool - else: discard - - MimeData* = ref object - content: seq[MimePart] - -proc `[]=`*(multipart: MimeData, k, v: string) = - multipart.content.add(MimePart(name: k, content: v)) - -proc curlWriteHeader(p: cstring, size: csize_t, nitems: csize_t, userdata: pointer): csize_t {.cdecl.} = - var line = newString(nitems) - for i in 0..<nitems: - line[i] = p[i] - - let headers = cast[HeaderResult](userdata) - if not headers.statusline: - headers.statusline = true - return nitems #TODO handle status line? - - let k = line.until(':') - - if k.len == line.len: - return nitems # empty line (last, before body) or invalid (=> error) - - let v = line.substr(k.len + 1).strip() - headers.headers.add(k, v) - return nitems + newFileLoader(DefaultHeaders) -proc curlWriteBody(p: cstring, size: csize_t, nmemb: csize_t, userdata: pointer): csize_t {.cdecl.} = - var s = newString(nmemb) - for i in 0..<nmemb: - s[i] = p[i] - let stream = cast[Stream](userdata) - stream.write(s) - stream.flush() - return nmemb - -template setopt(curl: CURL, opt: CURLoption, arg: typed) = - discard curl_easy_setopt(curl, opt, arg) - -template setopt(curl: CURL, opt: CURLoption, arg: string) = - discard curl_easy_setopt(curl, opt, cstring(arg)) - -template getinfo(curl: CURL, info: CURLINFO, arg: typed) = - discard curl_easy_getinfo(curl, info, arg) - -proc getPageLibcurl(loader: FileLoader, url: Url, smethod: HttpMethod = HTTP_GET, mimetype = "", body: string = "", multipart: MimeData = nil): LoadResult = - let curl = curl_easy_init() - - if curl == nil: return # fail - - let surl = url.serialize() - curl.setopt(CURLOPT_URL, surl) - - var cs = newStringStream() - curl.setopt(CURLOPT_WRITEDATA, cs) - curl.setopt(CURLOPT_WRITEFUNCTION, curlWriteBody) - - let headers = newHttpHeaderList() - let headerres = HeaderResult(headers: headers) - curl.setopt(CURLOPT_HEADERDATA, headerres) - curl.setopt(CURLOPT_HEADERFUNCTION, curlWriteHeader) - - var mime: curl_mime = nil - - case smethod - of HTTP_GET: curl.setopt(CURLOPT_HTTPGET, 1) - of HTTP_POST: - curl.setopt(CURLOPT_POST, 1) - if multipart != nil: - mime = curl_mime_init(curl) - if mime == nil: return # fail - for entry in multipart.content: - let part = curl_mime_addpart(mime) - if part == nil: return # fail - curl_mime_name(part, cstring(entry.name)) - if entry.isFile: - if entry.isStream: - curl_mime_filedata(part, cstring(entry.filename)) - else: - let fd = readFile(entry.filename) - curl_mime_data(part, cstring(fd), csize_t(fd.len)) - # may be overridden by curl_mime_filedata, so set it here - curl_mime_filename(part, cstring(entry.filename)) - else: - curl_mime_data(part, cstring(entry.content), csize_t(entry.content.len)) - curl.setopt(CURLOPT_MIMEPOST, mime) - elif body != "": - curl.setopt(CURLOPT_POSTFIELDS, cstring(body)) - curl.setopt(CURLOPT_POSTFIELDSIZE, body.len) - else: discard #TODO - - var requestHeaders = newHttpHeaderList() - requestHeaders.table = loader.headers.table - if mimetype != "": - requestHeaders["Content-Type"] = mimetype - var slist: curl_slist = nil - for k, v in requestHeaders: - let header = k & ": " & v - slist = curl_slist_append(slist, cstring(header)) - if slist != nil: - curl.setopt(CURLOPT_HTTPHEADER, slist) - - let res = curl_easy_perform(curl) - if res == CURLE_OK: # TODO handle errors - cs.setPosition(0) - result.s = cs - - let ct = headers.getOrDefault("Content-Type") - if ct != "": - result.contenttype = ct.until(';') - else: - result.contenttype = guessContentType(url.path.serialize()) - discard curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, addr result.status) - if result.status in {301, 302, 303}: #TODO 300, 304, 307 - var urlp: cstring - curl.getinfo(CURLINFO_REDIRECT_URL, addr urlp) - if urlp != nil: - let urls = $urlp - result.redirect = parseUrl(urls, some(url)) - - curl_easy_cleanup(curl) - if mime != nil: - curl_mime_free(mime) - if slist != nil: - curl_slist_free_all(slist) - -proc getPage*(loader: FileLoader, url: Url, smethod: HttpMethod = HTTP_GET, mimetype = "", body: string = "", multipart: MimeData = nil): LoadResult = - if url.scheme == "file": +proc getPage*(loader: FileLoader, url: Url, smethod: HttpMethod = HTTP_GET, mimetype = "", body = none(string), multipart = none(MimeData)): LoadResult = + case url.scheme + of "file": when defined(windows) or defined(OS2) or defined(DOS): let path = url.path.serialize_unicode_dos() else: let path = url.path.serialize_unicode() result.contenttype = guessContentType(path) result.s = newFileStream(path, fmRead) - result.status = 200 # doesn't make much sense... - elif url.scheme == "http" or url.scheme == "https": - return getPageLibcurl(loader, url, smethod, mimetype, body, multipart) - -proc getPage*(loader: FileLoader, url: string, smethod: HttpMethod = HTTP_GET, mimetype = "", body: string = "", multipart: MimeData = nil): LoadResult = - let url = parseUrl(url) - if url.isnone: - raise newException(Exception, "Invalid URL") - loader.getPage(url.get, smethod, mimetype, body, multipart) + if result.s != nil: + result.status = 200 # ok + else: + result.status = 404 # file not found + of "http", "https": + let request = loader.newRequest(url, smethod, {"Content-Type": mimetype}, body, multipart) + return getPageHttp(request) |