Mercurial > core / lisp/lib/net/fetch.lisp
changeset 584: |
35bb0d5ec95e |
parent: |
32bd859533b3
|
child: |
7ce855f76e1d |
author: |
Richard Westhaver <ellis@rwest.io> |
date: |
Sat, 10 Aug 2024 00:30:45 -0400 |
permissions: |
-rw-r--r-- |
description: |
bug fixes, added freedesktop.org.xml rule. more work on prolog/dql - considering lib/lang+forrth.. |
1 (in-package :net/fetch) 3 (define-condition invalid-path-error (error) 4 ((text :initarg :text :reader text))) 6 (defun download (url &key (output (obj/uri:uri-path (obj/uri:uri url))) 7 (if-exists :error) (progress nil) (connect-timeout net/req:*default-connect-timeout*) 9 (let ((*progress-bar-enabled* progress)) 10 (multiple-value-bind (stream status header uri) 11 (req:get url :want-stream t :force-binary t :connect-timeout connect-timeout :verbose (log:trace-p) 14 (log:debug! "download connect OK:" url) 15 (log:debug! "headers:" (hash-table-alist header)) 16 (let ((len (gethash "content-length" header))) 17 (when len (setf len (parse-integer len))) 18 (with-progress-bar (len "downloading ~a to ~a..." url output) 19 (with-open-file (out output :direction :output :element-type '(unsigned-byte 8) :if-exists if-exists) 20 (loop for c = (read-byte stream nil nil) 23 (update-progress *progress-bar* 1) 24 (write-byte c out))))) 25 (values stream status uri header)))))) 27 (defun split-file-path (path) 28 (let ((pos-last-slash (1+ (position #\/ path :from-end t)))) 29 (list (subseq path 0 pos-last-slash) 30 (subseq path pos-last-slash)))) 32 (defun split-uri-string (uri-string) 33 (let ((pu (parse-uri uri-string))) 34 (cons (uri-host pu) (split-file-path (uri-path pu))))) 36 (defun condition-path (path) 37 "Abuse parse-uri to strip possible get args from path" 38 (let ((p (parse-uri path))) (uri-path p))) 41 (handler-case (probe-file path) 42 (type-error (e) #+sbcl (declare (ignore e)) (error 'invalid-path-error 43 :text (format nil "Invalid path: ~A" path))))) 45 (defun %fetch (url-or-path &key (cache t) 49 ((is-file (condition-path url-or-path)) (condition-path url-or-path)) 50 ((is-file (condition-path (concatenate 'string dir (uri-path url-or-path)))) 51 (condition-path (concatenate 'string dir url-or-path))) 52 ((parse-uri url-or-path) 53 (let* ((tmp-pathname (split-uri-string url-or-path)) 54 (file-pathstring (format nil "~{~A~^~}" (if dir (cons dir tmp-pathname) tmp-pathname))) 55 (file-pathname (ensure-directories-exist 58 (when (is-file file-pathname) (delete-file file-pathname)) 59 (if (and cache (probe-file file-pathname)) 60 (values file-pathname 200 "OK") 61 (download url-or-path :output file-pathname))))) 62 (t (values nil 404 "Not file of url")))) 64 (defun fetch (url-or-path 67 (external-format :utf-8) 71 "Fetch file from ~url-or-location~ if not cached in ~dir~ 72 stores the file in the location specified by dir if url or file is url the file 73 is stored in ~dir~/~uri-host~/~uri-path~. 75 Note that it is important to ensure that dir and subdir if used end in a / 77 -return: path to file or stream if :stream parameter is passed 79 - url-or-path: <string> pathname or url string identifying file to be fetched. 80 - stream: resuests that fetch returns a stream 81 - cache: <T|NIL> if T looks for file in -dir and uses that as source if NIL then the a fresh copy of the file is fetched 82 - dir: location to store fetched file. 83 - flush: if T fetch does not download the file it deletes the existing file. 85 (let ((fetched-path (%fetch url-or-path :dir dir :cache cache :flush flush))) 86 (if (not fetched-path) 89 (open fetched-path :direction :input :external-format external-format)