lux/rpc/lux_rpc_defrag.go

136 lines
3.3 KiB
Go

package rpc
import "lux/proto"
// FSM to handle TCP-window split XML packets
const LUX_XML_REQUEST_BEGIN = "<request"
const LUX_XML_REQUEST_END = "</request>"
const LUX_XML_RESPONSE_BEGIN = "<response"
const LUX_XML_RESPONSE_END = "</response>"
const LUX_XML_ERROR_BEGIN = "<error"
const LUX_XML_ERROR_END = "</error>"
type LuxRpcDefragState int
const (
LuxRpcDefragStateOff = 0
LuxRpcDefragStateReading = 1
LuxRpcDefragStateReadingRequest = 1
LuxRpcDefragStateReadingResponse = 2
LuxRpcDefragStateReadingError = 3
LuxRpcDefragStateHasData = 4
LuxRpcDefragStateHasRequest = 4
LuxRpcDefragStateHasResponse = 5
LuxRpcDefragStateHasError = 6
)
type LuxRpcDefrag struct {
buffer proto.LuxBuffer
state LuxRpcDefragState
beginOff int
endOff int
}
func NewLuxRpcDefrag() LuxRpcDefrag {
return LuxRpcDefrag{
buffer: proto.NewLuxBuffer(),
state: LuxRpcDefragStateOff,
beginOff: 0,
endOff: 0,
}
}
func (def *LuxRpcDefrag) HasRequest() bool {
return def.state == LuxRpcDefragStateHasRequest
}
func (def *LuxRpcDefrag) HasResponse() bool {
return def.state == LuxRpcDefragStateHasResponse
}
func (def *LuxRpcDefrag) HasError() bool {
return def.state == LuxRpcDefragStateHasError
}
func matchTag(slice []byte, tag string) bool {
tagLen := len(tag)
if len(slice) >= tagLen {
return string(slice[:tagLen]) == tag
}
return false
}
// will return true if data is complete
func (def *LuxRpcDefrag) Feed(data []byte) bool {
// try find tag opening at beginning
if def.state == LuxRpcDefragStateOff {
for i := 0; i < len(data); i++ {
slice := data[i:]
if matchTag(slice, LUX_XML_REQUEST_BEGIN) {
def.state = LuxRpcDefragStateReadingRequest
def.beginOff = i + len(LUX_XML_REQUEST_BEGIN)
break
} else if matchTag(slice, LUX_XML_RESPONSE_BEGIN) {
def.state = LuxRpcDefragStateReadingResponse
def.beginOff = i + len(LUX_XML_RESPONSE_BEGIN)
break
} else if matchTag(slice, LUX_XML_ERROR_BEGIN) {
def.state = LuxRpcDefragStateReadingError
def.beginOff = i + len(LUX_XML_ERROR_BEGIN)
break
}
}
}
// now check if slice has ending tag, if so - complete data
if def.state >= LuxRpcDefragStateReading {
for i := def.beginOff; i < len(data); i++ {
slice := data[i:]
if matchTag(slice, LUX_XML_REQUEST_END) {
def.state = LuxRpcDefragStateHasRequest
def.endOff = i + len(LUX_XML_REQUEST_END)
break
} else if matchTag(slice, LUX_XML_RESPONSE_END) {
def.state = LuxRpcDefragStateHasResponse
def.endOff = i + len(LUX_XML_RESPONSE_END)
break
} else if matchTag(slice, LUX_XML_ERROR_END) {
def.state = LuxRpcDefragStateHasError
def.endOff = i + len(LUX_XML_ERROR_END)
break
}
}
// got end tag? push exact 0:endOff slice
if def.state >= LuxRpcDefragStateHasData {
def.buffer.WriteBytes(data[:def.endOff])
return true // we got complete data!
} else {
// if there is no end tag, then reset beginOff so it wont
// confuse next Feed call
def.beginOff = 0
}
}
// push whatever data is
def.buffer.WriteBytes(data)
return false
}
func (def *LuxRpcDefrag) GetAndForget() []byte {
data := def.buffer.AllBytes()
// reset state
def.buffer = proto.NewLuxBuffer()
def.state = LuxRpcDefragStateOff
def.beginOff = 0
def.endOff = 0
return data
}