📄 readme
字号:
September 23, 2002head/vocal/sip2/sipstack/READMESipMessage design goals:- efficient parsing- reasonable interface for the application writer- reasonably easy path to adding new headers, methods, parameter namesSipMessage design.There are two APIs to SipMessage; one from the transport layer 'up' and one fromthe application layer 'down'. The transport layers pulls the raw message textoff the wire, parses the text just enough to segment it onto headers and setsthe text of the headers into the SipMessage generically. This minimal headerparse phase is called "pre-parsing".The pre-parse phase needs to identify the header for generic storage into theSipMessage. In addition, the pre-parse phase needs to know if unescaped commasare delimiters in the header text to be parsed. However, the pre-parse phasedoes not require static typing of the header or whether the header can appearmultiple times in the message. The pre-parse phase allows unknown headers.The application requests a specific header by static type. If the header doesnot exists, an empty header of the appropriate type is created. If the headerhas not already been parsed, a parser is created and associated with theheader. The parser is not invoked until the application requests a sub-part ofthe header. This permits headers to be moved from one message to another withoutparsing within the header.The parser is statically typed to provide a specific interface to theapplication. The specifics of how to parse a message of a given type isimplemented by the parser type. The static header types are available to theapplication through global variables named after the headers; e.g. CallId, CSeq,From, Via.The interface from each parser is a set of accessors. Each accessor may return areference, allowing the application to set the corresponding value directly, orit may return a const reference, indicating a read only value. Most parsers alsopresent an interface to a generic parameter accessor. This generic parameteraccessors allows the application to get or set proprietary parameters.The application retrieves unknown headers by the string name of the unknownheader.Interface Examples:SipMessage *msg;msg->get(From).getAddressOfRecord();Data& aor = msg->get(From)->host();msg->get(From).host() = "vovida.org";msg->exists(Warning);msg->exists("Proprietary-Header"); // true if presentmsg->get("Proprietary-Header"); // returns ParserContainer<String>&for (Vias::iterator i = msg->get(Vias).begin(); i != msg->get(Vias).end(); i++){ Data& host = i->host(); int& port = i->port();}Implementation:There is a fixed set of headers. Each header is assigned an enum value. There isa type associated with each enum value and a distinctly named global variable ofheader type. The enum value is used to store and retrieve the headers atruntime. The header type is used to retrieve the parsed and statically typedheaders at compile time.A SipMessage contains an array of pointers to HeaderFieldValueList. This arrayis indexed by the header enums.The pre-parse phase consists of:0. determine the boundaries of the header string1. determine the boundaries of the header name string2. look up the enum value for the header name3. look up the comma tokenizing property of the header enum4. determine the boundaries of each header in the header string5. for each header in the header string, add the header string boundaries by enum to the message -- the message stores the header string boundaries in the as indexed by the header enumWhile processing headers the pre-parse will allocate chunks of memory to hold theheader strings. Each of these buffers is passed opaquely to the message fordeletion when the message is deleted.After pre-parsing the message header array contains one HeaderFieldValueList foreach header encountered in the pre-parse. Each HeaderFieldValueList contains a list of HeaderFieldValue. The application requests the contents of a header through a type safe interface;the returned object is either a particular parser type or a particular parsercontainer. A parser container is returned if and only if the header is specifiedto permit multiple values.If the header specifies multiple values, the returned value is iterable. Eachiteration provides access to a header value. In addition, there is an interfaceon the returned value to determine the number of header values.The returned value in the single header value case, and within the iterationover the multi header value case provides access to the components of the headervalue specfic to the type of header. For example, CSeq provides getMethod() andsequence. For sequence, the sequence value is an integer returned by referenceand may be directly modified.Adding a new header:Assume the new header name New-HeaderSymbols.hxx1. Add New_Header to the static symbol declarations.Symbols.chxx1. Add New_Header = "New-Header" to the static symbol definitions.HeaderTypes.hxx1. Add New_Header to the Headers::Type enum. Add this entry anywhere before UNKNOWN. Headers are output in the same order they appear in the Headers::Type enum.2. Determine if the header allows multiple values. Use the MultiHeader template if the new header allows multiple values, use the Header template if the new header does not allow multiple values.3. Determine the parser type for the header. This may be StringComponent for unstructured headers, may be another existing parser type (see ParserCategories.hxx), or may require a new parser type.4. Add the declaration of the header type and the header type global variable to HeaderTypes.hxxe.g.:class Header<Headers::New_Header>{ public: typedef StringComponent Type; Header() { Headers::CommaTokenizing[Headers::New_Header] = Type::isCommaTokenizing; HeaderStrings[Headers::New_Header] = Symbols::New_Header; }};extern Header<Headers::Content_Disposition> Content_Disposition;5. Add the mapping from the four character hash to the header enum. This is error prone. The hash value is platform dependent.HeaderTypesSetup.cxx1. Add a line to the hash id generator.HeaderTypes.cxx1. Add the definition of the new header type global variable. Be sure to use the same template type as in the declaration.e.g.Header<Headers::New_Header> Vocal2:: New_Header>;ParserCategories.hxx(if the new header requires a new parser)1. Declare the new parser category as publicly inheriting from ParserCategory.2. Determine if commas can be used to include multiple header values in a single line.3. Declare virtual ParserCategory* clone(HeaderFieldValue*) const;4. Declare virtual void parse();5. Declare private members as required to store the parsed header value.6. Declare public methods as required to access the parsed header value.7. Typedef the container for the parser. Not required if the header value is never used in a header that allows multiple values.e.g.typedef ParserContainer<NewComponent> NewComponents; ParserCategories.cxx(if the new header requires a new parser)1. Define clone(), parse()2. Define accessors. Each accessor must call checkParsed() before processing any data.(* It would be nice if getHeaderType(const char *headerName, int len) was defined my configure/make. The supported.hxx would be replaced with a configuration file headers.hdr that specified the SIP header name, the C++ parser category, and whether the header allows multiple values. This file could be compiled into the various declarations and definitions. It is even possible to allow the user to specify additional headers in the code; have the automatically generated types go into the enum HeaderTypesBase, and have HeaderTypes inherit; HeaderTypes::Types could continue where the inherited enum leaves off. *)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -