优化海康发送的Message消息设置的消息体长度为0,导致无法解析的问题  #920
3个文件已修改
2个文件已添加
1 文件已重命名
496 ■■■■■ 已修改文件
src/main/java/com/genersoft/iot/vmp/gb28181/SipLayer.java 3 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/genersoft/iot/vmp/gb28181/bean/GBStringMsgParser.java 457 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/genersoft/iot/vmp/gb28181/bean/GbSipDate.java 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/genersoft/iot/vmp/gb28181/bean/GbStringMsgParserFactory.java 21 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/RegisterRequestProcessor.java 10 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/device/DeviceQuery.java 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/genersoft/iot/vmp/gb28181/SipLayer.java
@@ -2,6 +2,7 @@
import com.genersoft.iot.vmp.conf.SipConfig;
import com.genersoft.iot.vmp.conf.UserSetting;
import com.genersoft.iot.vmp.gb28181.bean.GbStringMsgParserFactory;
import com.genersoft.iot.vmp.gb28181.conf.DefaultProperties;
import com.genersoft.iot.vmp.gb28181.transmit.ISIPProcessorObserver;
import gov.nist.javax.sip.SipProviderImpl;
@@ -63,6 +64,7 @@
        SipStackImpl sipStack;
        try {
            sipStack = (SipStackImpl)SipFactory.getInstance().createSipStack(DefaultProperties.getProperties(monitorIp, userSetting.getSipLog()));
            sipStack.setMessageParserFactory(new GbStringMsgParserFactory());
        } catch (PeerUnavailableException e) {
            logger.error("[SIP SERVER] SIP服务启动失败, 监听地址{}失败,请检查ip是否正确", monitorIp);
            return;
@@ -75,7 +77,6 @@
            tcpSipProvider.setDialogErrorsAutomaticallyHandled();
            tcpSipProvider.addSipListener(sipProcessorObserver);
            tcpSipProviderMap.put(monitorIp, tcpSipProvider);
            logger.info("[SIP SERVER] tcp://{}:{} 启动成功", monitorIp, port);
        } catch (TransportNotSupportedException
                 | TooManyListenersException
src/main/java/com/genersoft/iot/vmp/gb28181/bean/GBStringMsgParser.java
New file
@@ -0,0 +1,457 @@
package com.genersoft.iot.vmp.gb28181.bean;
import gov.nist.core.CommonLogger;
import gov.nist.core.Host;
import gov.nist.core.HostNameParser;
import gov.nist.core.StackLogger;
import gov.nist.javax.sip.SIPConstants;
import gov.nist.javax.sip.address.AddressImpl;
import gov.nist.javax.sip.address.GenericURI;
import gov.nist.javax.sip.address.SipUri;
import gov.nist.javax.sip.address.TelephoneNumber;
import gov.nist.javax.sip.header.*;
import gov.nist.javax.sip.message.SIPMessage;
import gov.nist.javax.sip.message.SIPRequest;
import gov.nist.javax.sip.message.SIPResponse;
import gov.nist.javax.sip.parser.*;
import java.io.UnsupportedEncodingException;
import java.text.ParseException;
public class GBStringMsgParser implements MessageParser {
    protected static boolean computeContentLengthFromMessage = false;
    private static StackLogger logger = CommonLogger.getLogger(StringMsgParser.class);
    /**
     * @since v0.9
     */
    public GBStringMsgParser() {
        super();
    }
    /**
     * Parse a buffer containing a single SIP Message where the body is an array
     * of un-interpreted bytes. This is intended for parsing the message from a
     * memory buffer when the buffer. Incorporates a bug fix for a bug that was
     * noted by Will Sullin of Callcast
     *
     * @param msgBuffer
     *            a byte buffer containing the messages to be parsed. This can
     *            consist of multiple SIP Messages concatenated together.
     * @return a SIPMessage[] structure (request or response) containing the
     *         parsed SIP message.
     * @exception ParseException
     *                is thrown when an illegal message has been encountered
     *                (and the rest of the buffer is discarded).
     * @see ParseExceptionListener
     */
    public SIPMessage parseSIPMessage(byte[] msgBuffer, boolean readBody, boolean strict, ParseExceptionListener parseExceptionListener) throws ParseException {
        if (msgBuffer == null || msgBuffer.length == 0)
            return null;
        int i = 0;
        // Squeeze out any leading control character.
        try {
            while (msgBuffer[i] < 0x20)
                i++;
        }
        catch (ArrayIndexOutOfBoundsException e) {
            // Array contains only control char, return null.
            if (logger.isLoggingEnabled(StackLogger.TRACE_DEBUG)) {
                logger.logDebug("handled only control char so returning null");
            }
            return null;
        }
        // Iterate thru the request/status line and headers.
        String currentLine = null;
        String currentHeader = null;
        boolean isFirstLine = true;
        SIPMessage message = null;
        do
        {
            int lineStart = i;
            // Find the length of the line.
            try {
                while (msgBuffer[i] != '\r' && msgBuffer[i] != '\n')
                    i++;
            }
            catch (ArrayIndexOutOfBoundsException e) {
                // End of the message.
                break;
            }
            int lineLength = i - lineStart;
            // Make it a String.
            try {
                currentLine = new String(msgBuffer, lineStart, lineLength, "UTF-8");
            } catch (UnsupportedEncodingException e) {
                throw new ParseException("Bad message encoding!", 0);
            }
            currentLine = trimEndOfLine(currentLine);
            if (currentLine.length() == 0) {
                // Last header line, process the previous buffered header.
                if (currentHeader != null && message != null) {
                    processHeader(currentHeader, message, parseExceptionListener, msgBuffer);
                }
            }
            else {
                if (isFirstLine) {
                    message = processFirstLine(currentLine, parseExceptionListener, msgBuffer);
                } else {
                    char firstChar = currentLine.charAt(0);
                    if (firstChar == '\t' || firstChar == ' ') {
                        if (currentHeader == null)
                            throw new ParseException("Bad header continuation.", 0);
                        // This is a continuation, append it to the previous line.
                        currentHeader += currentLine.substring(1);
                    }
                    else {
                        if (currentHeader != null && message != null) {
                            processHeader(currentHeader, message, parseExceptionListener, msgBuffer);
                        }
                        currentHeader = currentLine;
                    }
                }
            }
            if (msgBuffer[i] == '\r' && msgBuffer.length > i+1 && msgBuffer[i+1] == '\n')
                i++;
            i++;
            isFirstLine = false;
        } while (currentLine.length() > 0); // End do - while
        if (message == null) throw new ParseException("Bad message", 0);
        message.setSize(i);
        // Check for content legth header
        if (readBody && message.getContentLength() != null ) {
            if ( message.getContentLength().getContentLength() != 0) {
                int bodyLength = msgBuffer.length - i;
                byte[] body = new byte[bodyLength];
                System.arraycopy(msgBuffer, i, body, 0, bodyLength);
                message.setMessageContent(body,!strict,computeContentLengthFromMessage,message.getContentLength().getContentLength());
            } else if (message.getCSeqHeader().getMethod().equalsIgnoreCase("MESSAGE")) {
                int bodyLength = msgBuffer.length - i;
                byte[] body = new byte[bodyLength];
                System.arraycopy(msgBuffer, i, body, 0, bodyLength);
                message.setMessageContent(body,!strict,computeContentLengthFromMessage,bodyLength);
            }else if (!computeContentLengthFromMessage && strict) {
                String last4Chars = new String(msgBuffer, msgBuffer.length - 4, 4);
                if(!"\r\n\r\n".equals(last4Chars)) {
                    throw new ParseException("Extraneous characters at the end of the message ",i);
                }
            }
        }
        return message;
    }
    protected static String trimEndOfLine(String line) {
        if (line == null)
            return line;
        int i = line.length() - 1;
        while (i >= 0 && line.charAt(i) <= 0x20)
            i--;
        if (i == line.length() - 1)
            return line;
        if (i == -1)
            return "";
        return line.substring(0, i+1);
    }
    protected SIPMessage processFirstLine(String firstLine, ParseExceptionListener parseExceptionListener, byte[] msgBuffer) throws ParseException {
        SIPMessage message;
        if (!firstLine.startsWith(SIPConstants.SIP_VERSION_STRING)) {
            message = new SIPRequest();
            try {
                RequestLine requestLine = new RequestLineParser(firstLine + "\n")
                        .parse();
                ((SIPRequest) message).setRequestLine(requestLine);
            } catch (ParseException ex) {
                if (parseExceptionListener != null)
                    try {
                        parseExceptionListener.handleException(ex, message,
                                RequestLine.class, firstLine, new String(msgBuffer, "UTF-8"));
                    } catch (UnsupportedEncodingException e) {
                        e.printStackTrace();
                    }
                else
                    throw ex;
            }
        } else {
            message = new SIPResponse();
            try {
                StatusLine sl = new StatusLineParser(firstLine + "\n").parse();
                ((SIPResponse) message).setStatusLine(sl);
            } catch (ParseException ex) {
                if (parseExceptionListener != null) {
                    try {
                        parseExceptionListener.handleException(ex, message,
                                StatusLine.class, firstLine, new String(msgBuffer, "UTF-8"));
                    } catch (UnsupportedEncodingException e) {
                        e.printStackTrace();
                    }
                } else
                    throw ex;
            }
        }
        return message;
    }
    protected void processHeader(String header, SIPMessage message, ParseExceptionListener parseExceptionListener, byte[] rawMessage) throws ParseException {
        if (header == null || header.length() == 0)
            return;
        HeaderParser headerParser = null;
        try {
            headerParser = ParserFactory.createParser(header + "\n");
        } catch (ParseException ex) {
            // https://java.net/jira/browse/JSIP-456
            if (parseExceptionListener != null) {
                parseExceptionListener.handleException(ex, message, null,
                        header, null);
                return;
            } else {
                throw ex;
            }
        }
        try {
            SIPHeader sipHeader = headerParser.parse();
            message.attachHeader(sipHeader, false);
        } catch (ParseException ex) {
            if (parseExceptionListener != null) {
                String headerName = Lexer.getHeaderName(header);
                Class headerClass = NameMap.getClassFromName(headerName);
                if (headerClass == null) {
                    headerClass = ExtensionHeaderImpl.class;
                }
                try {
                    parseExceptionListener.handleException(ex, message,
                            headerClass, header, new String(rawMessage, "UTF-8"));
                } catch (UnsupportedEncodingException e) {
                    e.printStackTrace();
                }
            }
        }
    }
    /**
     * Parse an address (nameaddr or address spec) and return and address
     * structure.
     *
     * @param address
     *            is a String containing the address to be parsed.
     * @return a parsed address structure.
     * @since v1.0
     * @exception ParseException
     *                when the address is badly formatted.
     */
    public AddressImpl parseAddress(String address) throws ParseException {
        AddressParser addressParser = new AddressParser(address);
        return addressParser.address(true);
    }
    /**
     * Parse a host:port and return a parsed structure.
     *
     * @param hostport
     *            is a String containing the host:port to be parsed
     * @return a parsed address structure.
     * @since v1.0
     * @exception throws
     *                a ParseException when the address is badly formatted.
     *
    public HostPort parseHostPort(String hostport) throws ParseException {
    Lexer lexer = new Lexer("charLexer", hostport);
    return new HostNameParser(lexer).hostPort();
    }
     */
    /**
     * Parse a host name and return a parsed structure.
     *
     * @param host
     *            is a String containing the host name to be parsed
     * @return a parsed address structure.
     * @since v1.0
     * @exception ParseException
     *                a ParseException when the hostname is badly formatted.
     */
    public Host parseHost(String host) throws ParseException {
        Lexer lexer = new Lexer("charLexer", host);
        return new HostNameParser(lexer).host();
    }
    /**
     * Parse a telephone number return a parsed structure.
     *
     * @param telephone_number
     *            is a String containing the telephone # to be parsed
     * @return a parsed address structure.
     * @since v1.0
     * @exception ParseException
     *                a ParseException when the address is badly formatted.
     */
    public TelephoneNumber parseTelephoneNumber(String telephone_number)
            throws ParseException {
        // Bug fix contributed by Will Scullin
        return new URLParser(telephone_number).parseTelephoneNumber(true);
    }
    /**
     * Parse a SIP url from a string and return a URI structure for it.
     *
     * @param url
     *            a String containing the URI structure to be parsed.
     * @return A parsed URI structure
     * @exception ParseException
     *                if there was an error parsing the message.
     */
    public SipUri parseSIPUrl(String url) throws ParseException {
        try {
            return new URLParser(url).sipURL(true);
        } catch (ClassCastException ex) {
            throw new ParseException(url + " Not a SIP URL ", 0);
        }
    }
    /**
     * Parse a uri from a string and return a URI structure for it.
     *
     * @param url
     *            a String containing the URI structure to be parsed.
     * @return A parsed URI structure
     * @exception ParseException
     *                if there was an error parsing the message.
     */
    public GenericURI parseUrl(String url) throws ParseException {
        return new URLParser(url).parse();
    }
    /**
     * Parse an individual SIP message header from a string.
     *
     * @param header
     *            String containing the SIP header.
     * @return a SIPHeader structure.
     * @exception ParseException
     *                if there was an error parsing the message.
     */
    public static SIPHeader parseSIPHeader(String header) throws ParseException {
        int start = 0;
        int end = header.length() - 1;
        try {
            // Squeeze out any leading control character.
            while (header.charAt(start) <= 0x20)
                start++;
            // Squeeze out any trailing control character.
            while (header.charAt(end) <= 0x20)
                end--;
        }
        catch (ArrayIndexOutOfBoundsException e) {
            // Array contains only control char.
            throw new ParseException("Empty header.", 0);
        }
        StringBuilder buffer = new StringBuilder(end + 1);
        int i = start;
        int lineStart = start;
        boolean endOfLine = false;
        while (i <= end) {
            char c = header.charAt(i);
            if (c == '\r' || c == '\n') {
                if (!endOfLine) {
                    buffer.append(header.substring(lineStart, i));
                    endOfLine = true;
                }
            }
            else {
                if (endOfLine) {
                    endOfLine = false;
                    if (c == ' ' || c == '\t') {
                        buffer.append(' ');
                        lineStart = i + 1;
                    }
                    else {
                        lineStart = i;
                    }
                }
            }
            i++;
        }
        buffer.append(header.substring(lineStart, i));
        buffer.append('\n');
        HeaderParser hp = ParserFactory.createParser(buffer.toString());
        if (hp == null)
            throw new ParseException("could not create parser", 0);
        return hp.parse();
    }
    /**
     * Parse the SIP Request Line
     *
     * @param requestLine
     *            a String containing the request line to be parsed.
     * @return a RequestLine structure that has the parsed RequestLine
     * @exception ParseException
     *                if there was an error parsing the requestLine.
     */
    public RequestLine parseSIPRequestLine(String requestLine)
            throws ParseException {
        requestLine += "\n";
        return new RequestLineParser(requestLine).parse();
    }
    /**
     * Parse the SIP Response message status line
     *
     * @param statusLine
     *            a String containing the Status line to be parsed.
     * @return StatusLine class corresponding to message
     * @exception ParseException
     *                if there was an error parsing
     * @see StatusLine
     */
    public StatusLine parseSIPStatusLine(String statusLine)
            throws ParseException {
        statusLine += "\n";
        return new StatusLineParser(statusLine).parse();
    }
    public static void setComputeContentLengthFromMessage(
            boolean computeContentLengthFromMessage) {
        GBStringMsgParser.computeContentLengthFromMessage = computeContentLengthFromMessage;
    }
}
src/main/java/com/genersoft/iot/vmp/gb28181/bean/GbSipDate.java
File was renamed from src/main/java/com/genersoft/iot/vmp/gb28181/bean/WvpSipDate.java
@@ -8,7 +8,7 @@
/**
 * 重写jain sip的SIPDate解决与国标时间格式不一致的问题
 */
public class WvpSipDate extends SIPDate {
public class GbSipDate extends SIPDate {
    /**
     *
@@ -17,7 +17,7 @@
    
    private Calendar javaCal;
    public WvpSipDate(long timeMillis) {
    public GbSipDate(long timeMillis) {
        this.javaCal = new GregorianCalendar(TimeZone.getDefault(), Locale.getDefault());
        Date date = new Date(timeMillis);
        this.javaCal.setTime(date);
src/main/java/com/genersoft/iot/vmp/gb28181/bean/GbStringMsgParserFactory.java
New file
@@ -0,0 +1,21 @@
package com.genersoft.iot.vmp.gb28181.bean;
import gov.nist.javax.sip.parser.MessageParser;
import gov.nist.javax.sip.parser.MessageParserFactory;
import gov.nist.javax.sip.stack.SIPTransactionStack;
public class GbStringMsgParserFactory implements MessageParserFactory {
    /**
     * msg parser is completely stateless, reuse isntance for the whole stack
     * fixes https://github.com/RestComm/jain-sip/issues/92
     */
    private static GBStringMsgParser msgParser = new GBStringMsgParser();
    /*
     * (non-Javadoc)
     * @see gov.nist.javax.sip.parser.MessageParserFactory#createMessageParser(gov.nist.javax.sip.stack.SIPTransactionStack)
     */
    public MessageParser createMessageParser(SIPTransactionStack stack) {
        return msgParser;
    }
}
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/RegisterRequestProcessor.java
@@ -6,7 +6,7 @@
import com.genersoft.iot.vmp.gb28181.bean.Device;
import com.genersoft.iot.vmp.gb28181.bean.RemoteAddressInfo;
import com.genersoft.iot.vmp.gb28181.bean.SipTransactionInfo;
import com.genersoft.iot.vmp.gb28181.bean.WvpSipDate;
import com.genersoft.iot.vmp.gb28181.bean.GbSipDate;
import com.genersoft.iot.vmp.gb28181.transmit.SIPProcessorObserver;
import com.genersoft.iot.vmp.gb28181.transmit.SIPSender;
import com.genersoft.iot.vmp.gb28181.transmit.event.request.ISIPRequestProcessor;
@@ -145,8 +145,8 @@
            // 添加date头
            SIPDateHeader dateHeader = new SIPDateHeader();
            // 使用自己修改的
            WvpSipDate wvpSipDate = new WvpSipDate(Calendar.getInstance(Locale.ENGLISH).getTimeInMillis());
            dateHeader.setDate(wvpSipDate);
            GbSipDate gbSipDate = new GbSipDate(Calendar.getInstance(Locale.ENGLISH).getTimeInMillis());
            dateHeader.setDate(gbSipDate);
            response.addHeader(dateHeader);
            if (request.getExpires() == null) {
@@ -218,8 +218,8 @@
        // 添加date头
        SIPDateHeader dateHeader = new SIPDateHeader();
        // 使用自己修改的
        WvpSipDate wvpSipDate = new WvpSipDate(Calendar.getInstance(Locale.ENGLISH).getTimeInMillis());
        dateHeader.setDate(wvpSipDate);
        GbSipDate gbSipDate = new GbSipDate(Calendar.getInstance(Locale.ENGLISH).getTimeInMillis());
        dateHeader.setDate(gbSipDate);
        response.addHeader(dateHeader);
        // 添加Contact头
src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/device/DeviceQuery.java
@@ -470,7 +470,6 @@
    public void getSnap(HttpServletResponse resp, @PathVariable String deviceId, @PathVariable String channelId, @RequestParam(required = false) String mark) {
        try {
            final InputStream in = Files.newInputStream(new File("snap" + File.separator + deviceId + "_" + channelId + (mark == null? ".jpg": ("_" + mark + ".jpg"))).toPath());
            resp.setContentType(MediaType.IMAGE_PNG_VALUE);
            IOUtils.copy(in, resp.getOutputStream());