Say a Platen job's second page as an error instead of dropping it (issue 1531)
mesh/merge-gate out of date: judged against trunk 199f72fed29d, the trunk is now 43e0daf6947d; judged again on a recheck or when it is merged
mesh/repo-check out of date: judged against trunk 199f72fed29d, the trunk is now 43e0daf6947d; judged again on a recheck or when it is merged
mesh/delivery superseded: a newer head of the same pull request
mesh/merge-gate out of date: judged against trunk 199f72fed29d, the trunk is now 43e0daf6947d; judged again on a recheck or when it is merged
mesh/repo-check out of date: judged against trunk 199f72fed29d, the trunk is now 43e0daf6947d; judged again on a recheck or when it is merged
mesh/delivery superseded: a newer head of the same pull request
This commit is contained in:
@@ -150,9 +150,13 @@ func (c *Client) Scan(ctx context.Context, resolution int, colorMode string) ([]
|
|||||||
c.deleteJob(job)
|
c.deleteJob(job)
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if !c.closeJob(ctx, doc) {
|
closed, extra := c.closeJob(ctx, doc)
|
||||||
|
if !closed {
|
||||||
c.deleteJob(job)
|
c.deleteJob(job)
|
||||||
}
|
}
|
||||||
|
if extra > 0 {
|
||||||
|
return nil, fmt.Errorf("the scanner gave more pages than the glass holds: %d", 1+extra)
|
||||||
|
}
|
||||||
return image, nil
|
return image, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -250,31 +254,32 @@ func (c *Client) page(ctx context.Context, doc string) ([]byte, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// maxClosing bounds the NextDocument requests that ask for the end of a job.
|
// maxClosing bounds the NextDocument requests that ask for the end of a job.
|
||||||
const maxClosing = 3
|
const maxClosing = 1
|
||||||
|
|
||||||
// closeJob asks NextDocument until the scanner answers 404, which ends the job; true when it did (or the job is
|
// closeJob asks NextDocument until the scanner answers 404, which ends the job; true when it did (or the job is
|
||||||
// gone already). A further page is read and dropped, within the bound.
|
// gone already), and how many further pages it sent instead; any is an error for the caller, for a Platen job holds
|
||||||
func (c *Client) closeJob(ctx context.Context, doc string) bool {
|
// one page. It stops at the first further page, and the job is then deleted.
|
||||||
|
func (c *Client) closeJob(ctx context.Context, doc string) (closed bool, extra int) {
|
||||||
ctx, cancel := context.WithTimeout(context.WithoutCancel(ctx), 15*time.Second)
|
ctx, cancel := context.WithTimeout(context.WithoutCancel(ctx), 15*time.Second)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
for i := 0; i < maxClosing; i++ {
|
for i := 0; i < maxClosing; i++ {
|
||||||
resp, err := c.get(ctx, doc)
|
resp, err := c.get(ctx, doc)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.logf("brother: closing the scan job: %v", err)
|
c.logf("brother: closing the scan job: %v", err)
|
||||||
return false
|
return false, 0
|
||||||
}
|
}
|
||||||
readBody(resp, maxImage)
|
readBody(resp, maxImage)
|
||||||
switch resp.StatusCode {
|
switch resp.StatusCode {
|
||||||
case http.StatusNotFound, http.StatusGone:
|
case http.StatusNotFound, http.StatusGone:
|
||||||
return true
|
return true, 0
|
||||||
case http.StatusOK:
|
case http.StatusOK:
|
||||||
continue
|
c.logf("brother: the scanner sent a further page after the glass's one")
|
||||||
|
return false, 1
|
||||||
}
|
}
|
||||||
c.logf("brother: closing the scan job: NextDocument answered %s", resp.Status)
|
c.logf("brother: closing the scan job: NextDocument answered %s", resp.Status)
|
||||||
return false
|
return false, 0
|
||||||
}
|
}
|
||||||
c.logf("brother: the scanner kept sending pages after %d requests", maxClosing)
|
return false, 0
|
||||||
return false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// deleteJob deletes the job, best effort; true when the job is gone. A job that cannot be deleted is remembered, so
|
// deleteJob deletes the job, best effort; true when the job is gone. A job that cannot be deleted is remembered, so
|
||||||
|
|||||||
@@ -203,3 +203,16 @@ func TestABusyScannerWithABrotherJobLeftIsRetriedOnceAfterItCloses(t *testing.T)
|
|||||||
t.Fatalf("%d posts, want 3 (stuck job, refused, retried)", f.posts)
|
t.Fatalf("%d posts, want 3 (stuck job, refused, retried)", f.posts)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 1531: a second page for a Platen job is an error, never a silently incomplete scan, and the job is closed.
|
||||||
|
func TestMorePagesThanTheGlassHoldsIsAnErrorAndTheJobIsClosed(t *testing.T) {
|
||||||
|
f := newFakeScanner(t, fixture(t, "scan.jpg"))
|
||||||
|
f.pages = 2
|
||||||
|
image, err := NewClient(f.base()).Scan(context.Background(), 300, "color")
|
||||||
|
if err == nil || !strings.Contains(err.Error(), "the scanner gave more pages than the glass holds") {
|
||||||
|
t.Fatalf("image %d bytes, error = %v, want more pages than the glass holds", len(image), err)
|
||||||
|
}
|
||||||
|
if f.open || f.deletes != 1 {
|
||||||
|
t.Fatalf("job open %v, %d DELETEs, want closed by 1 DELETE", f.open, f.deletes)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -33,6 +33,8 @@ type fakeScanner struct {
|
|||||||
// or the job is DELETEd; the fake does the same.
|
// or the job is DELETEd; the fake does the same.
|
||||||
open bool
|
open bool
|
||||||
delivered bool
|
delivered bool
|
||||||
|
pages int // pages a job holds beyond the first
|
||||||
|
served int
|
||||||
deletes int
|
deletes int
|
||||||
gone bool // the first NextDocument answers 410 (the job was cancelled at the device)
|
gone bool // the first NextDocument answers 410 (the job was cancelled at the device)
|
||||||
dropConn bool // the first NextDocument loses its answer
|
dropConn bool // the first NextDocument loses its answer
|
||||||
@@ -70,7 +72,7 @@ func newFakeScanner(t *testing.T, image []byte) *fakeScanner {
|
|||||||
http.Error(w, "busy", http.StatusServiceUnavailable)
|
http.Error(w, "busy", http.StatusServiceUnavailable)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
f.open, f.delivered = true, false
|
f.open, f.delivered, f.served = true, false, 0
|
||||||
// The captured Location names the real scanner's host; the test's own server stands in for it.
|
// The captured Location names the real scanner's host; the test's own server stands in for it.
|
||||||
w.Header().Set("Location", f.URL+"/eSCL/ScanJobs/7320ead5")
|
w.Header().Set("Location", f.URL+"/eSCL/ScanJobs/7320ead5")
|
||||||
w.WriteHeader(http.StatusCreated)
|
w.WriteHeader(http.StatusCreated)
|
||||||
@@ -95,12 +97,13 @@ func newFakeScanner(t *testing.T, image []byte) *fakeScanner {
|
|||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if f.delivered || f.noPage {
|
if f.noPage || (f.delivered && f.served > f.pages) {
|
||||||
f.open = false
|
f.open = false
|
||||||
http.NotFound(w, r)
|
http.NotFound(w, r)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
f.delivered = true
|
f.delivered = true
|
||||||
|
f.served++
|
||||||
w.Header().Set("Content-Type", "image/jpeg")
|
w.Header().Set("Content-Type", "image/jpeg")
|
||||||
w.Write(f.image)
|
w.Write(f.image)
|
||||||
})
|
})
|
||||||
|
|||||||
Reference in New Issue
Block a user