Skip to content

iss41: notes on issues and undecodable response #42

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion adapter-http.go
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,9 @@ func (h *HttpAdapter) SendRequest(url string, req *Request, additionalResponseDa
return nil, fmt.Errorf("unable to buffer response: %w", err)
}

ippResp, err := NewResponseDecoder(buf).Decode(additionalResponseData)
decoder := NewResponseStateMachine()
ippResp, err := decoder.Decode(buf)

if err != nil {
return nil, err
}
Expand Down
4 changes: 3 additions & 1 deletion adapter-socket.go
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,9 @@ func (h *SocketAdapter) SendRequest(url string, r *Request, additionalData io.Wr
httpResp.Body.Close()

// decode reply
ippResp, err := NewResponseDecoder(buf).Decode(additionalData)
decoder := NewResponseStateMachine()
ippResp, err := decoder.Decode(buf)

if err != nil {
return nil, fmt.Errorf("unable to decode IPP response: %w", err)
}
Expand Down
175 changes: 19 additions & 156 deletions response.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,12 @@ type Response struct {
StatusCode int16
RequestId int32

OperationAttributes Attributes
PrinterAttributes []Attributes
JobAttributes []Attributes
OperationAttributes Attributes
PrinterAttributes []Attributes
JobAttributes []Attributes
UnsupportedAttributes []Attributes

Data []byte
}

// CheckForErrors checks the status code and returns a error if it is not zero. it also returns the status message if provided by the server
Expand All @@ -43,13 +46,14 @@ func (r *Response) CheckForErrors() error {
// NewResponse creates a new ipp response
func NewResponse(statusCode int16, reqID int32) *Response {
return &Response{
ProtocolVersionMajor: ProtocolVersionMajor,
ProtocolVersionMinor: ProtocolVersionMinor,
StatusCode: statusCode,
RequestId: reqID,
OperationAttributes: make(Attributes),
PrinterAttributes: make([]Attributes, 0),
JobAttributes: make([]Attributes, 0),
ProtocolVersionMajor: ProtocolVersionMajor,
ProtocolVersionMinor: ProtocolVersionMinor,
StatusCode: statusCode,
RequestId: reqID,
OperationAttributes: make(Attributes),
PrinterAttributes: make([]Attributes, 0),
JobAttributes: make([]Attributes, 0),
UnsupportedAttributes: make([]Attributes, 0),
}
}

Expand Down Expand Up @@ -210,150 +214,9 @@ func encodeOperationAttribute(enc *AttributeEncoder, name string, attr []Attribu
return enc.Encode(name, values)
}

// ResponseDecoder reads and decodes a response from a stream
type ResponseDecoder struct {
reader io.Reader
}

// NewResponseDecoder returns a new decoder that reads from r
func NewResponseDecoder(r io.Reader) *ResponseDecoder {
return &ResponseDecoder{
reader: r,
}
}

// Decode decodes a ipp response into a response struct. additional data will be written to an io.Writer if data is not nil
func (d *ResponseDecoder) Decode(data io.Writer) (*Response, error) {
/*
1 byte: Protocol Major Version - b
1 byte: Protocol Minor Version - b
2 byte: Status ID - h
4 byte: Request ID - i
1 byte: Operation Attribute Byte (\0x01)
N times: Attributes
1 byte: Attribute End Byte (\0x03)
*/

resp := new(Response)

// wrap the reader so we have more functionality
// reader := bufio.NewReader(d.reader)

if err := binary.Read(d.reader, binary.BigEndian, &resp.ProtocolVersionMajor); err != nil {
return nil, err
}

if err := binary.Read(d.reader, binary.BigEndian, &resp.ProtocolVersionMinor); err != nil {
return nil, err
}

if err := binary.Read(d.reader, binary.BigEndian, &resp.StatusCode); err != nil {
return nil, err
}

if err := binary.Read(d.reader, binary.BigEndian, &resp.RequestId); err != nil {
return nil, err
}

startByteSlice := make([]byte, 1)

tag := TagCupsInvalid
previousAttributeName := ""
tempAttributes := make(Attributes)
tagSet := false

attribDecoder := NewAttributeDecoder(d.reader)

// decode attribute buffer
for {
if _, err := d.reader.Read(startByteSlice); err != nil {
// when we read from a stream, we may get an EOF if we want to read the end tag
// all data should be read and we can ignore the error
if err == io.EOF {
break
}
return nil, err
}

startByte := int8(startByteSlice[0])

// check if attributes are completed
if startByte == TagEnd {
break
}

if startByte == TagOperation {
if len(tempAttributes) > 0 && tag != TagCupsInvalid {
appendAttributeToResponse(resp, tag, tempAttributes)
tempAttributes = make(Attributes)
}

tag = TagOperation
tagSet = true
}

if startByte == TagJob {
if len(tempAttributes) > 0 && tag != TagCupsInvalid {
appendAttributeToResponse(resp, tag, tempAttributes)
tempAttributes = make(Attributes)
}

tag = TagJob
tagSet = true
}

if startByte == TagPrinter {
if len(tempAttributes) > 0 && tag != TagCupsInvalid {
appendAttributeToResponse(resp, tag, tempAttributes)
tempAttributes = make(Attributes)
}

tag = TagPrinter
tagSet = true
}

if tagSet {
if _, err := d.reader.Read(startByteSlice); err != nil {
return nil, err
}
startByte = int8(startByteSlice[0])
}

attrib, err := attribDecoder.Decode(startByte)
if err != nil {
return nil, err
}

if attrib.Name != "" {
tempAttributes[attrib.Name] = append(tempAttributes[attrib.Name], *attrib)
previousAttributeName = attrib.Name
} else {
tempAttributes[previousAttributeName] = append(tempAttributes[previousAttributeName], *attrib)
}

tagSet = false
}

if len(tempAttributes) > 0 && tag != TagCupsInvalid {
appendAttributeToResponse(resp, tag, tempAttributes)
}

if data != nil {
if _, err := io.Copy(data, d.reader); err != nil {
return nil, err
}
}

return resp, nil
}

func appendAttributeToResponse(resp *Response, tag int8, attr map[string][]Attribute) {
switch tag {
case TagOperation:
resp.OperationAttributes = attr
case TagPrinter:
resp.PrinterAttributes = append(resp.PrinterAttributes, attr)
case TagJob:
resp.JobAttributes = append(resp.JobAttributes, attr)
}
func (r *Response) Decode(reader io.Reader) error {
sm := NewResponseStateMachine()
sm.Response = r
_, err := sm.Decode(reader)
return err
}
Loading