From mboxrd@z Thu Jan 1 00:00:00 1970 Path: news.gmane.io!.POSTED.blaine.gmane.org!not-for-mail From: Christopher Baines Newsgroups: gmane.lisp.guile.devel Subject: [PATCH 2/2] web: Don't hide missing data in the chunked input port. Date: Thu, 30 Jun 2022 19:15:54 +0100 Message-ID: <20220630181554.25772-2-mail@cbaines.net> References: <20220630181554.25772-1-mail@cbaines.net> Mime-Version: 1.0 Content-Transfer-Encoding: 8bit Injection-Info: ciao.gmane.io; posting-host="blaine.gmane.org:116.202.254.214"; logging-data="23297"; mail-complaints-to="usenet@ciao.gmane.io" To: guile-devel@gnu.org Original-X-From: guile-devel-bounces+guile-devel=m.gmane-mx.org@gnu.org Thu Jun 30 20:18:17 2022 Return-path: Envelope-to: guile-devel@m.gmane-mx.org Original-Received: from lists.gnu.org ([209.51.188.17]) by ciao.gmane.io with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.92) (envelope-from ) id 1o6yk5-0005u2-HG for guile-devel@m.gmane-mx.org; Thu, 30 Jun 2022 20:18:17 +0200 Original-Received: from localhost ([::1]:37106 helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1o6yk4-00047g-Et for guile-devel@m.gmane-mx.org; Thu, 30 Jun 2022 14:18:16 -0400 Original-Received: from eggs.gnu.org ([2001:470:142:3::10]:36510) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1o6yi3-0003np-1d for guile-devel@gnu.org; Thu, 30 Jun 2022 14:16:13 -0400 Original-Received: from mira.cbaines.net ([2a01:7e00:e000:2f8:fd4d:b5c7:13fb:3d27]:34151) by eggs.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1o6yhp-0002L9-7Z for guile-devel@gnu.org; Thu, 30 Jun 2022 14:16:10 -0400 Original-Received: from localhost (unknown [IPv6:2a02:8010:68c1:0:54d1:d5d4:280e:f699]) by mira.cbaines.net (Postfix) with ESMTPSA id A5F7927BBEA for ; Thu, 30 Jun 2022 19:15:54 +0100 (BST) Original-Received: from localhost (localhost [local]) by localhost (OpenSMTPD) with ESMTPA id c29ef7b8 for ; Thu, 30 Jun 2022 18:15:54 +0000 (UTC) X-Mailer: git-send-email 2.36.1 In-Reply-To: <20220630181554.25772-1-mail@cbaines.net> Received-SPF: pass client-ip=2a01:7e00:e000:2f8:fd4d:b5c7:13fb:3d27; envelope-from=mail@cbaines.net; helo=mira.cbaines.net X-Spam_score_int: -18 X-Spam_score: -1.9 X-Spam_bar: - X-Spam_report: (-1.9 / 5.0 requ) BAYES_00=-1.9, SPF_HELO_PASS=-0.001, SPF_PASS=-0.001, T_SCC_BODY_TEXT_LINE=-0.01, UNPARSEABLE_RELAY=0.001 autolearn=ham autolearn_force=no X-Spam_action: no action X-BeenThere: guile-devel@gnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: "Developers list for Guile, the GNU extensibility library" List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: guile-devel-bounces+guile-devel=m.gmane-mx.org@gnu.org Original-Sender: "guile-devel" Xref: news.gmane.io gmane.lisp.guile.devel:21248 Archived-At: This port is of limited use if it cannot be used reliably. Rather than behaving as if the input has finished when it ends unexpectedly, instead raise an exception. * module/web/http.scm (make-chunked-input-port): Raise an exception on premature termination. (&chunked-input-ended-prematurely): New exception type. (chunked-input-ended-prematurely-error?): New procedure. * test-suite/tests/web-http.test (pass-if-named-exception): Rename to pass-if-named-exception. (pass-if-named-exception): New syntax. ("Exception on premature chunk end"): New test for this behaviour. --- doc/ref/web.texi | 3 +++ module/web/http.scm | 18 ++++++++++++++++-- test-suite/tests/web-http.test | 25 +++++++++++++++++-------- 3 files changed, 36 insertions(+), 10 deletions(-) diff --git a/doc/ref/web.texi b/doc/ref/web.texi index 93cd0214f..b04d328b7 100644 --- a/doc/ref/web.texi +++ b/doc/ref/web.texi @@ -1117,6 +1117,9 @@ Returns a new port, that transparently reads and decodes chunk-encoded data from @var{port}. If no more chunk-encoded data is available, it returns the end-of-file object. When the port is closed, @var{port} will also be closed, unless @var{keep-alive?} is true. + +If the chunked input ends prematurely, a +@code{&chunked-input-ended-promaturely} exception will be raised. @end deffn @example diff --git a/module/web/http.scm b/module/web/http.scm index 163c52176..b3ea076ae 100644 --- a/module/web/http.scm +++ b/module/web/http.scm @@ -38,6 +38,7 @@ #:use-module (ice-9 q) #:use-module (ice-9 binary-ports) #:use-module (ice-9 textual-ports) + #:use-module (ice-9 exceptions) #:use-module (rnrs bytevectors) #:use-module (web uri) #:export (string->header @@ -67,6 +68,8 @@ read-response-line write-response-line + &chunked-input-error-prematurely + chunked-input-ended-prematurely-error? make-chunked-input-port make-chunked-output-port @@ -1935,6 +1938,17 @@ treated specially, and is just returned as a plain string." ;; Chunked Responses +(define &chunked-input-ended-prematurely + (make-exception-type '&chunked-input-error-prematurely + &external-error + '())) + +(define make-chunked-input-ended-prematurely-error + (record-constructor &chunked-input-ended-prematurely)) + +(define chunked-input-ended-prematurely-error? + (record-predicate &chunked-input-ended-prematurely)) + (define (read-chunk-header port) "Read a chunk header from PORT and return the size in bytes of the upcoming chunk." @@ -1987,8 +2001,8 @@ closed it will also close PORT, unless the KEEP-ALIVE? is true." ask-for))) (cond ((eof-object? read) ;premature termination - (set! finished? #t) - num-read) + (raise-exception + (make-chunked-input-ended-prematurely-error))) (else (let ((left (- remaining read))) (set! remaining left) diff --git a/test-suite/tests/web-http.test b/test-suite/tests/web-http.test index 86fbc0e1a..796c44dd9 100644 --- a/test-suite/tests/web-http.test +++ b/test-suite/tests/web-http.test @@ -28,16 +28,19 @@ #:use-module (test-suite lib)) -(define-syntax pass-if-named-exception +(define-syntax pass-if-expected-exception (syntax-rules () - ((_ name k pat exp) + ((_ name exception-predicate? exp) (pass-if name - (catch 'k - (lambda () exp (error "expected exception" 'k)) - (lambda (k message args) - (if (string-match pat message) - #t - (error "unexpected exception" message args)))))))) + (with-exception-handler + (lambda (exn) + (if (exception-predicate? exn) + #t + (error "unexpected exception" exn))) + (lambda () + exp + #f) + #:unwind? #t))))) (define-syntax pass-if-only-parse (syntax-rules () @@ -486,6 +489,12 @@ (port (make-chunked-input-port (open-input-string str)))) (get-string-all port))) + (pass-if-expected-exception "Exception on premature chunk end" + chunked-input-ended-prematurely-error? + (let* ((str "b\r\nFirst chunk\r\nc\r\nSecond chun") + (port (make-chunked-input-port (open-input-string str)))) + (get-string-all port))) + (pass-if-equal (call-with-output-string (lambda (out-raw) -- 2.36.1