aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--nar-herder/server.scm127
1 files changed, 127 insertions, 0 deletions
diff --git a/nar-herder/server.scm b/nar-herder/server.scm
index 68e3219..4743627 100644
--- a/nar-herder/server.scm
+++ b/nar-herder/server.scm
@@ -18,15 +18,33 @@
(define-module (nar-herder server)
#:use-module (srfi srfi-1)
+ #:use-module (srfi srfi-11)
+ #:use-module (srfi srfi-34)
#:use-module (ice-9 match)
+ #:use-module (ice-9 binary-ports)
+ #:use-module (rnrs bytevectors)
#:use-module (web uri)
#:use-module (web response)
#:use-module (web request)
#:use-module (logging logger)
#:use-module (prometheus)
#:use-module (json)
+ #:use-module ((system foreign)
+ #:select (bytevector->pointer pointer->bytevector))
+ #:use-module (guix store)
+ #:use-module (guix base32)
+ #:use-module (guix serialization)
+ #:use-module ((guix utils)
+ #:select (decompressed-port))
+ #:use-module ((guix build utils)
+ #:select (dump-port))
#:use-module (nar-herder database)
#:use-module (nar-herder storage)
+
+
+ #:use-module (ice-9 textual-ports)
+
+
#:export (make-request-handler))
(define* (render-json json #:key (extra-headers '())
@@ -73,6 +91,66 @@
(metric-set metric value))))
metrics))))
+(define (serve-fixed-output-file input compression proc)
+ ;; TODO It's hard with fold-archive from (guix serialization) to
+ ;; read just the singular file from the archive, so the following
+ ;; procedures allow to just read the parts prior to the file, which
+ ;; includes the file length
+
+ (define (sub-bytevector bv len)
+ (define max (bytevector-length bv))
+ (cond ((= len max) bv)
+ ((< len max)
+ ;; Yes, this is safe because the result of each conversion procedure
+ ;; has its life cycle synchronized with that of its argument.
+ (pointer->bytevector (bytevector->pointer bv) len))
+ (else
+ (error "sub-bytevector called to get a super bytevector"))))
+
+ (define (read-long-long p)
+ (let ((b (get-bytevector-n p 8)))
+ (bytevector-u64-ref b 0 (endianness little))))
+
+ (define (read-int p)
+ (let ((b (get-bytevector-n p 8)))
+ (bytevector-u32-ref b 0 (endianness little))))
+
+ (define (read-byte-string p)
+ (let* ((len (read-int p))
+ (m (modulo len 8))
+ (pad (if (zero? m) 0 (- 8 m)))
+ (bv (get-bytevector-n p (+ len pad))))
+ (sub-bytevector bv len)))
+
+ (define (read-string p)
+ (utf8->string (read-byte-string p)))
+
+ (let*-values (((port pids)
+ (decompressed-port
+ (string->symbol compression)
+ input)))
+
+ ;; The decompressor can be an external program, so wait for it to
+ ;; exit
+ (every (compose zero? cdr waitpid) pids)
+
+ (match (list
+ (read-string port)
+ (read-string port)
+ (read-string port)
+ (read-string port)
+
+ (match (read-string port)
+ ("contents" 'regular)
+ ("executable"
+ (match (list (read-string port) (read-string port))
+ (("" "contents") 'executable))))
+
+ (read-long-long port))
+ (("nix-archive-1" "(" "type" "regular" type size)
+
+ (proc port size)))))
+
(define* (make-request-handler database storage-root
#:key ttl negative-ttl logger
metrics-registry)
@@ -178,6 +256,55 @@
#f)
(values (build-response #:code 404)
"404"))))
+ (('GET "file" name algo hash)
+ (guard (c ((invalid-base32-character? c)
+ (values (build-response #:code 404)
+ "404")))
+ (let ((hash-bytevector (nix-base32-string->bytevector hash)))
+ (if (and (string=? algo "sha256") (= 32 (bytevector-length
+ hash-bytevector)))
+ (let* ((store-path
+ (fixed-output-path name hash-bytevector
+ #:hash-algo
+ (string->symbol algo)
+ #:recursive? #f))
+ (store-path-hash
+ (store-path-hash-part store-path))
+ (narinfo-files
+ (database-select-narinfo-files
+ database
+ store-path-hash))
+ (selected-narinfo-file
+ ;; TODO Select intelligently
+ (first narinfo-files)))
+
+ (if selected-narinfo-file
+ (let* ((url
+ (assq-ref selected-narinfo-file 'url))
+ (filename
+ (string-append storage-root
+ (uri-decode url))))
+
+ (serve-fixed-output-file
+ (open-input-file filename)
+ (assq-ref selected-narinfo-file
+ 'compression)
+ (lambda (nar-port bytes)
+ (values `((content-type . (application/octet-stream
+ (charset . "ISO-8859-1")))
+ (content-length . ,bytes))
+ (lambda (output-port)
+ (dump-port nar-port
+ output-port
+ bytes)
+
+ (close-port output-port))))))
+
+ (values (build-response #:code 404)
+ "404")))
+ (values (build-response #:code 404)
+ "404")))))
+
(('GET "recent-changes")
(let ((query-parameters
(or (and=> (uri-query (request-uri request))