All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.pkts.packet.sip.header.SipHeader Maven / Gradle / Ivy

/**
 *
 */
package io.pkts.packet.sip.header;

import io.pkts.buffer.Buffer;
import io.pkts.buffer.Buffers;
import io.pkts.packet.sip.SipParseException;
import io.pkts.packet.sip.header.impl.SipHeaderImpl;
import io.pkts.packet.sip.impl.SipParser;

import java.io.IOException;

import static io.pkts.packet.sip.impl.PreConditions.assertNotEmpty;


/**
 * 

* Represents any header in SIP. *

* *

* All {@link SipHeader}s in this API are created through a set of factory methods as well as * through builders. Any header that is simple in nature, as in it only contains a single value, * such as the {@link MaxForwardsHeader}, can be created directly through a create * method on the the corresponding interface of that header. Headers that are constructed through * many arguments, or have ambiguous arguments, are created through builders. Finally, all headers * have a frame method that takes a {@link Buffer} and will attempt to frame the * content into a specific header. Note it frames the header and as such does not verify * every aspect of the header since speed is important. *

* *

* Example: create a simple header directly, such as the {@link MaxForwardsHeader}. * *

 * MaxForwardsHeader header = MaxForwardsHeader.create(20);
 * 
* *

* *

* Example: create a header using the builder-pattern, such as a {@link ToHeader}. * *

 * ToHeader header = ToHeader.with().user("alice").host("example.com").build();
 * 
* *

* *

* Note, by default most things are done lazily in order to speed things up. As such, you may * successfully construct a header but it may in fact miss important information. If you are * building an io.sipstack.application.application where you want to be 100% sure that a header is correct according to the * BNF in rfc 3261 then call {@link #verify()}. *

* * @author [email protected] */ public interface SipHeader extends Cloneable { String CANNOT_CAST_HEADER_OF_TYPE = "Cannot cast header of type "; String UNABLE_TO_PARSE_OUT_THE_HEADER_NAME_DUE_TO_UNDERLYING_IO_EXCEPTION = "Unable to parse out the header name due to underlying IOException"; /** * Get the name of the header * * @return */ Buffer getName(); /** * Get the name as a string. * * TODO: need better method names! * * @return */ default String getNameStr() { return getName().toString(); } /** * Convenience method for checking the name of this header. * * @param name * @return */ default boolean is(final String name) { if (name == null || name.isEmpty()) { return false; } return Buffers.wrap(name).equals(getName()); } default boolean is(final Buffer name) { if (name == null) { return false; } return name.equals(getName()); } /** * Get the value of the buffer * * @return */ Buffer getValue(); void verify() throws SipParseException; void getBytes(Buffer dst); default int getBufferSize() { // TODO: shouldn't be a default value. Each header should know return getName().capacity() + 2 + getValue().getReadableBytes(); } SipHeader clone(); /** * Create a new {@link SipHeader} based on the buffer. Each {@link SipHeader} will override this * factory method to parse the header into a more specialized header. * * Note, the header returned really is a {@link SipHeader} and is NOT e.g. a {@link ToHeader}. * If you really need to parse it as a {@link ToHeader} you should use the * * @param header the raw header * @return a new {@link SipHeader}. * @throws SipParseException in case the header is not a correct formatted header. */ static SipHeader frame(final Buffer value) throws SipParseException { assertNotEmpty(value, "The value of the header cannot be null or the empty buffer"); return SipParser.nextHeader(value); } static SipHeader frame(final String value) throws SipParseException { return SipParser.nextHeader(Buffers.wrap(value)); } static SipHeader create(final String name, final String value) { assertNotEmpty(name, "The name of the header cannot be empty"); return new SipHeaderImpl(Buffers.wrap(name), Buffers.wrap(value)); } /** * As most things in this library are done lazily, such as framing headers, you can make sure * that a particular header has indeed been parsed to the more specific header type by calling * this method. If the header has yet not been parsed fully, e.g., we may have extracted out a * {@link ContactHeader} but it is still in its "raw" form and therefore represented as a * {@link SipHeader} as opposed to an actual {@link ContactHeader} but by calling this method * you will force the library to actually fully frame it. * * Note, if the header is successfully parsed into a more explicit header type you may * still not really know what to cast it so in order to make life somewhat easier you can * use the isXXXHeader methods (such as {@link SipHeader#isAddressParametersHeader()} to * check what type it possible can be and then use the corresponding toXXXHeader to * "cast" it. * * @return */ SipHeader ensure(); /** * If you use the {@link SipHeader#ensure()} method then, if possible, the header will * be parsed to a more specific header type but you may not know exactly which type * but you can use the various isXXX methods to find out. * @return */ default boolean isAddressParametersHeader() { return false; } /** * * @return */ default boolean isSystemHeader() { return isToHeader() || isFromHeader() || isViaHeader() || isContactHeader() || isRouteHeader() || isRecordRouteHeader() || isCSeqHeader() || isMaxForwardsHeader(); } default boolean isFromHeader() { final Buffer m = getName(); try { if (m.getReadableBytes() == 4) { return (m.getByte(0) == 'F' || m.getByte(0) == 'f') && (m.getByte(1) == 'r' || m.getByte(1) == 'R') && (m.getByte(2) == 'o' || m.getByte(2) == 'O') && (m.getByte(3) == 'm' || m.getByte(3) == 'M'); } else if (m.getReadableBytes() == 1) { return m.getByte(0) == 'F' || m.getByte(0) == 'f'; } } catch (final IOException e) { throw new SipParseException(0, UNABLE_TO_PARSE_OUT_THE_HEADER_NAME_DUE_TO_UNDERLYING_IO_EXCEPTION, e); } return false; } default FromHeader toFromHeader() { throw new ClassCastException(CANNOT_CAST_HEADER_OF_TYPE + getClass().getName() + " to type " + FromHeader.class.getName()); } default boolean isToHeader() { final Buffer m = getName(); try { if (m.getReadableBytes() == 2) { return (m.getByte(0) == 'T' || m.getByte(0) == 't') && (m.getByte(1) == 'o' || m.getByte(1) == 'O'); } else if (m.getReadableBytes() == 1) { return (m.getByte(0) == 'T' || m.getByte(0) == 't'); } } catch (final IOException e) { throw new SipParseException(0, UNABLE_TO_PARSE_OUT_THE_HEADER_NAME_DUE_TO_UNDERLYING_IO_EXCEPTION, e); } return false; } default ToHeader toToHeader() { throw new ClassCastException(CANNOT_CAST_HEADER_OF_TYPE + getClass().getName() + " to type " + ToHeader.class.getName()); } /** * Check whether this is a contact header. * * Note: if this is true you MUST still do {@link SipHeader#ensure()}.{@link SipHeader#toContactHeader()} * because the way this method works is that if the header hasn't been fully framed * then the default method, which is on the {@link SipHeader} itself, will check the method name * if it "spells" "Contact". However, if the header you have really has been parsed into a * {@link ContactHeader} then this method will be overridden to return true right away * but since you cannot know that you must force it to be framed (if you actually want to * work with the {@link ContactHeader} interface i.e.), which is done by calling * {@link SipHeader#ensure()}. * * @return */ default boolean isContactHeader() { final Buffer m = getName(); try { if (m.getReadableBytes() == 7) { return (m.getByte(0) == 'C' || m.getByte(0) == 'c') && (m.getByte(1) == 'o' || m.getByte(1) == 'O') && (m.getByte(2) == 'n' || m.getByte(2) == 'N') && (m.getByte(3) == 't' || m.getByte(3) == 'T') && (m.getByte(4) == 'a' || m.getByte(4) == 'A') && (m.getByte(5) == 'c' || m.getByte(5) == 'C') && (m.getByte(6) == 't' || m.getByte(6) == 'T'); } else if (m.getReadableBytes() == 1) { // short form for the contact header is 'm' return (m.getByte(0) == 'M' || m.getByte(0) == 'm'); } } catch (final IOException e) { throw new SipParseException(0, UNABLE_TO_PARSE_OUT_THE_HEADER_NAME_DUE_TO_UNDERLYING_IO_EXCEPTION, e); } return false; } default ContactHeader toContactHeader() { throw new ClassCastException(CANNOT_CAST_HEADER_OF_TYPE + getClass().getName() + " to type " + ContactHeader.class.getName()); } default boolean isSubjectHeader() { final Buffer m = getName(); try { if (m.getReadableBytes() == 7) { return (m.getByte(0) == 'S' || m.getByte(0) == 's') && (m.getByte(1) == 'u' || m.getByte(1) == 'U') && (m.getByte(2) == 'b' || m.getByte(2) == 'B') && (m.getByte(3) == 'j' || m.getByte(3) == 'J') && (m.getByte(4) == 'e' || m.getByte(4) == 'E') && (m.getByte(5) == 'c' || m.getByte(5) == 'C') && (m.getByte(6) == 't' || m.getByte(6) == 'T'); } } catch (final IOException e) { throw new SipParseException(0, UNABLE_TO_PARSE_OUT_THE_HEADER_NAME_DUE_TO_UNDERLYING_IO_EXCEPTION, e); } return false; } default boolean isCallIdHeader() { final Buffer m = getName(); try { if (m.getReadableBytes() == 7) { return (m.getByte(0) == 'C' || m.getByte(0) == 'c') && (m.getByte(1) == 'a' || m.getByte(1) == 'A') && (m.getByte(2) == 'l' || m.getByte(2) == 'L') && (m.getByte(3) == 'l' || m.getByte(3) == 'L') && m.getByte(4) == '-' && (m.getByte(5) == 'I' || m.getByte(5) == 'i') && (m.getByte(6) == 'D' || m.getByte(6) == 'd'); } else if (m.getReadableBytes() == 1) { // short form for the call-id header is 'i' return (m.getByte(0) == 'I' || m.getByte(0) == 'i'); } } catch (final IOException e) { throw new SipParseException(0, UNABLE_TO_PARSE_OUT_THE_HEADER_NAME_DUE_TO_UNDERLYING_IO_EXCEPTION, e); } return false; } default CallIdHeader toCallIdHeader() { throw new ClassCastException(CANNOT_CAST_HEADER_OF_TYPE + getClass().getName() + " to type " + CallIdHeader.class.getName()); } default boolean isRouteHeader() { // Note: route header doesn't have a short form. final Buffer m = getName(); if (m.getReadableBytes() == 5) { try { return (m.getByte(0) == 'R' || m.getByte(0) == 'r') && (m.getByte(1) == 'o' || m.getByte(1) == 'O') && (m.getByte(2) == 'u' || m.getByte(2) == 'U') && (m.getByte(3) == 't' || m.getByte(3) == 'T') && (m.getByte(4) == 'e' || m.getByte(4) == 'E'); } catch (final IOException e) { throw new SipParseException(0, UNABLE_TO_PARSE_OUT_THE_HEADER_NAME_DUE_TO_UNDERLYING_IO_EXCEPTION, e); } } return false; } default boolean isRecordRouteHeader() { final Buffer m = getName(); if (m.getReadableBytes() == 12) { try { return (m.getByte(0) == 'R' || m.getByte(0) == 'r') && (m.getByte(1) == 'e' || m.getByte(1) == 'E') && (m.getByte(2) == 'c' || m.getByte(2) == 'C') && (m.getByte(3) == 'o' || m.getByte(3) == 'O') && (m.getByte(4) == 'r' || m.getByte(4) == 'R') && (m.getByte(5) == 'd' || m.getByte(5) == 'D') && m.getByte(6) == '-' && (m.getByte(7) == 'R' || m.getByte(7) == 'r') && (m.getByte(8) == 'o' || m.getByte(8) == 'O') && (m.getByte(9) == 'u' || m.getByte(9) == 'U') && (m.getByte(10) == 't' || m.getByte(10) == 'T') && (m.getByte(11) == 'e' || m.getByte(11) == 'E'); } catch (final IOException e) { throw new SipParseException(0, UNABLE_TO_PARSE_OUT_THE_HEADER_NAME_DUE_TO_UNDERLYING_IO_EXCEPTION, e); } } return false; } default RecordRouteHeader toRecordRouteHeader() { throw new ClassCastException(CANNOT_CAST_HEADER_OF_TYPE + getClass().getName() + " to type " + RecordRouteHeader.class.getName()); } default RouteHeader toRouteHeader() { throw new ClassCastException(CANNOT_CAST_HEADER_OF_TYPE + getClass().getName() + " to type " + RouteHeader.class.getName()); } default boolean isContentLengthHeader() { final Buffer m = getName(); try { if (m.getReadableBytes() == 14) { return (m.getByte(0) == 'C' || m.getByte(0) == 'c') && (m.getByte(1) == 'o' || m.getByte(1) == 'O') && (m.getByte(2) == 'n' || m.getByte(2) == 'N') && (m.getByte(3) == 't' || m.getByte(3) == 'T') && (m.getByte(4) == 'e' || m.getByte(4) == 'E') && (m.getByte(5) == 'n' || m.getByte(5) == 'N') && (m.getByte(6) == 't' || m.getByte(6) == 'T') && m.getByte(7) == '-' && (m.getByte(8) == 'L' || m.getByte(8) == 'l') && (m.getByte(9) == 'e' || m.getByte(9) == 'E') && (m.getByte(10) == 'n' || m.getByte(10) == 'N') && (m.getByte(11) == 'g' || m.getByte(11) == 'G') && (m.getByte(12) == 't' || m.getByte(12) == 'T') && (m.getByte(13) == 'h' || m.getByte(13) == 'H'); } else if (m.getReadableBytes() == 1) { return (m.getByte(0) == 'L' || m.getByte(0) == 'l'); } } catch (final IOException e) { throw new SipParseException(0, UNABLE_TO_PARSE_OUT_THE_HEADER_NAME_DUE_TO_UNDERLYING_IO_EXCEPTION, e); } return false; } default ContentLengthHeader toContentLengthHeader() { throw new ClassCastException(CANNOT_CAST_HEADER_OF_TYPE + getClass().getName() + " to type " + ContentLengthHeader.class.getName()); } default boolean isContentTypeHeader() { final Buffer m = getName(); try { if (m.getReadableBytes() == 12) { return (m.getByte(0) == 'C' || m.getByte(0) == 'c') && (m.getByte(1) == 'o' || m.getByte(1) == 'O') && (m.getByte(2) == 'n' || m.getByte(2) == 'N') && (m.getByte(3) == 't' || m.getByte(3) == 'T') && (m.getByte(4) == 'e' || m.getByte(4) == 'E') && (m.getByte(5) == 'n' || m.getByte(5) == 'N') && (m.getByte(6) == 't' || m.getByte(6) == 'T') && m.getByte(7) == '-' && (m.getByte(8) == 'T' || m.getByte(8) == 't') && (m.getByte(9) == 'y' || m.getByte(9) == 'Y') && (m.getByte(10) == 'p' || m.getByte(10) == 'P') && (m.getByte(11) == 'e' || m.getByte(11) == 'E'); } else if (m.getReadableBytes() == 1) { return (m.getByte(0) == 'C' || m.getByte(0) == 'c'); } } catch (final IOException e) { throw new SipParseException(0, UNABLE_TO_PARSE_OUT_THE_HEADER_NAME_DUE_TO_UNDERLYING_IO_EXCEPTION, e); } return false; } default ContentTypeHeader toContentTypeHeader() { throw new ClassCastException(CANNOT_CAST_HEADER_OF_TYPE + getClass().getName() + " to type " + ContentTypeHeader.class.getName()); } default boolean isExpiresHeader() { final Buffer m = getName(); try { if (m.getReadableBytes() == 7) { return (m.getByte(0) == 'E' || m.getByte(0) == 'e') && (m.getByte(1) == 'x' || m.getByte(1) == 'X') && (m.getByte(2) == 'p' || m.getByte(2) == 'P') && (m.getByte(3) == 'i' || m.getByte(3) == 'I') && (m.getByte(4) == 'r' || m.getByte(4) == 'R') && (m.getByte(5) == 'e' || m.getByte(5) == 'E') && (m.getByte(6) == 's' || m.getByte(6) == 'S'); } } catch (final IOException e) { throw new SipParseException(0, UNABLE_TO_PARSE_OUT_THE_HEADER_NAME_DUE_TO_UNDERLYING_IO_EXCEPTION, e); } return false; } default ExpiresHeader toExpiresHeader() { throw new ClassCastException(CANNOT_CAST_HEADER_OF_TYPE + getClass().getName() + " to type " + ExpiresHeader.class.getName()); } default boolean isCSeqHeader() { final Buffer m = getName(); try { if (m.getReadableBytes() == 4) { return (m.getByte(0) == 'C' || m.getByte(0) == 'c') && (m.getByte(1) == 'S' || m.getByte(1) == 's') && (m.getByte(2) == 'e' || m.getByte(2) == 'E') && (m.getByte(3) == 'q' || m.getByte(3) == 'Q'); } } catch (final IOException e) { throw new SipParseException(0, UNABLE_TO_PARSE_OUT_THE_HEADER_NAME_DUE_TO_UNDERLYING_IO_EXCEPTION, e); } return false; } default CSeqHeader toCSeqHeader() { throw new ClassCastException(CANNOT_CAST_HEADER_OF_TYPE + getClass().getName() + " to type " + CSeqHeader.class.getName()); } default boolean isMaxForwardsHeader() { final Buffer m = getName(); if (m.getReadableBytes() == 12) { try { return (m.getByte(0) == 'M' || m.getByte(0) == 'm') && (m.getByte(1) == 'a' || m.getByte(1) == 'A') && (m.getByte(2) == 'x' || m.getByte(2) == 'X') && m.getByte(3) == '-' && (m.getByte(4) == 'F' || m.getByte(4) == 'f') && (m.getByte(5) == 'o' || m.getByte(5) == 'O') && (m.getByte(6) == 'r' || m.getByte(6) == 'R') && (m.getByte(7) == 'w' || m.getByte(7) == 'W') && (m.getByte(8) == 'a' || m.getByte(8) == 'A') && (m.getByte(9) == 'r' || m.getByte(9) == 'R') && (m.getByte(10) == 'd' || m.getByte(10) == 'D') && (m.getByte(11) == 's' || m.getByte(11) == 'S'); } catch (final IOException e) { throw new SipParseException(0, UNABLE_TO_PARSE_OUT_THE_HEADER_NAME_DUE_TO_UNDERLYING_IO_EXCEPTION, e); } } return false; } default ViaHeader toViaHeader() { throw new ClassCastException(CANNOT_CAST_HEADER_OF_TYPE + getClass().getName() + " to type " + ViaHeader.class.getName()); } default boolean isViaHeader() { final Buffer m = getName(); try { if (m.getReadableBytes() == 3) { return (m.getByte(0) == 'V' || m.getByte(0) == 'v') && (m.getByte(1) == 'i' || m.getByte(1) == 'I') && (m.getByte(2) == 'a' || m.getByte(2) == 'A'); } else if (m.getReadableBytes() == 1) { // Short form for the via header is a 'v' return (m.getByte(0) == 'V' || m.getByte(0) == 'v'); } } catch (final IOException e) { throw new SipParseException(0, UNABLE_TO_PARSE_OUT_THE_HEADER_NAME_DUE_TO_UNDERLYING_IO_EXCEPTION, e); } return false; } default MaxForwardsHeader toMaxForwardsHeader() { throw new ClassCastException(CANNOT_CAST_HEADER_OF_TYPE + getClass().getName() + " to type " + MaxForwardsHeader.class.getName()); } default AddressParametersHeader toAddressParametersHeader() throws ClassCastException { throw new ClassCastException(CANNOT_CAST_HEADER_OF_TYPE + getClass().getName() + " to type " + AddressParametersHeader.class.getName()); } /** * Everything within the pkts.io SIP module are immutable so if you actually want * to change anything you have to create a copy, which will return a specific Builder * for that header. * * @return */ Builder copy(); interface Builder { Builder withValue(Buffer value); default Builder withValue(final String value) { return withValue(Buffers.wrap(value)); } H build() throws SipParseException; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy