[cups.bugs] [MOD] STR #3791: CUPS ipp backend doesn't handle printers that don't send 100-continue

Duncan McEwan duncan at ecs.vuw.ac.nz
Mon Feb 7 23:03:40 PST 2011


DO NOT REPLY TO THIS MESSAGE.  INSTEAD, POST ANY RESPONSES TO THE LINK BELOW.

[STR New]

This is a bug report based on an investigation I've carried out that is
reported in a series of postings in cups.bugs with the title "Problem 
with FUJI XEROX ApeosPort MultifunctionDevice and the "Expect: 
100-continue header".

A brief summary of the problem is that we have a bunch of the above
mentioned photocopier/printers which don't seem to return the "100 
continue" response when CUPS sends a "Get-Printer-Attributes" request 
that includes the HTTP "Expect: 100-continue" header.  This causes CUPS 
to get out of sync with the printer, reading bogus data, which causes a 
delay of around two minutes before it finally gets back in sync and is 
able to send a "Print-Job".

I don't know the HTTP or IPP protocol standards well enough to know 
whether this printer behaviour is legal or not.  But regardless, I 
believe there are bugs in the CUPS ipp implementation that cause it to 
not handle the situation as well as it should do.

As a result of my postings to cups.bugs, the bug report STR #3778 was
filed.  This fixed a side issue that I had noted in my postings, but not
the main problem I was describing, though it did alter the way the
problem occurred slightly.

Also, as a result of STR #3778 I redid my tests on a 1.5 svn trunk 
version of CUPS and although the behaviour changed slightly the problem 
still occured in a roughly similar way.  The following description is 
based on the testing I've done on the 1.5 trunk including the STR #3778 
fixes.

1) The CUPS ipp driver calls cupsDoRequest(), which calls 
   cupsDoIORequest() which calls cupsSendRequest() to send 
   Get-Printer-Attributes with "Expect: 100-continue" in the http header

2) CUPS waits for the printer to respond with "100 continue", but 
   instead the printer responds with "HTTP/1.1 200 OK" followed by the 
   printer attributes that were requested.

3) cupsSendRequest() calls _httpUpdate() to read the response.  This
   calls httpGets() which reads the HTTP header into http->buffer and 
   then returns "HTTP/1.1 200 OK" line.  At this point I don't think the
   IPP portion of the response is buffered because it was sent by the
   printer in a separate segment and so CUPS hasn't read it yet, but I
   don't believe that matters to what happens next.

4) cupsGetResponse() is called by cupsDoIORequest().  Because 
   http->status is 200 rather than the 100 that it is expected to be if 
   the printer had responded with "100 continue", the loop calling 
   httpUpdate() doesn't get executed to read the rest of the HTTP 
   header.  So then ippRead() is called which I believe is expecting to 
   see the IPP portion of the packet, but instead sees the remainder of 
   the http header still stored in http->buffer.  The following extract 
   from a debug trace shows this:

        03:20:03.829 cupsSendRequest: Waiting for 100-continue...
        03:20:03.829 _httpWait(http=0xbb531000, msec=1000, usessl=1)
        03:20:03.836 _httpWait: returning with nfds=1, errno=2...
        03:20:03.836 _httpUpdate(http=0xbb531000, status=0xbfbe9004),
state=HTTP_POST_SEND
        03:20:03.836 httpGets(line=0xbfbe0fb8, length=32768,
http=0xbb531000)
        03:20:03.836 httpGets: read 158 bytes...
        03:20:03.836 httpGets: Returning "HTTP/1.1 200 OK"
        03:20:03.836 _httpUpdate: Got "HTTP/1.1 200 OK"
        03:20:03.836 cupsSendRequest: status=200
        03:20:03.836 cupsDoIORequest: status=200
        03:20:03.836 cupsGetResponse(http=0xbb531000, resource="/ipp")
        03:20:03.836 cupsGetResponse: Update loop, http->status=200...
        03:20:03.836 cupsGetResponse: status=200
        03:20:03.836 ippNew()
        03:20:03.836 ippNew: Returning 0xbb401130
        03:20:03.836 ippRead(http=0xbb531000, ipp=0xbb401130),
data_remaining=0
        03:20:03.836 ippRead: http->state=7, http->used=141
        03:20:03.836 ippReadIO(src=0xbb531000, cb=0xbbbb374d, blocking=1,
parent=0x0, ipp=0xbb401130)
        03:20:03.836 ippReadIO: ipp->state=0
        03:20:03.836 ippReadIO: version=67.97
        03:20:03.836 ippReadIO: op_status=6368
        03:20:03.836 ippReadIO: request_id=1697465199
        03:20:03.836 ippReadIO: ipp->current=0x0, ipp->prev=0x0
        03:20:03.836 ippReadIO: value tag=6e(UNKNOWN)
        03:20:03.836 ippReadIO: name length=29810
        03:20:03.836 httpRead2(http=0xbb531000, buffer=0xbb40e082,
length=29680)
        03:20:03.836 httpRead2: data_remaining=-141
        03:20:03.836 ippReadIO: unable to read name!
        03:20:03.836 cupsGetResponse: IPP read error!
        03:20:03.836 ippDelete(ipp=0xbb401130)
        03:20:03.836 _cupsSetError:
last_error=server-error-service-unavailable, last_status_message="(null)"

   Note the bogus version that gets reported.  The characters 67 and 97
   correspond to the characters "C" and "a", which look to me like the
   next part of the HTTP headers "Cache-Control: ...".  However, the
   attempt to read the IPP response has caused the remaining part of
   the HTTP header in http->buffer to be consumed.

5) The failure to read a valid IPP header and the return of the error 
   status "server-error-service-unavailable" causes cupsDoIORequest() to 
   retry the cupsSendRequest().  This again results in calls to 
   _httpUpdate() and httpGets().  By now the IPP portion of the printers 
   response has arrived and so this data is read into http->buffer by    
   ipp_read_http().  A debug trace shows the following:

       ...
       03:20:03.836 cupsSendRequest: Waiting for 100-continue...
       03:20:03.836 _httpWait(http=0xbb531000, msec=1000, usessl=1)
       03:20:03.837 _httpWait: returning with nfds=1, errno=0...
       03:20:03.837 _httpUpdate(http=0xbb531000, status=0xbfbe9004),
state=HTTP_POST_SEND
       03:20:03.837 httpGets(line=0xbfbe0fb8, length=32768,
http=0xbb531000)
       03:20:03.837 httpGets: read 691 bytes...
       03:20:03.837 httpGets: Returning "2a7"
       03:20:03.837 _httpUpdate: Got "2a7"
       03:20:03.837 _httpUpdate: Bad response line "2a7"!
       03:20:03.837 cupsSendRequest: status=-1
       03:20:03.837 cupsDoIORequest: status=-1
       03:20:03.837 httpFlush(http=0xbb531000), state=HTTP_POST_SEND
       03:20:03.837 httpRead2(http=0xbb531000, buffer=0xbfbe7018,
length=8192)
       03:20:03.837 httpRead2: data_remaining=0
       03:20:03.837 cupsDoIORequest: status=-1
       03:20:03.837 cupsArrayAdd(a=0xbb4030c0, e=0xbb407060)
       03:20:03.837 _cupsSetError: last_error=server-error-internal-error,
last_status_message="Unknown"
       ...

   From a tcpdump wire trace I have the "2a7" that is read corresponds 
   to the chunk length of the chunked response from the printer.  This 
   time there isn't even a valid HTTP header buffered in http->buffer, 
   which results in -1 (HTTP_ERROR) being returned to cupsDoIORequest().  
   This is mapped into "server-error-internal-error" and results in 
   cupsDoIORequest() breaking out of its while loop and eventually 
   returning back to the ipp backend.

6) The ipp backend sees the internal error indication, sleeps for 10 
   seconds and then retries.  The second of the above two error 
   situations reoccurs several times with successive portions of the 
   printers first IPP response being misinterpreted as HTTP headers.

7) Eventually the entire first buffered response is consumed and so the
   next response is read.  I'm not 100% sure what happens that is 
   different this time, but it appears from my debug trace that valid
   HTTP and IPP responses are read and so the Get-Printer-Attributes 
   request finally succeeds.

8) Now the ipp backend attempts to send a "Validate-Job" request.  This
   fails, I believe for the same reasons that the original
   Get-Printer-Attributes request failed, and so ipp aborts the entire
   print attempt.

With the CUPS 1.4.4 that I originally started this investigation with
the situation was a little different.

Prior to the fix for STR #3778 going in, the call to _httpUpdate() was 
actually a call to httpUpdate() and so the consuming of data from 
http->buffer was a little different.  But a similar situation where data 
was read from the buffer and incorrectly interpreted as an HTTP header 
was observed.

Also, no "Validate-Job" request was sent, so the print job actually 
succeeded eventually, after a delay of approximately two minutes.  I can 
provide more details of the sequence of events for this version if 
required.  But I figured if the problem could be solved for the 1.5
trunk version first, then a similar fix for the 1.4 branch might be
fairly obvious, without needing to go through all that detail...

It may be the case that these printers are violating the RFC by not 
sending "100 continue" when a they receive a request with an "Expect: 
100-continue" header.  From my quick reading of the RFC, it seemed that 
this behaviour was legal as long as an entire request had been received, 
but there could be subtleties that I missed.  Regardless, hopefully the 
CUPS software can at least be fixed to detect this situation and report 
it correctly.

Thanks,

Duncan

Link: http://www.cups.org/str.php?L3791
Version: 1.4-current





More information about the cups-devel mailing list