func (mng *Manager) makeKey(s base.SipMessage) (key, bool) { viaHeaders := s.Headers("Via") via, ok := viaHeaders[0].(*base.ViaHeader) if !ok { panic(errors.New("Headers('Via') returned non-Via header!")) } b, ok := (*via)[0].Params.Get("branch") if !ok { return key{}, false } branch, ok := b.(base.String) if !ok { return key{}, false } var method string switch s := s.(type) { case *base.Request: // Correlate an ACK request to the related INVITE. if s.Method == base.ACK { method = string(base.INVITE) } else { method = string(s.Method) } case *base.Response: cseqs := s.Headers("CSeq") if len(cseqs) == 0 { // TODO - Handle non-existent CSeq panic("No CSeq on response!") } cseq, _ := s.Headers("CSeq")[0].(*base.CSeq) method = string(cseq.MethodName) } return key{branch.String(), method}, true }
// Consume input lines one at a time, producing base.SipMessage objects and sending them down p.output. func (p *parser) parse(requireContentLength bool) { var message base.SipMessage for { // Parse the StartLine. startLine, err := p.input.NextLine() if err != nil { log.Debug("Parser %p stopped", p) break } if isRequest(startLine) { method, recipient, sipVersion, err := parseRequestLine(startLine) message = base.NewRequest(method, recipient, sipVersion, []base.SipHeader{}, "") p.terminalErr = err } else if isResponse(startLine) { sipVersion, statusCode, reason, err := parseStatusLine(startLine) message = base.NewResponse(sipVersion, statusCode, reason, []base.SipHeader{}, "") p.terminalErr = err } else { p.terminalErr = fmt.Errorf("transmission beginning '%s' is not a SIP message", startLine) } if p.terminalErr != nil { p.terminalErr = fmt.Errorf("failed to parse first line of message: %s", p.terminalErr.Error()) p.errs <- p.terminalErr break } // Parse the header section. // Headers can be split across lines (marked by whitespace at the start of subsequent lines), // so store lines into a buffer, and then flush and parse it when we hit the end of the header. var buffer bytes.Buffer headers := make([]base.SipHeader, 0) flushBuffer := func() { if buffer.Len() > 0 { newHeaders, err := p.parseHeader(buffer.String()) if err == nil { headers = append(headers, newHeaders...) } else { log.Debug("Skipping header '%s' due to error: %s", buffer.String(), err.Error()) } buffer.Reset() } } for { line, err := p.input.NextLine() if err != nil { log.Debug("Parser %p stopped", p) break } if len(line) == 0 { // We've hit the end of the header section. // Parse anything remaining in the buffer, then break out. flushBuffer() break } if !strings.Contains(c_ABNF_WS, string(line[0])) { // This line starts a new header. // Parse anything currently in the buffer, then store the new header line in the buffer. flushBuffer() buffer.WriteString(line) } else if buffer.Len() > 0 { // This is a continuation line, so just add it to the buffer. buffer.WriteString(" ") buffer.WriteString(line) } else { // This is a continuation line, but also the first line of the whole header section. // Discard it and log. log.Debug("Discarded unexpected continuation line '%s' at start of header block in message '%s'", line, message.Short()) } } // Store the headers in the message object. for _, header := range headers { message.AddHeader(header) } var contentLength int // Determine the length of the body, so we know when to stop parsing this message. if p.streamed { // Use the content-length header to identify the end of the message. contentLengthHeaders := message.Headers("Content-Length") if len(contentLengthHeaders) == 0 { p.terminalErr = fmt.Errorf("Missing required content-length header on message %s", message.Short()) p.errs <- p.terminalErr break } else if len(contentLengthHeaders) > 1 { var errbuf bytes.Buffer errbuf.WriteString("Multiple content-length headers on message ") errbuf.WriteString(message.Short()) errbuf.WriteString(":\n") for _, header := range contentLengthHeaders { errbuf.WriteString("\t") errbuf.WriteString(header.String()) } p.terminalErr = fmt.Errorf(errbuf.String()) p.errs <- p.terminalErr break } contentLength = int(*(contentLengthHeaders[0].(*base.ContentLength))) } else { // We're not in streaming mode, so the Write method should have calculated the length of the body for us. contentLength = (<-p.bodyLengths.Out).(int) } // Extract the message body. body, err := p.input.NextChunk(contentLength) if err != nil { log.Debug("Parsed %p stopped", p) break } switch message.(type) { case *base.Request: message.(*base.Request).Body = body case *base.Response: message.(*base.Response).Body = body default: log.Severe("Internal error - message %s is neither a request type nor a response type", message.Short()) } p.output <- message } if !p.streamed { // We're in unstreamed mode, so we created a bodyLengths ElasticChan which // needs to be disposed. close(p.bodyLengths.In) } return }