sph-lib includes a multipart-form-data parser that can do partial reading and supports unlimited nesting of parts
below is an example that reads all data and saves it in a list. this is what the simpler to use "html-read-multipart-form-data" does internally
(import (sph) (sph web html)) (define-as example-data open-input-string "-----------------------------19737173311706079401624261242\r\nContent-Disposition: form-data; name=\"content\"\r\n\r\naeu\r\n-----------------------------19737173311706079401624261242\r\nContent-Disposition: form-data; name=\"names\"\r\n\r\n[\"a\",\"b\"]\r\n-----------------------------19737173311706079401624261242\r\nContent-Disposition: form-data; name=\"type\"\r\n\r\ndocl\r\n-----------------------------19737173311706079401624261242--\r\n") (html-fold-multipart-form-data (l (header fold-lines result) (fold-lines (l (line result next) (next (pair line result))) (list) (l (result-fold-lines result next-part) (next-part (pair (pair header (if (null? result-fold-lines) result-fold-lines (string-join (reverse (pair (string-drop-right (first result-fold-lines) 2) (tail result-fold-lines))) ""))) result))))) (l (header port result) (pair (pair header (html-read-multipart-form-data port)) result)) (list) example-data)
the result
(((("Content-Disposition" ("form-data" . #t) ("name" . "type"))) . "docl") ((("Content-Disposition" ("form-data" . #t) ("name" . "names"))) . "[\"a\",\"b\"]") ((("Content-Disposition" ("form-data" . #t) ("name" . "content"))) . "aeu"))
(html-multipart-form-data-ref result "submit-name")
((("Content-Disposition" ("form-data" . #t) ("name" . "submit-name"))) . "Larry")