From 35a64008a1e32647909641ab2375f100a2d97df7 Mon Sep 17 00:00:00 2001
From: junmingyang
Date: Tue, 28 Sep 2021 10:36:50 +0800
Subject: [PATCH] add WWWAuthenticateHeader
---
.../java/io/pkts/packet/sip/SipMessage.java | 227 ++++++++---------
.../sip/header/AddressParametersHeader.java | 3 +
.../io/pkts/packet/sip/header/CSeqHeader.java | 1 +
.../io/pkts/packet/sip/header/SipHeader.java | 188 ++++++++------
.../sip/header/WWWAuthenticateHeader.java | 92 +++++++
.../impl/AddressParametersHeaderImpl.java | 1 +
.../impl/WWWAuthenticateHeaderImpl.java | 184 ++++++++++++++
.../packet/sip/impl/ImmutableSipMessage.java | 20 +-
.../packet/sip/impl/SipMessageBuilder.java | 33 ++-
.../io/pkts/packet/sip/impl/SipParser.java | 233 ++++++++++--------
.../io/pkts/packet/sip/SipRequestTest.java | 168 +++++++++++--
.../impl/WWWAuthenticateHeaderImplTest.java | 77 ++++++
12 files changed, 873 insertions(+), 354 deletions(-)
create mode 100644 pkts-sip/src/main/java/io/pkts/packet/sip/header/WWWAuthenticateHeader.java
create mode 100644 pkts-sip/src/main/java/io/pkts/packet/sip/header/impl/WWWAuthenticateHeaderImpl.java
create mode 100644 pkts-sip/src/test/java/io/pkts/packet/sip/header/impl/WWWAuthenticateHeaderImplTest.java
diff --git a/pkts-sip/src/main/java/io/pkts/packet/sip/SipMessage.java b/pkts-sip/src/main/java/io/pkts/packet/sip/SipMessage.java
index 9afb7f7b..eb2bacee 100644
--- a/pkts-sip/src/main/java/io/pkts/packet/sip/SipMessage.java
+++ b/pkts-sip/src/main/java/io/pkts/packet/sip/SipMessage.java
@@ -3,20 +3,7 @@
import io.pkts.buffer.Buffer;
import io.pkts.buffer.Buffers;
import io.pkts.packet.sip.address.SipURI;
-import io.pkts.packet.sip.header.AddressParametersHeader;
-import io.pkts.packet.sip.header.CSeqHeader;
-import io.pkts.packet.sip.header.CallIdHeader;
-import io.pkts.packet.sip.header.ContactHeader;
-import io.pkts.packet.sip.header.ContentLengthHeader;
-import io.pkts.packet.sip.header.ContentTypeHeader;
-import io.pkts.packet.sip.header.ExpiresHeader;
-import io.pkts.packet.sip.header.FromHeader;
-import io.pkts.packet.sip.header.MaxForwardsHeader;
-import io.pkts.packet.sip.header.RecordRouteHeader;
-import io.pkts.packet.sip.header.RouteHeader;
-import io.pkts.packet.sip.header.SipHeader;
-import io.pkts.packet.sip.header.ToHeader;
-import io.pkts.packet.sip.header.ViaHeader;
+import io.pkts.packet.sip.header.*;
import io.pkts.packet.sip.impl.SipInitialLine;
import io.pkts.packet.sip.impl.SipParser;
@@ -35,9 +22,8 @@
/**
* Packet representing a SIP message.
- *
+ *
* @author jonas@jonasborjesson.com
- *
*/
public interface SipMessage extends Cloneable {
@@ -46,7 +32,7 @@ public interface SipMessage extends Cloneable {
/**
* The first line of a sip message, which is either a request or a response
* line
- *
+ *
* @return
*/
Buffer getInitialLine();
@@ -56,15 +42,14 @@ public interface SipMessage extends Cloneable {
/**
* Got tired of casting the {@link SipMessage} into a {@link SipRequest} so
* you can use this method instead. Just a short cut for:
- *
+ *
*
- * (SipRequest)sipMessage;
+ * (SipRequest)sipMessage;
*
- *
+ *
* @return this but casted into a {@link SipRequest}
- * @throws ClassCastException
- * in case this {@link SipMessage} is actually a
- * {@link SipResponse}.
+ * @throws ClassCastException in case this {@link SipMessage} is actually a
+ * {@link SipResponse}.
*/
default SipRequest toRequest() throws ClassCastException {
throw new ClassCastException("Unable to cast a " + this.getClass().getName() + " into a " + SipRequest.class.getName());
@@ -73,15 +58,14 @@ default SipRequest toRequest() throws ClassCastException {
/**
* Got tired of casting the {@link SipMessage} into a {@link SipResponse} so
* you can use this method instead. Just a short cut for:
- *
+ *
*
- * (SipResponse)sipMessage;
+ * (SipResponse)sipMessage;
*
- *
+ *
* @return this but casted into a {@link SipResponse}
- * @throws ClassCastException
- * in case this {@link SipMessage} is actually a
- * {@link SipResponse}.
+ * @throws ClassCastException in case this {@link SipMessage} is actually a
+ * {@link SipResponse}.
*/
default SipResponse toResponse() throws ClassCastException {
throw new ClassCastException("Unable to cast a " + this.getClass().getName() + " into a " + SipResponse.class.getName());
@@ -99,12 +83,11 @@ default SipResponse toResponse() throws ClassCastException {
* {@link CSeqHeader}
* {@link ViaHeader}
*
- *
+ *
* @param responseCode
* @return
- * @throws SipParseException
- * in case anything goes wrong when parsing out headers from the
- * {@link SipRequest}
+ * @throws SipParseException in case anything goes wrong when parsing out headers from the
+ * {@link SipRequest}
*/
default SipResponse.Builder createResponse(final int responseCode) throws SipParseException, ClassCastException {
return createResponse(responseCode, null);
@@ -116,7 +99,7 @@ default SipResponse.Builder createResponse(int responseCode, Buffer content) thr
/**
* Check whether this sip message is a response or not
- *
+ *
* @return
*/
default boolean isResponse() {
@@ -125,7 +108,7 @@ default boolean isResponse() {
/**
* Check whether this sip message is a request or not
- *
+ *
* @return
*/
default boolean isRequest() {
@@ -282,24 +265,23 @@ default boolean isTimeout() {
/**
* Checks whether this {@link SipMessage} is carrying anything in its
* message body.
- *
+ *
* @return true if this {@link SipMessage} has a message body, false
- * otherwise.
+ * otherwise.
*/
boolean hasContent();
/**
* Get the method of this sip message
- *
+ *
* @return
*/
Buffer getMethod() throws SipParseException;
/**
* Get the header as a buffer
- *
- * @param headerName
- * the name of the header we wish to fetch
+ *
+ * @param headerName the name of the header we wish to fetch
* @return the header as a {@link SipHeader} or null if not found
* @throws SipParseException
*/
@@ -307,9 +289,8 @@ default boolean isTimeout() {
/**
* Same as {@link #getHeader(Buffers.wrap(keyParameter)}.
- *
- * @param headerName
- * the name of the header we wish to fetch
+ *
+ * @param headerName the name of the header we wish to fetch
* @return the header as a {@link SipHeader} or null if not found
* @throws SipParseException
*/
@@ -328,16 +309,15 @@ default boolean isTimeout() {
/**
* Convenience method for fetching the from-header
- *
+ *
* @return the from header as a buffer
- * @throws SipParseException
- * TODO
+ * @throws SipParseException TODO
*/
FromHeader getFromHeader() throws SipParseException;
/**
* Convenience method for fetching the to-header
- *
+ *
* @return the to header as a buffer
*/
ToHeader getToHeader() throws SipParseException;
@@ -347,9 +327,9 @@ default boolean isTimeout() {
* has been sent then there should always be a {@link ViaHeader} present.
* However, you just created a {@link SipMessage} youself then this method
* may return null so please check for it.
- *
+ *
* @return the top-most {@link ViaHeader} or null if there are no
- * {@link ViaHeader}s on this message just yet.
+ * {@link ViaHeader}s on this message just yet.
* @throws SipParseException
*/
ViaHeader getViaHeader() throws SipParseException;
@@ -364,7 +344,6 @@ default boolean isTimeout() {
List getViaHeaders() throws SipParseException;
/**
- *
* @return
* @throws SipParseException
*/
@@ -372,10 +351,10 @@ default boolean isTimeout() {
/**
* Get the top-most {@link RecordRouteHeader} header if present.
- *
+ *
* @return the top-most {@link RecordRouteHeader} header or null if there
- * are no {@link RecordRouteHeader} headers found in this
- * {@link SipMessage}.
+ * are no {@link RecordRouteHeader} headers found in this
+ * {@link SipMessage}.
* @throws SipParseException
*/
RecordRouteHeader getRecordRouteHeader() throws SipParseException;
@@ -384,7 +363,7 @@ default boolean isTimeout() {
* Get all the RecordRoute-headers in this {@link SipMessage}. If there are
* no {@link RecordRouteHeader}s in this {@link SipMessage} then an empty
* list will be returned.
- *
+ *
* @return
* @throws SipParseException
*/
@@ -392,9 +371,9 @@ default boolean isTimeout() {
/**
* Get the top-most {@link RouteHeader} header if present.
- *
+ *
* @return the top-most {@link RouteHeader} header or null if there are no
- * {@link RouteHeader} headers found in this {@link SipMessage}.
+ * {@link RouteHeader} headers found in this {@link SipMessage}.
* @throws SipParseException
*/
RouteHeader getRouteHeader() throws SipParseException;
@@ -403,7 +382,7 @@ default boolean isTimeout() {
* Get all the Route-headers in this {@link SipMessage}. If there are no
* {@link RouteHeader}s in this {@link SipMessage} then an empty list will
* be returned.
- *
+ *
* @return
* @throws SipParseException
*/
@@ -411,7 +390,7 @@ default boolean isTimeout() {
/**
* Get the {@link ExpiresHeader}
- *
+ *
* @return
* @throws SipParseException
*/
@@ -419,7 +398,7 @@ default boolean isTimeout() {
/**
* Get the {@link ContactHeader}
- *
+ *
* @return
* @throws SipParseException
*/
@@ -428,7 +407,7 @@ default boolean isTimeout() {
/**
* Get the {@link ContentTypeHeader} for this message. If there is no
* Content-Type header in this SIP message then null will be returned.
- *
+ *
* @return the {@link ContentTypeHeader} or null if there is none.
* @throws SipParseException
*/
@@ -443,38 +422,38 @@ default boolean isTimeout() {
* @return the value of the Content-Length header if present or zero if
* there was no such header (or of course if the Content-Length header was
* present but actually had the value of zero)
- *
* @throws SipParseException
*/
int getContentLength() throws SipParseException;
/**
* Convenience method for fetching the call-id-header
- *
+ *
* @return the call-id header as a buffer
*/
CallIdHeader getCallIDHeader() throws SipParseException;
/**
* Convenience method for fetching the CSeq header
- *
+ *
* @return
* @throws SipParseException
*/
CSeqHeader getCSeqHeader() throws SipParseException;
+ WWWAuthenticateHeader getWWWAuthenticateHeader() throws SipParseException;
+
/**
* Convenience method for determining whether the method of this message is
* an INVITE or not.
- *
+ *
* Note, this method only determined if it is an INVITE, which then could
* be either a request or a response, which you can check with {@link SipMessage#isRequest()} and
- * {@link SipMessage#isResponse()}
+ * {@link SipMessage#isResponse()}
*
* @return true if the method of this message is a INVITE, false otherwise.
- * @throws SipParseException
- * in case the method could not be parsed out of the underlying
- * buffer.
+ * @throws SipParseException in case the method could not be parsed out of the underlying
+ * buffer.
*/
default boolean isInvite() throws SipParseException {
final Buffer m = getMethod();
@@ -491,7 +470,7 @@ default boolean isInvite() throws SipParseException {
*
* @return true if the method of this message is a REGISTER, false otherwise.
* @throws SipParseException in case the method could not be parsed out of the underlying
- * buffer.
+ * buffer.
*/
default boolean isRegister() throws SipParseException {
final Buffer m = getMethod();
@@ -509,7 +488,7 @@ default boolean isRegister() throws SipParseException {
*
* @return true if the method of this message is a BYE, false otherwise.
* @throws SipParseException in case the method could not be parsed out of the underlying
- * buffer.
+ * buffer.
*/
default boolean isBye() throws SipParseException {
final Buffer m = getMethod();
@@ -526,9 +505,8 @@ default boolean isBye() throws SipParseException {
* is an ACK Request or not!
*
* @return true if the method of this message is a ACK, false otherwise.
- * @throws SipParseException
- * in case the method could not be parsed out of the underlying
- * buffer.
+ * @throws SipParseException in case the method could not be parsed out of the underlying
+ * buffer.
*/
default boolean isAck() throws SipParseException {
final Buffer m = getMethod();
@@ -544,9 +522,8 @@ default boolean isAck() throws SipParseException {
* a CANCEL or not
*
* @return true if the method of this message is a CANCEL, false otherwise.
- * @throws SipParseException
- * in case the method could not be parsed out of the underlying
- * buffer.
+ * @throws SipParseException in case the method could not be parsed out of the underlying
+ * buffer.
*/
default boolean isCancel() throws SipParseException {
final Buffer m = getMethod();
@@ -564,9 +541,8 @@ default boolean isCancel() throws SipParseException {
* this is an OPTIONS Request or not!
*
* @return true if the method of this message is a OPTIONS, false otherwise.
- * @throws SipParseException
- * in case the method could not be parsed out of the underlying
- * buffer.
+ * @throws SipParseException in case the method could not be parsed out of the underlying
+ * buffer.
*/
default boolean isOptions() throws SipParseException {
final Buffer m = getMethod();
@@ -585,9 +561,8 @@ default boolean isOptions() throws SipParseException {
* this is an MESSAGE Request or not!
*
* @return true if the method of this message is a MESSAGE, false otherwise.
- * @throws SipParseException
- * in case the method could not be parsed out of the underlying
- * buffer.
+ * @throws SipParseException in case the method could not be parsed out of the underlying
+ * buffer.
*/
default boolean isMessage() throws SipParseException {
final Buffer m = getMethod();
@@ -605,9 +580,8 @@ default boolean isMessage() throws SipParseException {
* is an INFO Request or not!
*
* @return true if the method of this message is a INFO, false otherwise.
- * @throws SipParseException
- * in case the method could not be parsed out of the underlying
- * buffer.
+ * @throws SipParseException in case the method could not be parsed out of the underlying
+ * buffer.
*/
default boolean isInfo() throws SipParseException {
final Buffer m = getMethod();
@@ -621,7 +595,7 @@ default boolean isInfo() throws SipParseException {
/**
* Checks whether or not this request is considered to be an "initial"
* request, i.e., a request that does not go within a dialog.
- *
+ *
* @return
* @throws SipParseException
*/
@@ -655,7 +629,7 @@ default boolean isSubsequent() throws SipParseException {
*
proxy require - checks if all items of the proxy require header are
* present in the list of the extensions from the module parameter
* proxy_require.
- *
+ *
* parse uri's - checks if the specified URIs are present and parseable
* by the SIP-router parsers
* digest credentials - Check all instances of digest credentials in a
@@ -663,13 +637,12 @@ default boolean isSubsequent() throws SipParseException {
* and have meaningful values.
*
*
- *
+ *
* getAllHeaders() {
* headers, request-uri, body etc through the withXXX-methods and then upon build time,
* the builder will call out to any registered functions (as registered through the onXXX-methods)
* allowing the application to make any last minute changes.
- *
+ *
* In a real SIP stack there are headers within a {@link SipMessage} that typically needs to be
* manipulated before being sent out over the network. One such header is the {@link ViaHeader} where
* the transport layer typically has the responsibility to fill out the ip and port from which the
* message was sent as well as specify the transport used.
- *
+ *
* The idea is as follows: everything within this SIP library is immutable, which includes {@link SipRequest}
* and {@link SipResponse}s but if you build a stack, the stack may actually need to change headers
* before creating the "final" version of the {@link SipMessage} which is then sent out over the network.
@@ -750,10 +722,10 @@ default List getAllHeaders() {
* is about to be sent out and will (should) be filled out by the transport layer. Therefore, if you build
* a stack you probably want to pass down a {@link Builder} to be "sent" all the way down to the transport layer
* which will
- *
+ *
* All callback as registered through the various onXXXX-methods allow for multiple callbacks to be registered.
* They are called in reverse order from the order of registration.
- *
+ *
* TODO: don't think I will do this anymore. If you add the same header via an of the three methods then
* TODO: that header will be included three times. I.e., if you call withHeaderBuilder and withHeader and
* TODO: the same header also exists in the template then it will be included that many times.
@@ -763,14 +735,14 @@ default List getAllHeaders() {
* Any header added through a withXXX-method
* Any header copied from a "template"
*
- *
+ *
* I.e., if you e.g. have added a from-header builder object
* through {@link Builder#withFromHeader(AddressParametersHeader.Builder)}
* method then that builder will be used even if this builder is based off
* another {@link SipMessage} (which then serves as a "template")
- *
+ *
* TODO: add and/or clarify how headers are treated in general, i.e.:
- *
+ *
* A header can be added to the final {@link SipMessage} that is being built via 1 out of 3 ways:
*
* - Either via explicitly calling withXXXBuilder to add a builder object for that header
@@ -778,7 +750,7 @@ default List getAllHeaders() {
* - Or by copying an existing header from the {@link SipMessage} we are using as a template
* for constructing this new sip message.
*
- *
+ *
* No matter how a header is added to the final message (via one of the three ways described above)
* you will be given the opportunity to change the value of the header by registering a function
* for manipulating the header just before it gets added. You do so through two methods:
@@ -786,13 +758,12 @@ default List getAllHeaders() {
* {@link io.pkts.packet.sip.SipMessage.Builder#onHeader(Function)}
* or {@link io.pkts.packet.sip.SipMessage.Builder#onHeaderBuilder(Consumer)}
*
- *
+ *
* The first method is called when a header was added without a builder already created from it,
* which is the case when a header is copied from the template or when you called onXXXHeader(header).
* The reason for this is unless you actually want to change it, we don't want to waste time on
* constructing a builder that you are not going to use. However, if you added the header
* as a builder object then we will of coruse use that builder.
- *
*/
interface Builder {
@@ -826,7 +797,7 @@ default SipMessage.Builder toSipResponseBuilder() {
* {@link ContentLengthHeader} - Will be added if there is a body
* on the message and the length set to the correct length.
*
- *
+ *
* but if you don't want that, simply call this method and all the defaults
* of this builder will be suspended. Of course, if you wish to actually
* construct a valid {@link SipMessage} you are then responsible for adding
@@ -871,9 +842,9 @@ default SipMessage.Builder toSipResponseBuilder() {
* returns a {@link SipHeader}, which is the header that will be pushed onto the new
* {@link SipMessage}. If you do not want to include the header, then simply return
* null and the header will be dropped.
- *
+ *
* If you wish to leave the header un-touched, then simply return it has is.
- *
+ *
* Also note that the following headers have explicit "on" methods (they are considered
* to be "system" headers):
*
@@ -887,17 +858,16 @@ default SipMessage.Builder toSipResponseBuilder() {
* {@link MaxForwardsHeader}
* {@link CSeqHeader}
*
- *
+ *
* The reason is simply because these are typically manipulated before
* copying them over to a new request or response (e.g., Max Forwards is decremented,
* CSeq may increase etc) and therefore it makes life easier if those headers are
* "down casted" to their specific types.
*
- *
* @param f
* @return
* @throws IllegalStateException in case a function already had been registered with
- * this builder.
+ * this builder.
*/
Builder onHeader(Function f) throws IllegalStateException;
@@ -905,13 +875,14 @@ default SipMessage.Builder toSipResponseBuilder() {
* Adds the header to the list of headers already specified within this builder.
* The header will be added last to the list of headers. Any already existing
* headers with the same name will be preserved as is.
- *
+ *
* If there are any headers with the same name as part of the {@link SipMessage}
* used as a template, then those headers will
- *
+ *
* TODO: this is essentially an "add header" so should it be called that?
* TODO: and then should there be a set version? Just goes bad with a fluent
* TODO: naming.
+ *
* @param header
* @return
*/
@@ -922,8 +893,9 @@ default SipMessage.Builder toSipResponseBuilder() {
/**
* Push the header to be the first on the list of existing headers already
* added to this builder.
- *
+ *
* TODO: naming
+ *
* @return
*/
Builder withPushHeader(SipHeader header);
@@ -939,19 +911,25 @@ default SipMessage.Builder toSipResponseBuilder() {
* @return
*/
Builder withFromHeader(FromHeader from);
+
Builder withFromHeader(String from);
Builder onToHeader(Consumer> f);
+
Builder withToHeader(ToHeader to);
+
Builder withToHeader(String to);
Builder onContactHeader(Consumer> f);
+
Builder withContactHeader(ContactHeader contact);
Builder onCSeqHeader(Consumer f);
+
Builder withCSeqHeader(CSeqHeader cseq);
Builder onMaxForwardsHeader(Consumer f);
+
Builder withMaxForwardsHeader(MaxForwardsHeader maxForwards);
Builder withCallIdHeader(CallIdHeader callID);
@@ -995,7 +973,7 @@ default SipMessage.Builder toSipResponseBuilder() {
* @param routes
* @return
*/
- Builder withRouteHeaders(RouteHeader ... routes);
+ Builder withRouteHeaders(RouteHeader... routes);
Builder withRouteHeaders(List routes);
@@ -1012,7 +990,7 @@ default SipMessage.Builder toSipResponseBuilder() {
* Pop the top-most route. Note, if you e.g. add a {@link RouteHeader} via the method
* {@link io.pkts.packet.sip.SipMessage.Builder#withTopMostRouteHeader(RouteHeader)} followed
* by this method, then the route you just added will be removed again. Order is important!
- *
+ *
* Note: if you actually wanted to know the value of that {@link RouteHeader} then you should
* really have checked it on the {@link SipMessage} you received and not on the builder
* object.
@@ -1074,7 +1052,7 @@ default SipMessage.Builder toSipResponseBuilder() {
* @param recordRoute
* @return
*/
- Builder withRecordRouteHeaders(RecordRouteHeader ... recordRoute);
+ Builder withRecordRouteHeaders(RecordRouteHeader... recordRoute);
Builder withRecordRouteHeaders(List recordRoute);
@@ -1088,7 +1066,6 @@ default SipMessage.Builder toSipResponseBuilder() {
Builder withTopMostRecordRouteHeader(RecordRouteHeader recordRoute);
/**
- *
* @param f
* @return
*/
@@ -1116,15 +1093,15 @@ default SipMessage.Builder toSipResponseBuilder() {
* index of the Via being processed. The top-most Via header will NEVER
* be passed to this registered function but rather to the one explicitly
* meant for processing the top-most via (see {@link Builder#onTopMostViaHeader(Consumer)}.
- *
+ *
* I.e., Let's say you have the following message (request or response, same same):
*
*
- * ...
- * Via: SIP/2.0/TCP 12.13.14.15;branch=z9hG4bK-asdf
- * Via: SIP/2.0/UDP 60.61.62.63;branch=z9hG4bK-1234
- * Via: SIP/2.0/UDP 96.97.98.99;branch=z9hG4bK-wxyz
- * ...
+ * ...
+ * Via: SIP/2.0/TCP 12.13.14.15;branch=z9hG4bK-asdf
+ * Via: SIP/2.0/UDP 60.61.62.63;branch=z9hG4bK-1234
+ * Via: SIP/2.0/UDP 96.97.98.99;branch=z9hG4bK-wxyz
+ * ...
*
*
*
@@ -1166,7 +1143,7 @@ default SipMessage.Builder toSipResponseBuilder() {
* @param vias
* @return
*/
- Builder withViaHeaders(ViaHeader ... vias);
+ Builder withViaHeaders(ViaHeader... vias);
/**
* Set a list of Via headers. Any previously Via headers
@@ -1194,7 +1171,7 @@ default SipMessage.Builder toSipResponseBuilder() {
* a function with {@link Builder#onTopMostViaHeader(Consumer)} is rather silly
* so therefore you can just indicate that you want a new top-most via header
* but you will fill out all details later.
- *
+ *
* NOTE: if you do NOT register a function to handle this "empty" Via-header
* through the method {@link Builder#onTopMostViaHeader(Consumer)} things will
* blow up later with you try to build this message.
@@ -1207,7 +1184,7 @@ default SipMessage.Builder toSipResponseBuilder() {
* Pop the top-most via. Note, if you e.g. add a {@link ViaHeader} through the method
* {@link io.pkts.packet.sip.SipMessage.Builder#withTopMostViaHeader(ViaHeader)} followed
* by this method, then the Via you just added will be removed again. Order is important!
- *
+ *
* Note: if you actually wanted to know the value of that {@link ViaHeader} then you should
* really have checked it on the {@link SipMessage} you received and not on the builder
* object.
diff --git a/pkts-sip/src/main/java/io/pkts/packet/sip/header/AddressParametersHeader.java b/pkts-sip/src/main/java/io/pkts/packet/sip/header/AddressParametersHeader.java
index ce0b907e..76a4781e 100644
--- a/pkts-sip/src/main/java/io/pkts/packet/sip/header/AddressParametersHeader.java
+++ b/pkts-sip/src/main/java/io/pkts/packet/sip/header/AddressParametersHeader.java
@@ -72,6 +72,7 @@ default boolean isAddressParametersHeader() {
return true;
}
+ @Override
default AddressParametersHeader toAddressParametersHeader() throws ClassCastException {
return this;
}
@@ -100,6 +101,7 @@ protected Builder(final Buffer name, final ParametersSupport params) {
}
+ @Override
public final Builder withValue(final Buffer buffer) {
throw new RuntimeException("Not implemented yet");
// Address.frame(buffer).copy();
@@ -388,6 +390,7 @@ public final Builder withAddress(final Address address) throws SipParseExcept
* @throws SipParseException in case anything goes wrong while constructing the
* {@link ToHeader}.
*/
+ @Override
public final T build() throws SipParseException {
if (addressBuilder == null) {
throw new SipParseException("You must specify an address of some sort.");
diff --git a/pkts-sip/src/main/java/io/pkts/packet/sip/header/CSeqHeader.java b/pkts-sip/src/main/java/io/pkts/packet/sip/header/CSeqHeader.java
index d4cf0867..29c57ef2 100644
--- a/pkts-sip/src/main/java/io/pkts/packet/sip/header/CSeqHeader.java
+++ b/pkts-sip/src/main/java/io/pkts/packet/sip/header/CSeqHeader.java
@@ -114,6 +114,7 @@ public Builder withValue(Buffer value) {
throw new RuntimeException("Not implemented yet");
}
+ @Override
public CSeqHeader build() {
final int size = Buffers.stringSizeOf(this.cseq);
final Buffer value = Buffers.createBuffer(size + 1 + this.method.getReadableBytes());
diff --git a/pkts-sip/src/main/java/io/pkts/packet/sip/header/SipHeader.java b/pkts-sip/src/main/java/io/pkts/packet/sip/header/SipHeader.java
index 64dea7ca..46d7e201 100644
--- a/pkts-sip/src/main/java/io/pkts/packet/sip/header/SipHeader.java
+++ b/pkts-sip/src/main/java/io/pkts/packet/sip/header/SipHeader.java
@@ -128,7 +128,6 @@ default int getBufferSize() {
* 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.
*/
@@ -188,9 +187,9 @@ default boolean isFromHeader() {
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');
+ (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';
}
@@ -200,6 +199,39 @@ default boolean isFromHeader() {
return false;
}
+ default boolean isWWWAuthenticateHeader() {
+ //WWW-Authenticate
+ final Buffer m = getName();
+ try {
+ if (m.getReadableBytes() == 16) {
+ return (m.getByte(0) == 'W' || m.getByte(0) == 'w') &&
+ (m.getByte(1) == 'W' || m.getByte(1) == 'w') &&
+ (m.getByte(2) == 'W' || m.getByte(2) == 'w') &&
+ m.getByte(3) == '-' &&
+ (m.getByte(4) == 'A' || m.getByte(4) == 'a') &&
+ (m.getByte(5) == 'U' || m.getByte(5) == 'u') &&
+ (m.getByte(6) == 'T' || m.getByte(6) == 't') &&
+ (m.getByte(7) == 'H' || m.getByte(7) == 'h') &&
+ (m.getByte(8) == 'E' || m.getByte(8) == 'e') &&
+ (m.getByte(9) == 'N' || m.getByte(9) == 'n') &&
+ (m.getByte(10) == 'T' || m.getByte(10) == 't') &&
+ (m.getByte(11) == 'I' || m.getByte(11) == 'i') &&
+ (m.getByte(12) == 'C' || m.getByte(12) == 'c') &&
+ (m.getByte(13) == 'A' || m.getByte(13) == 'a') &&
+ (m.getByte(14) == 'T' || m.getByte(14) == 't') &&
+ (m.getByte(15) == 'E' || m.getByte(15) == '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 WWWAuthenticateHeader toWWWAuthenticateHeader() {
+ throw new ClassCastException(CANNOT_CAST_HEADER_OF_TYPE + getClass().getName()
+ + " to type " + WWWAuthenticateHeader.class.getName());
+ }
+
default FromHeader toFromHeader() {
throw new ClassCastException(CANNOT_CAST_HEADER_OF_TYPE + getClass().getName()
+ " to type " + FromHeader.class.getName());
@@ -244,12 +276,12 @@ default boolean isContactHeader() {
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');
+ (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');
@@ -270,12 +302,12 @@ default boolean isSubjectHeader() {
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');
+ (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);
@@ -288,12 +320,12 @@ default boolean isCallIdHeader() {
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(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');
+ (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');
@@ -315,10 +347,10 @@ default boolean isRouteHeader() {
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');
+ (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);
}
@@ -331,17 +363,17 @@ default boolean isRecordRouteHeader() {
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(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');
+ (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);
}
@@ -364,19 +396,19 @@ default boolean isContentLengthHeader() {
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(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');
+ (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');
@@ -397,17 +429,17 @@ default boolean isContentTypeHeader() {
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(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');
+ (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');
}
@@ -427,12 +459,12 @@ default boolean isExpiresHeader() {
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');
+ (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);
@@ -450,9 +482,9 @@ default boolean isCSeqHeader() {
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');
+ (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);
@@ -470,17 +502,17 @@ default boolean isMaxForwardsHeader() {
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(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');
+ (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);
}
@@ -498,8 +530,8 @@ default boolean isViaHeader() {
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');
+ (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');
diff --git a/pkts-sip/src/main/java/io/pkts/packet/sip/header/WWWAuthenticateHeader.java b/pkts-sip/src/main/java/io/pkts/packet/sip/header/WWWAuthenticateHeader.java
new file mode 100644
index 00000000..ab5d4f9f
--- /dev/null
+++ b/pkts-sip/src/main/java/io/pkts/packet/sip/header/WWWAuthenticateHeader.java
@@ -0,0 +1,92 @@
+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.WWWAuthenticateHeaderImpl;
+
+
+public interface WWWAuthenticateHeader extends SipHeader {
+
+ Buffer NAME = Buffers.wrap("WWW-Authenticate");
+
+ Buffer getRealm();
+
+ Buffer getNonce();
+
+ Buffer getAlgorithm();
+
+ Buffer getQop();
+
+ static WWWAuthenticateHeader frame(final Buffer buffer) throws SipParseException {
+ try {
+ return new WWWAuthenticateHeader.Builder(buffer).build();
+ } catch (final Exception e) {
+ throw new SipParseException(0, "Unable to frame the WWWAuthenticate header due to IOException", e);
+ }
+ }
+
+ @Override
+ default WWWAuthenticateHeader toWWWAuthenticateHeader() {
+ return this;
+ }
+
+
+ class Builder implements SipHeader.Builder {
+ private Buffer value;
+
+ private Buffer realm;
+ private Buffer nonce;
+ private Buffer algorithm;
+ private Buffer qop;
+
+ public Builder() {
+
+ }
+
+ public Builder(Buffer value) {
+ this.value = value;
+ }
+
+ @Override
+ public WWWAuthenticateHeader.Builder withValue(Buffer value) {
+ this.value = value;
+ return this;
+ }
+
+ public WWWAuthenticateHeader.Builder withRealm(Buffer realm) {
+ this.realm = realm;
+ return this;
+ }
+
+ public WWWAuthenticateHeader.Builder withNonce(Buffer nonce) {
+ this.nonce = nonce;
+ return this;
+ }
+
+ public WWWAuthenticateHeader.Builder withAlgorithm(Buffer algorithm) {
+ this.algorithm = algorithm;
+ return this;
+ }
+
+ public WWWAuthenticateHeader.Builder withQop(Buffer qop) {
+ this.qop = qop;
+ return this;
+ }
+
+ @Override
+ public WWWAuthenticateHeader build() throws SipParseException {
+ if (value == null &&
+ (this.realm == null && this.nonce == null)) {
+ throw new SipParseException("You must specify the [value] or [realm/nonce] of the WWWAuthenticate-Header");
+ }
+
+ if (this.value != null) {
+ return new WWWAuthenticateHeaderImpl(value);
+ } else {
+ return new WWWAuthenticateHeaderImpl(realm, nonce, algorithm, qop);
+ }
+ }
+ }
+
+}
diff --git a/pkts-sip/src/main/java/io/pkts/packet/sip/header/impl/AddressParametersHeaderImpl.java b/pkts-sip/src/main/java/io/pkts/packet/sip/header/impl/AddressParametersHeaderImpl.java
index 3ba71c8b..a5bfc5e0 100644
--- a/pkts-sip/src/main/java/io/pkts/packet/sip/header/impl/AddressParametersHeaderImpl.java
+++ b/pkts-sip/src/main/java/io/pkts/packet/sip/header/impl/AddressParametersHeaderImpl.java
@@ -41,6 +41,7 @@ public Address getAddress() {
return this.address;
}
+ @Override
public AddressParametersHeader.Builder copy() {
AddressParametersHeader.Builder b = AddressParametersHeader.with(getName());
b.withParameters(getRawParams());
diff --git a/pkts-sip/src/main/java/io/pkts/packet/sip/header/impl/WWWAuthenticateHeaderImpl.java b/pkts-sip/src/main/java/io/pkts/packet/sip/header/impl/WWWAuthenticateHeaderImpl.java
new file mode 100644
index 00000000..42413ead
--- /dev/null
+++ b/pkts-sip/src/main/java/io/pkts/packet/sip/header/impl/WWWAuthenticateHeaderImpl.java
@@ -0,0 +1,184 @@
+package io.pkts.packet.sip.header.impl;
+
+import io.pkts.buffer.Buffer;
+import io.pkts.buffer.Buffers;
+import io.pkts.packet.sip.SipParseException;
+import io.pkts.packet.sip.header.WWWAuthenticateHeader;
+import io.pkts.packet.sip.impl.SipParser;
+
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+public class WWWAuthenticateHeaderImpl extends SipHeaderImpl implements WWWAuthenticateHeader {
+
+
+ private Map paramMap = new LinkedHashMap<>();
+
+ private Buffer realm;
+ private Buffer nonce;
+ private Buffer algorithm;
+ private Buffer qop;
+
+
+ /**
+ * @param value
+ */
+ public WWWAuthenticateHeaderImpl(Buffer value) {
+ super(WWWAuthenticateHeader.NAME, value);
+
+ Buffer original = value.clone();
+ Buffer params = null;
+ if (original.hasReadableBytes()) {
+ params = original.slice("Digest ".length(), original.getUpperBoundary());
+ }
+
+ final byte[] VALUE_END_1 = Buffers.wrap("\", ").getArray();
+ final byte[] VALUE_END_2 = Buffers.wrap(", ").getArray();
+
+ //WWW-Authenticate: Digest realm="10.32.26.25",
+ // nonce="bee3366b-cf59-476e-bc5e-334e0d65b386",
+ // algorithm=MD5,
+ // qop="auth"
+
+ try {
+ // 思路:
+ // 1 遇到[=]号是key结束,遇到[,]或[", ]或[\r\n]是value结束
+ // 2 每次遇"="或”,”标识lastMarkIndex
+ int lastMarkIndex = params.getReaderIndex();
+ boolean inKey = true;
+ Buffer latestKey = Buffers.EMPTY_BUFFER, latestValue;
+ while (params.hasReadableBytes() && params.getReaderIndex() <= params.getUpperBoundary()) {
+ if (inKey && SipParser.isNext(params, SipParser.EQ)) {
+ //遇到[=]认为key结束
+ latestKey = params.slice(lastMarkIndex, params.getReaderIndex());
+ params.setReaderIndex(params.getReaderIndex() + 1);
+ if (SipParser.isNext(params, SipParser.DQUOT)) {
+ //跳过[="]等号后的第1个双引号
+ params.setReaderIndex(params.getReaderIndex() + 1);
+ inKey = false;
+ }
+ lastMarkIndex = params.getReaderIndex();
+ } else if (params.getReadableBytes() == 1 ||
+ SipParser.isNext(params, VALUE_END_1) ||
+ SipParser.isNext(params, VALUE_END_2)) {
+ //遇到[", ]或[, ]视为value结束
+ if (params.getReadableBytes() == 1 && params.peekByte() != SipParser.DQUOT) {
+ latestValue = params.slice(lastMarkIndex, params.getReaderIndex() + 1);
+ } else {
+ latestValue = params.slice(lastMarkIndex, params.getReaderIndex());
+ }
+
+ paramMap.put(latestKey, latestValue);
+
+ if (params.getReadableBytes() == 1) {
+ params.setReaderIndex(params.getReaderIndex() + 1);
+ } else if (SipParser.isNext(params, VALUE_END_1)) {
+ params.setReaderIndex(params.getReaderIndex() + VALUE_END_1.length);
+ } else if (SipParser.isNext(params, VALUE_END_2)) {
+ params.setReaderIndex(params.getReaderIndex() + VALUE_END_2.length);
+ }
+
+ lastMarkIndex = params.getReaderIndex();
+
+ inKey = true;
+ } else {
+ params.setReaderIndex(params.getReaderIndex() + 1);
+ }
+ }
+ } catch (Exception e) {
+ throw new SipParseException(NAME + " parse error, " + e.getCause());
+ }
+ }
+
+
+ public WWWAuthenticateHeaderImpl(Buffer realm, Buffer nonce, Buffer algorithm, Buffer qop) {
+ super(WWWAuthenticateHeader.NAME, Buffers.EMPTY_BUFFER);
+ this.realm = realm;
+ this.nonce = nonce;
+ this.algorithm = algorithm;
+ this.qop = qop;
+ }
+
+ @Override
+ public Buffer getValue() {
+ Buffer value = super.getValue();
+ if (value != null && value != Buffers.EMPTY_BUFFER) {
+ return value;
+ }
+ StringBuilder sb = new StringBuilder("Digest realm=\"" + this.getRealm() + "\", nonce=\"" + this.getNonce() + "\"");
+ if (this.getAlgorithm() != null) {
+ sb.append(", algorithm=" + this.getAlgorithm());
+ }
+ if (this.getQop() != null) {
+ sb.append(", qop=\"" + this.getQop() + "\"");
+ }
+ value = Buffers.wrap(sb.toString());
+ return value;
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder(NAME.toString());
+ sb.append(": Digest realm=\"" + this.getRealm() + "\", nonce=\"" + this.getNonce() + "\"");
+ if (this.getAlgorithm() != null) {
+ sb.append(", algorithm=" + this.getAlgorithm());
+ }
+ if (this.getQop() != null) {
+ sb.append(", qop=\"" + this.getQop() + "\"");
+ }
+ return sb.toString();
+ }
+
+
+ @Override
+ public WWWAuthenticateHeader.Builder copy() {
+ return new WWWAuthenticateHeader.Builder(getValue());
+ }
+
+ @Override
+ public WWWAuthenticateHeader ensure() {
+ return this;
+ }
+
+ @Override
+ public WWWAuthenticateHeader clone() {
+ final Buffer value = getValue();
+ return new WWWAuthenticateHeaderImpl(value.clone());
+ }
+
+ @Override
+ public Buffer getRealm() {
+ if (realm != null) {
+ return realm;
+ }
+ realm = paramMap.get(Buffers.wrap("realm"));
+ return realm;
+ }
+
+ @Override
+ public Buffer getNonce() {
+ if (nonce != null) {
+ return nonce;
+ }
+ nonce = paramMap.get(Buffers.wrap("nonce"));
+ return nonce;
+ }
+
+ @Override
+ public Buffer getAlgorithm() {
+ if (algorithm != null) {
+ return algorithm;
+ }
+ algorithm = paramMap.get(Buffers.wrap("algorithm"));
+ return algorithm;
+ }
+
+ @Override
+ public Buffer getQop() {
+ if (qop != null) {
+ return qop;
+ }
+ qop = paramMap.get(Buffers.wrap("qop"));
+ return qop;
+ }
+}
diff --git a/pkts-sip/src/main/java/io/pkts/packet/sip/impl/ImmutableSipMessage.java b/pkts-sip/src/main/java/io/pkts/packet/sip/impl/ImmutableSipMessage.java
index fb0ee581..04fc7a60 100644
--- a/pkts-sip/src/main/java/io/pkts/packet/sip/impl/ImmutableSipMessage.java
+++ b/pkts-sip/src/main/java/io/pkts/packet/sip/impl/ImmutableSipMessage.java
@@ -3,19 +3,7 @@
import io.pkts.buffer.Buffer;
import io.pkts.packet.sip.SipMessage;
import io.pkts.packet.sip.SipParseException;
-import io.pkts.packet.sip.header.CSeqHeader;
-import io.pkts.packet.sip.header.CallIdHeader;
-import io.pkts.packet.sip.header.ContactHeader;
-import io.pkts.packet.sip.header.ContentLengthHeader;
-import io.pkts.packet.sip.header.ContentTypeHeader;
-import io.pkts.packet.sip.header.ExpiresHeader;
-import io.pkts.packet.sip.header.FromHeader;
-import io.pkts.packet.sip.header.MaxForwardsHeader;
-import io.pkts.packet.sip.header.RecordRouteHeader;
-import io.pkts.packet.sip.header.RouteHeader;
-import io.pkts.packet.sip.header.SipHeader;
-import io.pkts.packet.sip.header.ToHeader;
-import io.pkts.packet.sip.header.ViaHeader;
+import io.pkts.packet.sip.header.*;
import java.util.ArrayList;
import java.util.Collections;
@@ -239,6 +227,12 @@ public ExpiresHeader getExpiresHeader() throws SipParseException {
return header != null ? header.ensure().toExpiresHeader() : null;
}
+ @Override
+ public WWWAuthenticateHeader getWWWAuthenticateHeader() throws SipParseException{
+ final SipHeader header = findHeader(WWWAuthenticateHeader.NAME.toString());
+ return header != null ? header.ensure().toWWWAuthenticateHeader() : null;
+ }
+
@Override
public ContactHeader getContactHeader() throws SipParseException {
return contactHeader != null ? contactHeader.ensure().toContactHeader() : null;
diff --git a/pkts-sip/src/main/java/io/pkts/packet/sip/impl/SipMessageBuilder.java b/pkts-sip/src/main/java/io/pkts/packet/sip/impl/SipMessageBuilder.java
index 8ca005bd..c58be627 100644
--- a/pkts-sip/src/main/java/io/pkts/packet/sip/impl/SipMessageBuilder.java
+++ b/pkts-sip/src/main/java/io/pkts/packet/sip/impl/SipMessageBuilder.java
@@ -18,11 +18,7 @@
import io.pkts.packet.sip.header.ToHeader;
import io.pkts.packet.sip.header.ViaHeader;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
+import java.util.*;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
import java.util.function.Function;
@@ -594,7 +590,7 @@ public T build() {
int msgSize = 2;
final int headerCount = this.headers.size() + sizeOf(viaHeaders) + sizeOf(recordRouteHeaders) + sizeOf(routeHeaders);
- final Map> finalHeaders = new HashMap<>(headerCount);
+ final Map> finalHeaders = new LinkedHashMap<>(headerCount);
SipHeader contentLengthHeader = null;
@@ -612,6 +608,19 @@ public T build() {
routeHeader = null;
recordRouteHeader = null;
contactHeader = null;
+
+ if (this.viaHeaders != null) {
+ for (int j = 0; j < this.viaHeaders.size(); ++j) {
+ final ViaHeader finalVia = processVia(j, this.viaHeaders.get(j));
+ msgSize += finalVia.getBufferSize() + 2;
+ if (viaHeader == null) {
+ viaHeader = finalVia;
+ }
+
+ finalHeaders.computeIfAbsent(finalVia.getName().toString(), k -> new ArrayList<>()).add(finalVia);
+ }
+ }
+
for (int i = 0; i < this.headers.size(); ++i) {
final SipHeader header = this.headers.get(i);
if (header != null) {
@@ -630,18 +639,6 @@ public T build() {
}
}
- if (this.viaHeaders != null) {
- for (int j = 0; j < this.viaHeaders.size(); ++j) {
- final ViaHeader finalVia = processVia(j, this.viaHeaders.get(j));
- msgSize += finalVia.getBufferSize() + 2;
- if (viaHeader == null) {
- viaHeader = finalVia;
- }
-
- finalHeaders.computeIfAbsent(finalVia.getName().toString(), k -> new ArrayList<>()).add(finalVia);
- }
- }
-
if (this.recordRouteHeaders != null) {
for (int j = 0; j < this.recordRouteHeaders.size(); ++j) {
final Consumer> f =
diff --git a/pkts-sip/src/main/java/io/pkts/packet/sip/impl/SipParser.java b/pkts-sip/src/main/java/io/pkts/packet/sip/impl/SipParser.java
index ad5b2903..334a5e00 100644
--- a/pkts-sip/src/main/java/io/pkts/packet/sip/impl/SipParser.java
+++ b/pkts-sip/src/main/java/io/pkts/packet/sip/impl/SipParser.java
@@ -7,19 +7,7 @@
import io.pkts.buffer.Buffers;
import io.pkts.packet.sip.SipMessage;
import io.pkts.packet.sip.SipParseException;
-import io.pkts.packet.sip.header.CSeqHeader;
-import io.pkts.packet.sip.header.CallIdHeader;
-import io.pkts.packet.sip.header.ContactHeader;
-import io.pkts.packet.sip.header.ContentLengthHeader;
-import io.pkts.packet.sip.header.ContentTypeHeader;
-import io.pkts.packet.sip.header.ExpiresHeader;
-import io.pkts.packet.sip.header.FromHeader;
-import io.pkts.packet.sip.header.MaxForwardsHeader;
-import io.pkts.packet.sip.header.RecordRouteHeader;
-import io.pkts.packet.sip.header.RouteHeader;
-import io.pkts.packet.sip.header.SipHeader;
-import io.pkts.packet.sip.header.ToHeader;
-import io.pkts.packet.sip.header.ViaHeader;
+import io.pkts.packet.sip.header.*;
import io.pkts.packet.sip.header.impl.SipHeaderImpl;
import java.io.IOException;
@@ -32,9 +20,6 @@
import java.util.function.Function;
-
-
-
/**
* Basic sip parser that contains most (all?) of the different grammar rules for SIP as defined by
* RFC 3261. View these various methods as building blocks for building up a complete SIP parser
@@ -240,6 +225,9 @@ public class SipParser {
framers.put(ViaHeader.NAME, header -> ViaHeader.frame(header.getValue()));
framers.put(ViaHeader.COMPACT_NAME, header -> ViaHeader.frame(header.getValue()));
+
+ //新增WWWAuthenticateHeader注册
+ framers.put(WWWAuthenticateHeader.NAME, header -> WWWAuthenticateHeader.frame(header.getValue()));
}
/**
@@ -342,7 +330,7 @@ public static boolean isWSS(final Buffer t) {
public static boolean isSCTP(final Buffer t) {
try {
return t.capacity() == 4 && t.getByte(0) == 'S' && t.getByte(1) == 'C' && t.getByte(2) == 'T'
- && t.getByte(3) == 'P';
+ && t.getByte(3) == 'P';
} catch (final IOException e) {
return false;
}
@@ -401,7 +389,7 @@ public static boolean isWSSLower(final Buffer t) {
public static boolean isSCTPLower(final Buffer t) {
try {
return t.capacity() == 4 && t.getByte(0) == 's' && t.getByte(1) == 'c' && t.getByte(2) == 't'
- && t.getByte(3) == 'p';
+ && t.getByte(3) == 'p';
} catch (final IOException e) {
return false;
}
@@ -425,7 +413,7 @@ public static boolean isSips(final Buffer buffer) throws SipParseException, Inde
if (b != 's') {
throw new SipParseException(buffer.getReaderIndex() - 1,
- "Expected 's' since the only schemes accepted are \"sip\" and \"sips\"");
+ "Expected 's' since the only schemes accepted are \"sip\" and \"sips\"");
}
SipParser.expect(buffer, SipParser.COLON);
@@ -457,7 +445,7 @@ public static boolean isSips(final Buffer buffer) throws SipParseException, Inde
* @throws IndexOutOfBoundsException
*/
public static List consumeGenericParams(final Buffer buffer) throws IndexOutOfBoundsException,
- IOException {
+ IOException {
final List params = new ArrayList();
while (buffer.hasReadableBytes() && buffer.peekByte() == SEMI) {
buffer.readByte(); // consume the SEMI
@@ -511,7 +499,7 @@ public static Buffer[] consumeGenericParam(final Buffer buffer) throws IndexOutO
}
}
return new Buffer[]{
- key, value};
+ key, value};
}
/**
@@ -529,6 +517,28 @@ public static boolean isNext(final Buffer buffer, final byte b) throws IOExcepti
return buffer.hasReadableBytes() && buffer.peekByte() == b;
}
+ public static boolean isNext(final Buffer buffer, final byte[] bytes) throws IOException {
+ boolean hasReadableBytes = buffer.hasReadableBytes();
+ if (!hasReadableBytes) {
+ return false;
+ }
+ int readableBytes = buffer.getReadableBytes();
+ int length = bytes.length;
+ if (readableBytes < length) {
+ return false;
+ }
+ boolean match = true;
+ for (int i = 0; i < length; i++) {
+ int readIndex = buffer.getReaderIndex() + i;
+ byte aByte = buffer.getByte(readIndex);
+ if (aByte != bytes[i]) {
+ match = false;
+ break;
+ }
+ }
+ return match;
+ }
+
/**
* Check whether the next byte is a digit or not
*
@@ -692,7 +702,7 @@ public static void expectSLASH(final Buffer buffer) throws SipParseException {
* @throws IndexOutOfBoundsException
*/
public static int consumeSLASH(final Buffer buffer) throws SipParseException, IndexOutOfBoundsException,
- IOException {
+ IOException {
return consumeSeparator(buffer, SLASH);
}
@@ -715,7 +725,7 @@ public static int expectWS(final Buffer buffer) throws SipParseException {
}
} else {
throw new SipParseException(buffer.getReaderIndex(),
- "Expected WS but nothing more to read in the buffer");
+ "Expected WS but nothing more to read in the buffer");
}
} catch (final IOException e) {
throw new SipParseException(buffer.getReaderIndex(), UNABLE_TO_READ_FROM_STREAM, e);
@@ -752,7 +762,7 @@ public static int consumeSWS(final Buffer buffer) {
* @throws IndexOutOfBoundsException
*/
public static int consumeSTAR(final Buffer buffer) throws SipParseException, IndexOutOfBoundsException,
- IOException {
+ IOException {
return consumeSeparator(buffer, STAR);
}
@@ -843,7 +853,7 @@ public static int consumeLAQUOT(final Buffer buffer) throws IndexOutOfBoundsExce
* @throws IndexOutOfBoundsException
*/
public static int consumeCOMMA(final Buffer buffer) throws SipParseException, IndexOutOfBoundsException,
- IOException {
+ IOException {
return consumeSeparator(buffer, COMMA);
}
@@ -859,7 +869,7 @@ public static int consumeCOMMA(final Buffer buffer) throws SipParseException, In
* @throws IndexOutOfBoundsException
*/
public static int consumeSEMI(final Buffer buffer) throws SipParseException, IndexOutOfBoundsException,
- IOException {
+ IOException {
return consumeSeparator(buffer, SEMI);
}
@@ -875,7 +885,7 @@ public static int consumeSEMI(final Buffer buffer) throws SipParseException, Ind
* @throws IndexOutOfBoundsException
*/
public static int consumeCOLON(final Buffer buffer) throws SipParseException, IndexOutOfBoundsException,
- IOException {
+ IOException {
return consumeSeparator(buffer, COLON);
}
@@ -891,7 +901,7 @@ public static int consumeCOLON(final Buffer buffer) throws SipParseException, In
* @throws IndexOutOfBoundsException
*/
public static int consumeLDQUOT(final Buffer buffer) throws SipParseException, IndexOutOfBoundsException,
- IOException {
+ IOException {
buffer.markReaderIndex();
int consumed = consumeSWS(buffer);
if (isNext(buffer, DQUOT)) {
@@ -916,7 +926,7 @@ public static int consumeLDQUOT(final Buffer buffer) throws SipParseException, I
* @throws IndexOutOfBoundsException
*/
public static int consumeRDQUOT(final Buffer buffer) throws SipParseException, IndexOutOfBoundsException,
- IOException {
+ IOException {
buffer.markReaderIndex();
int consumed = 0;
if (isNext(buffer, DQUOT)) {
@@ -956,7 +966,7 @@ public static int consumeRDQUOT(final Buffer buffer) throws SipParseException, I
* @throws IndexOutOfBoundsException
*/
private static int consumeSeparator(final Buffer buffer, final byte b) throws IndexOutOfBoundsException,
- IOException {
+ IOException {
buffer.markReaderIndex();
int consumed = consumeSWS(buffer);
if (isNext(buffer, b)) {
@@ -984,7 +994,7 @@ private static int consumeSeparator(final Buffer buffer, final byte b) throws In
* in case there is no token
*/
public static Buffer expectToken(final Buffer buffer) throws IndexOutOfBoundsException, IOException,
- SipParseException {
+ SipParseException {
final Buffer token = consumeToken(buffer);
if (token == null) {
throw new SipParseException(buffer.getReaderIndex(), "Expected TOKEN");
@@ -1094,7 +1104,7 @@ public static Buffer consumeDisplayName(final boolean quoted, final Buffer buffe
}
public static Buffer consumeAddressSpec(final Buffer buffer) throws IndexOutOfBoundsException, IOException,
- SipParseException {
+ SipParseException {
return consumeAddressSpec(false, buffer);
}
@@ -1135,7 +1145,7 @@ public static Buffer consumeAddressSpec(final Buffer buffer) throws IndexOutOfBo
* @throws SipParseException in case we cannot successfully frame the addr-spec.
*/
public static Buffer consumeAddressSpec(final boolean isProtected, final Buffer buffer) throws IndexOutOfBoundsException, IOException,
- SipParseException {
+ SipParseException {
final int startIndex = buffer.getReaderIndex();
int count = 0;
int state = 0; // zero is to look for colon, everything else is to find the end
@@ -1150,7 +1160,7 @@ public static Buffer consumeAddressSpec(final boolean isProtected, final Buffer
throw new SipParseException(buffer.getReaderIndex(), "No scheme found after 100 bytes, giving up");
} else if (count > MAX_LOOK_AHEAD) {
throw new SipParseException(buffer.getReaderIndex(),
- "Have not been able to find the entire addr-spec after " + count + " bytes, giving up");
+ "Have not been able to find the entire addr-spec after " + count + " bytes, giving up");
} else if (state == 0 && b == COLON) {
state = 1;
} else if (state == 1 && (b == RAQUOT || b == CR || b == LF)) {
@@ -1339,7 +1349,7 @@ public static Object[] consumeVia(final Buffer buffer) throws SipParseException,
consumeSWS(buffer);
result[0] = consumeSentProtocol(buffer);
- if(consumeLWS(buffer) == 0){
+ if (consumeLWS(buffer) == 0) {
throw new SipParseException(buffer.getReaderIndex(), "Expected at least 1 WSP");
}
@@ -1361,8 +1371,8 @@ public static Object[] consumeVia(final Buffer buffer) throws SipParseException,
if (count == MAX_LOOK_AHEAD) {
throw new SipParseException(buffer.getReaderIndex(),
- "Unable to find the parameters part of the Via-header "
- + "even after searching for " + MAX_LOOK_AHEAD + " bytes.");
+ "Unable to find the parameters part of the Via-header "
+ + "even after searching for " + MAX_LOOK_AHEAD + " bytes.");
}
if (indexOfSemi == 0) {
@@ -1370,7 +1380,7 @@ public static Object[] consumeVia(final Buffer buffer) throws SipParseException,
// if there are no parameters present at all then there
// is no chance of it being present.
throw new SipParseException(buffer.getReaderIndex(),
- "No via-parameters found. The Via-header MUST contain at least the branch parameter.");
+ "No via-parameters found. The Via-header MUST contain at least the branch parameter.");
}
buffer.setReaderIndex(index);
@@ -1392,7 +1402,7 @@ public static Object[] consumeVia(final Buffer buffer) throws SipParseException,
// indication an strange number of colons. May be the strange
// ipv4 address after ipv6 thing which we currently dont handle
throw new SipParseException(indexOfLastColon, "Found " + countOfColons + " which seems odd."
- + " Expecting 0, 1, 7 or 8 colons in the Via-host:port portion. Please check your traffic");
+ + " Expecting 0, 1, 7 or 8 colons in the Via-host:port portion. Please check your traffic");
}
final List params = consumeGenericParams(buffer);
@@ -1475,7 +1485,7 @@ public static Buffer[] consumeSentBye(final Buffer buffer) throws SipParseExcept
port = consumePort(buffer);
}
return new Buffer[]{
- host, port};
+ host, port};
}
/**
@@ -1538,7 +1548,7 @@ public static Buffer consumeMType(final Buffer buffer) throws SipParseException
throw new SipParseException(buffer.getReaderIndex(), "Tried to consume m-type but buffer ended abruptly", e);
} catch (final IOException e) {
throw new SipParseException(buffer.getReaderIndex(),
- "Tried to consume m-type but problem reading from underlying stream", e);
+ "Tried to consume m-type but problem reading from underlying stream", e);
}
}
@@ -1569,7 +1579,7 @@ public static Buffer consumeMSubtype(final Buffer buffer) throws SipParseExcepti
throw new SipParseException(buffer.getReaderIndex(), "Tried to consume m-type but buffer ended abruptly", e);
} catch (final IOException e) {
throw new SipParseException(buffer.getReaderIndex(),
- "Tried to consume m-type but problem reading from underlying stream", e);
+ "Tried to consume m-type but problem reading from underlying stream", e);
}
}
@@ -1590,8 +1600,8 @@ public static int getTokenCount(final Buffer buffer) throws IndexOutOfBoundsExce
while (buffer.hasReadableBytes() && !done) {
final byte b = buffer.readByte();
final boolean ok = isAlphaNum(b) || b == DASH || b == PERIOD || b == EXCLAMATIONPOINT
- || b == PERCENT || b == STAR || b == UNDERSCORE || b == PLUS || b == BACKTICK
- || b == TICK || b == TILDE;
+ || b == PERCENT || b == STAR || b == UNDERSCORE || b == PLUS || b == BACKTICK
+ || b == TICK || b == TILDE;
if (ok) {
++count;
} else {
@@ -1766,7 +1776,7 @@ public static void expect(final Buffer buffer, final byte expected) throws SipPa
final String actualStr = new String(new byte[]{actual}, StandardCharsets.UTF_8);
final String expectedStr = new String(new byte[]{expected});
throw new SipParseException(buffer.getReaderIndex(), "Expected '" + expected + "' (" + expectedStr
- + ") got '" + actual + "' (" + actualStr + ")");
+ + ") got '" + actual + "' (" + actualStr + ")");
}
}
@@ -1912,8 +1922,9 @@ private static boolean isHeaderAllowingMultipleValues(final Buffer headerName) {
return !isAllowEventsHeaderShort(headerName);
} else if (size == 12) {
return !isAllowEventsHeader(headerName);
+ } else if (size == 16) {
+ return !isWWWAuthenticateHeader(headerName);
}
-
return true;
}
@@ -1926,7 +1937,7 @@ private static boolean isHeaderAllowingMultipleValues(final Buffer headerName) {
private static boolean isDateHeader(final Buffer name) {
try {
return name.getByte(0) == 'D' && name.getByte(1) == 'a' &&
- name.getByte(2) == 't' && name.getByte(3) == 'e';
+ name.getByte(2) == 't' && name.getByte(3) == 'e';
} catch (final IOException e) {
return false;
}
@@ -1935,8 +1946,8 @@ private static boolean isDateHeader(final Buffer name) {
private static boolean isAllowHeader(final Buffer name) {
try {
return name.getByte(0) == 'A' && name.getByte(1) == 'l' &&
- name.getByte(2) == 'l' && name.getByte(3) == 'o' &&
- name.getByte(4) == 'w';
+ name.getByte(2) == 'l' && name.getByte(3) == 'o' &&
+ name.getByte(4) == 'w';
} catch (final IOException e) {
return false;
}
@@ -1953,11 +1964,34 @@ private static boolean isAllowEventsHeaderShort(final Buffer name) {
private static boolean isAllowEventsHeader(final Buffer name) {
try {
return name.getByte(0) == 'A' && name.getByte(1) == 'l' &&
- name.getByte(2) == 'l' && name.getByte(3) == 'o' &&
- name.getByte(4) == 'w' && name.getByte(5) == '-' &&
- name.getByte(6) == 'E' && name.getByte(7) == 'v' &&
- name.getByte(8) == 'e' && name.getByte(9) == 'n' &&
- name.getByte(10) == 't' && name.getByte(11) == 's';
+ name.getByte(2) == 'l' && name.getByte(3) == 'o' &&
+ name.getByte(4) == 'w' && name.getByte(5) == '-' &&
+ name.getByte(6) == 'E' && name.getByte(7) == 'v' &&
+ name.getByte(8) == 'e' && name.getByte(9) == 'n' &&
+ name.getByte(10) == 't' && name.getByte(11) == 's';
+ } catch (final IOException e) {
+ return false;
+ }
+ }
+
+ private static boolean isWWWAuthenticateHeader(final Buffer name) {
+ try {
+ return (name.getByte(0) == 'W' || name.getByte(0) == 'w') &&
+ (name.getByte(1) == 'W' || name.getByte(1) == 'w') &&
+ (name.getByte(2) == 'W' || name.getByte(2) == 'w') &&
+ name.getByte(3) == '-' &&
+ (name.getByte(4) == 'A' || name.getByte(4) == 'a') &&
+ (name.getByte(5) == 'U' || name.getByte(5) == 'u') &&
+ (name.getByte(6) == 'T' || name.getByte(6) == 't') &&
+ (name.getByte(7) == 'H' || name.getByte(7) == 'h') &&
+ (name.getByte(8) == 'E' || name.getByte(8) == 'e') &&
+ (name.getByte(9) == 'N' || name.getByte(9) == 'n') &&
+ (name.getByte(10) == 'T' || name.getByte(10) == 't') &&
+ (name.getByte(11) == 'I' || name.getByte(11) == 'i') &&
+ (name.getByte(12) == 'C' || name.getByte(12) == 'c') &&
+ (name.getByte(13) == 'A' || name.getByte(13) == 'a') &&
+ (name.getByte(14) == 'T' || name.getByte(14) == 't') &&
+ (name.getByte(15) == 'E' || name.getByte(15) == 'e');
} catch (final IOException e) {
return false;
}
@@ -1966,9 +2000,9 @@ private static boolean isAllowEventsHeader(final Buffer name) {
private static boolean isSubjectHeader(final Buffer name) {
try {
return name.getByte(0) == 'S' && name.getByte(1) == 'u' &&
- name.getByte(2) == 'b' && name.getByte(3) == 'j' &&
- name.getByte(4) == 'e' && name.getByte(5) == 'c' &&
- name.getByte(6) == 't';
+ name.getByte(2) == 'b' && name.getByte(3) == 'j' &&
+ name.getByte(4) == 'e' && name.getByte(5) == 'c' &&
+ name.getByte(6) == 't';
} catch (final IOException e) {
return false;
}
@@ -2166,7 +2200,7 @@ public static SipMessage frame(final Buffer buffer) throws IOException {
if (!couldBeSipMessage(buffer)) {
throw new SipParseException(0, "Cannot be a SIP message because is doesnt start with \"SIP\" "
- + "(for responses) or a method (for requests)");
+ + "(for responses) or a method (for requests)");
}
final int startIndex = buffer.getReaderIndex();
@@ -2185,6 +2219,7 @@ public static SipMessage frame(final Buffer buffer) throws IOException {
SipHeader routeHeader = null;
SipHeader recordRouteHeader = null;
SipHeader contactHeader = null;
+ SipHeader wwwAuthenticateHeader = null;
final Map> headers = new HashMap<>();
int contentLength = 0;
@@ -2205,7 +2240,7 @@ public static SipMessage frame(final Buffer buffer) throws IOException {
} else if (cSeqHeader == null && header.isCSeqHeader()) {
header = header.ensure();
cSeqHeader = header;
- } else if ( maxForwardsHeader == null && header.isMaxForwardsHeader()) {
+ } else if (maxForwardsHeader == null && header.isMaxForwardsHeader()) {
header = header.ensure();
maxForwardsHeader = header;
} else if (fromHeader == null && header.isFromHeader()) {
@@ -2226,8 +2261,12 @@ public static SipMessage frame(final Buffer buffer) throws IOException {
} else if (recordRouteHeader == null && header.isRecordRouteHeader()) {
header = header.ensure();
recordRouteHeader = header;
+ } else if (wwwAuthenticateHeader == null && header.isWWWAuthenticateHeader()) {
+ header = header.ensure();
+ wwwAuthenticateHeader = header;
}
+
headers.computeIfAbsent(headerName.toString(), k -> new ArrayList<>(4)).add(header);
}
}
@@ -2248,32 +2287,32 @@ public static SipMessage frame(final Buffer buffer) throws IOException {
if (initialLine.isRequestLine()) {
return new ImmutableSipRequest(msg,
- initialLine.toRequestLine(),
- headers,
- toHeader,
- fromHeader,
- cSeqHeader,
- callIdHeader,
- maxForwardsHeader,
- viaHeader,
- routeHeader,
- recordRouteHeader,
- contactHeader,
- payload);
+ initialLine.toRequestLine(),
+ headers,
+ toHeader,
+ fromHeader,
+ cSeqHeader,
+ callIdHeader,
+ maxForwardsHeader,
+ viaHeader,
+ routeHeader,
+ recordRouteHeader,
+ contactHeader,
+ payload);
} else {
return new ImmutableSipResponse(msg,
- initialLine.toResponseLine(),
- headers,
- toHeader,
- fromHeader,
- cSeqHeader,
- callIdHeader,
- maxForwardsHeader,
- viaHeader,
- routeHeader,
- recordRouteHeader,
- contactHeader,
- payload);
+ initialLine.toResponseLine(),
+ headers,
+ toHeader,
+ fromHeader,
+ cSeqHeader,
+ callIdHeader,
+ maxForwardsHeader,
+ viaHeader,
+ routeHeader,
+ recordRouteHeader,
+ contactHeader,
+ payload);
}
}
@@ -2293,20 +2332,20 @@ public static boolean couldBeSipMessage(final Buffer data) throws IOException {
public static boolean couldBeSipMessage(final byte a, final byte b, final byte c) throws IOException {
return a == 'S' && b == 'I' && c == 'P' || // response
- a == 'I' && b == 'N' && c == 'V' || // INVITE
- a == 'A' && b == 'C' && c == 'K' || // ACK
- a == 'B' && b == 'Y' && c == 'E' || // BYE
- a == 'O' && b == 'P' && c == 'T' || // OPTIONS
- a == 'C' && b == 'A' && c == 'N' || // CANCEL
- a == 'M' && b == 'E' && c == 'S' || // MESSAGE
- a == 'R' && b == 'E' && c == 'G' || // REGISTER
- a == 'I' && b == 'N' && c == 'F' || // INFO
- a == 'P' && b == 'R' && c == 'A' || // PRACK
- a == 'S' && b == 'U' && c == 'B' || // SUBSCRIBE
- a == 'N' && b == 'O' && c == 'T' || // NOTIFY
- a == 'U' && b == 'P' && c == 'D' || // UPDATE
- a == 'R' && b == 'E' && c == 'F' || // REFER
- a == 'P' && b == 'U' && c == 'B'; // PUBLISH
+ a == 'I' && b == 'N' && c == 'V' || // INVITE
+ a == 'A' && b == 'C' && c == 'K' || // ACK
+ a == 'B' && b == 'Y' && c == 'E' || // BYE
+ a == 'O' && b == 'P' && c == 'T' || // OPTIONS
+ a == 'C' && b == 'A' && c == 'N' || // CANCEL
+ a == 'M' && b == 'E' && c == 'S' || // MESSAGE
+ a == 'R' && b == 'E' && c == 'G' || // REGISTER
+ a == 'I' && b == 'N' && c == 'F' || // INFO
+ a == 'P' && b == 'R' && c == 'A' || // PRACK
+ a == 'S' && b == 'U' && c == 'B' || // SUBSCRIBE
+ a == 'N' && b == 'O' && c == 'T' || // NOTIFY
+ a == 'U' && b == 'P' && c == 'D' || // UPDATE
+ a == 'R' && b == 'E' && c == 'F' || // REFER
+ a == 'P' && b == 'U' && c == 'B'; // PUBLISH
}
diff --git a/pkts-sip/src/test/java/io/pkts/packet/sip/SipRequestTest.java b/pkts-sip/src/test/java/io/pkts/packet/sip/SipRequestTest.java
index 3478e956..9fc5338b 100644
--- a/pkts-sip/src/test/java/io/pkts/packet/sip/SipRequestTest.java
+++ b/pkts-sip/src/test/java/io/pkts/packet/sip/SipRequestTest.java
@@ -1,5 +1,5 @@
/**
- *
+ *
*/
package io.pkts.packet.sip;
@@ -8,20 +8,17 @@
import io.pkts.buffer.Buffer;
import io.pkts.buffer.Buffers;
import io.pkts.packet.sip.address.SipURI;
-import io.pkts.packet.sip.header.CSeqHeader;
-import io.pkts.packet.sip.header.CallIdHeader;
-import io.pkts.packet.sip.header.ContactHeader;
-import io.pkts.packet.sip.header.FromHeader;
-import io.pkts.packet.sip.header.MaxForwardsHeader;
-import io.pkts.packet.sip.header.RecordRouteHeader;
-import io.pkts.packet.sip.header.RouteHeader;
-import io.pkts.packet.sip.header.SipHeader;
-import io.pkts.packet.sip.header.ViaHeader;
+import io.pkts.packet.sip.header.*;
+import io.pkts.packet.sip.impl.SipParser;
import org.junit.Before;
import org.junit.Test;
+import java.io.IOException;
import java.util.List;
+import java.util.Optional;
import java.util.function.Consumer;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.CoreMatchers.not;
@@ -35,6 +32,131 @@ public class SipRequestTest extends PktsTestBase {
private FromHeader from;
+
+ @Test
+ public void testBuildRegisterResponse() throws IOException {
+ StringBuilder register = new StringBuilder("REGISTER sip:10.32.26.25:5070;transport=tcp SIP/2.0\r\n" +
+ "Via: SIP/2.0/TCP 10.32.26.25:51696;rport;branch=z9hG4bKPj8d4db68b24754f539dbf3b563a44fe55;alias\r\n" +
+ "Max-Forwards: 70\r\n" +
+ "From: ;tag=89aefb1f3fc0413283a453eda5407f60\r\n" +
+ "To: jimmy\r\n" +
+ "Call-ID: 1e7af0e67a5044658fc7f6716d329642\r\n" +
+ "CSeq: 36850 REGISTER\r\n" +
+ "User-Agent: MicroSIP/3.20.3\r\n" +
+ "Supported: outbound, path\r\n" +
+ "Contact: ;reg-id=1;+sip.instance=\"\"\r\n" +
+ "Expires: 300\r\n" +
+ "Allow: PRACK, INVITE, ACK, BYE, CANCEL, UPDATE, INFO, SUBSCRIBE, NOTIFY, REFER, MESSAGE, OPTIONS\r\n" +
+ "Content-Length: 0\r\n");
+ SipMessage msgMessage = SipParser.frame(Buffers.wrap(register.toString()));
+
+ SipResponse sipResponse = msgMessage.createResponse(401)
+ .withHeader(SipHeader.create("User-Agent", "FreeSWITCH-mod_sofia/1.6.18+git~20170612T211449Z~6e79667c0a~64bit"))
+ .withHeader(SipHeader.create("Allow", "INVITE, ACK, BYE, CANCEL, OPTIONS, MESSAGE, INFO, UPDATE, REGISTER, REFER, NOTIFY, PUBLISH, SUBSCRIBE"))
+ .withHeader(SipHeader.create("Supported", "timer, path, replaces"))
+ .withHeader(SipHeader.create("WWW-Authenticate", "Digest realm=\"10.32.26.25\", nonce=\"bee3366b-cf59-476e-bc5e-334e0d65b386\", algorithm=MD5, qop=\"auth\""))
+ .withHeader(new ContentLengthHeader.Builder(0).build())
+ .build();
+
+ System.out.println(sipResponse);
+
+ }
+
+ @Test
+ public void testParseRegisterResponse() throws IOException {
+ StringBuilder register = new StringBuilder("SIP/2.0 401 Unauthorized\r\n" +
+ "Via: SIP/2.0/TCP 10.32.26.25:51696;rport=51696;branch=z9hG4bKPj8d4db68b24754f539dbf3b563a44fe55;alias\r\n" +
+ "From: ;tag=89aefb1f3fc0413283a453eda5407f60\r\n" +
+ "To: ;tag=Q0m1g96BS3vpa\r\n" +
+ "Call-ID: 1e7af0e67a5044658fc7f6716d329642\r\n" +
+ "CSeq: 36850 REGISTER\r\n" +
+ "User-Agent: FreeSWITCH-mod_sofia/1.6.18+git~20170612T211449Z~6e79667c0a~64bit\r\n" +
+ "Allow: INVITE, ACK, BYE, CANCEL, OPTIONS, MESSAGE, INFO, UPDATE, REGISTER, REFER, NOTIFY, PUBLISH, SUBSCRIBE\r\n" +
+ "Supported: timer, path, replaces\r\n" +
+ "WWW-Authenticate: Digest realm=\"10.32.26.25\", nonce=\"bee3366b-cf59-476e-bc5e-334e0d65b386\", algorithm=MD5, qop=\"auth\"\r\n" +
+ "Content-Length: 0");
+
+ SipMessage msgMessage = SipParser.frame(Buffers.wrap(register.toString()));
+
+ WWWAuthenticateHeader wwwAuthenticateHeader = msgMessage.getWWWAuthenticateHeader();
+
+ List wwwAuthenticateList = msgMessage.getAllHeaders()
+ .stream()
+ .filter(c -> c.getName().equalsIgnoreCase(Buffers.wrap("WWW-Authenticate")))
+ .collect(Collectors.toList());
+ System.out.println(wwwAuthenticateList);
+ }
+
+
+
+ @Test
+ public void testParseRegister() throws IOException {
+ StringBuilder register = new StringBuilder("REGISTER sip:10.32.26.25:5070;transport=tcp SIP/2.0\r\n" +
+ "Via: SIP/2.0/TCP 10.32.26.25:51696;rport;branch=z9hG4bKPj8d4db68b24754f539dbf3b563a44fe55;alias\r\n" +
+ "Max-Forwards: 70\r\n" +
+ "From: ;tag=89aefb1f3fc0413283a453eda5407f60\r\n" +
+ "To: jimmy\r\n" +
+ "Call-ID: 1e7af0e67a5044658fc7f6716d329642\r\n" +
+ "CSeq: 36850 REGISTER\r\n" +
+ "User-Agent: MicroSIP/3.20.3\r\n" +
+ "Supported: outbound, path\r\n" +
+ "Contact: ;reg-id=1;+sip.instance=\"\"\r\n" +
+ "Expires: 300\r\n" +
+ "Allow: PRACK, INVITE, ACK, BYE, CANCEL, UPDATE, INFO, SUBSCRIBE, NOTIFY, REFER, MESSAGE, OPTIONS\r\n" +
+ "Content-Length: 0\r\n");
+
+ SipMessage msgMessage = SipParser.frame(Buffers.wrap(register.toString()));
+
+ if (msgMessage.isRegisterRequest()) {
+ System.out.println("This is a REGISTER request");
+ }
+
+ Buffer method = msgMessage.getMethod();
+ System.out.println("方法:" + method + "\n");
+ Buffer initialLine = msgMessage.getInitialLine();
+ System.out.println("第一行:" + initialLine + "\n");
+
+ List viaHeaders = msgMessage.getViaHeaders();
+ System.out.println("via:");
+ for (ViaHeader viaHeader : viaHeaders) {
+ System.out.println("host:" + viaHeader.getHost() + ",branch:" + viaHeader.getBranch() + ",alias:" + viaHeader.getParameter("alias"));
+ }
+
+ MaxForwardsHeader maxForwards = msgMessage.getMaxForwards();
+ System.out.println("\nmaxForwards:" + maxForwards.getMaxForwards());
+
+ FromHeader fromHeader = msgMessage.getFromHeader();
+ System.out.println("\nfrom-tag:" + fromHeader.getTag());
+
+ ToHeader toHeader = msgMessage.getToHeader();
+ System.out.println("\nto:" + toHeader.getAddress().getDisplayName());
+
+ CallIdHeader callIDHeader = msgMessage.getCallIDHeader();
+ System.out.println("\ncallId:" + callIDHeader.getCallId());
+
+ CSeqHeader cSeqHeader = msgMessage.getCSeqHeader();
+ System.out.println("\ncSeq:" + cSeqHeader.getSeqNumber());
+
+ Optional userAgentHeader = msgMessage.getHeader("User-Agent");
+ System.out.println("\nuserAgent value:" + userAgentHeader.get().getValue());
+
+ Optional supported = msgMessage.getHeader("Supported");
+ System.out.println("\nsupported name:" + supported.get().getName());
+
+ ContactHeader contactHeader = msgMessage.getContactHeader();
+ System.out.println("\ncontact reg-id:" + contactHeader.getParameter("reg-id"));
+
+ ExpiresHeader expiresHeader = msgMessage.getExpiresHeader();
+ System.out.println("\nexpires:" + expiresHeader.getExpires());
+
+ Optional allowHeader = msgMessage.getHeader("Allow");
+ System.out.println("\nallow:" + allowHeader.get().getValue());
+
+ int contentLength = msgMessage.getContentLength();
+ System.out.println("\ncontentLength:" + contentLength);
+
+ }
+
@Override
@Before
public void setUp() throws Exception {
@@ -124,7 +246,7 @@ private static void assertFromHeaderTag(final String from, final String expected
*
* @throws Exception
*/
- @Test(expected = SipParseException.class )
+ @Test(expected = SipParseException.class)
public void testBlowUpOnNoFunctionToHandleEmptyTopMostVia() throws Exception {
parseMessage(RawData.sipInviteOneRecordRouteHeader)
.copy()
@@ -140,7 +262,7 @@ public void testBlowUpOnNoFunctionToHandleEmptyTopMostVia() throws Exception {
*
* @throws Exception
*/
- @Test(expected = SipParseException.class )
+ @Test(expected = SipParseException.class)
public void testBlowUpOnNoFunctionToHandleEmptyVia() throws Exception {
parseMessage(RawData.sipInviteOneRecordRouteHeader)
.copy()
@@ -175,8 +297,8 @@ public void testCreateB2BUARequest() throws Exception {
// Ensure that the from-tags now are different and that the two are actually
// set in the first place.
- assertThat(req.getFromHeader().getTag(), not((Buffer)null));
- assertThat(b2bua.getFromHeader().getTag(), not((Buffer)null));
+ assertThat(req.getFromHeader().getTag(), not((Buffer) null));
+ assertThat(b2bua.getFromHeader().getTag(), not((Buffer) null));
assertThat(req.getFromHeader().getTag(), not(b2bua.getFromHeader().getTag()));
// ensure the Vias are correct. We pushed one, which should be at the top
@@ -193,12 +315,12 @@ public void testCreateB2BUARequest() throws Exception {
// make sure the body is left intact as well.
final String expectedContent =
"v=0\r\n"
- + "o=user1 53655765 2353687637 IN IP4 192.168.8.110\r\n"
- + "s=-\r\n"
- + "c=IN IP4 192.168.8.110\r\n"
- + "t=0 0\r\n"
- + "m=audio 6000 RTP/AVP 0\r\n"
- + "a=rtpmap:0 PCMU/8000\r\n";
+ + "o=user1 53655765 2353687637 IN IP4 192.168.8.110\r\n"
+ + "s=-\r\n"
+ + "c=IN IP4 192.168.8.110\r\n"
+ + "t=0 0\r\n"
+ + "m=audio 6000 RTP/AVP 0\r\n"
+ + "a=rtpmap:0 PCMU/8000\r\n";
assertThat(b2bua.getContent().toString(), is(expectedContent));
@@ -347,7 +469,7 @@ public void testCreateResponse() throws Exception {
/**
* Test to create a new INVITE request and check all the headers that are supposed to be created
* by default when not specified indeed are created with the correct values.
- *
+ *
* @throws Exception
*/
@Test
@@ -371,7 +493,7 @@ public void testCreateInvite() throws Exception {
/**
* Although not mandatory from the builder's perspective, having a request without a
* {@link ContactHeader} is pretty much useless so make sure that we can add that as well.
- *
+ *
* @throws Exception
*/
@Test
@@ -395,7 +517,7 @@ public void testCreateInviteWithViaHeaders() throws Exception {
assertThat(invite.getViaHeaders().size(), is(1));
assertThat(
invite.getViaHeaders().get(0).toString()
- .startsWith("Via: SIP/2.0/UDP 127.0.0.1:9898;branch=z9hG4bK"), is(true));
+ .startsWith("Via: SIP/2.0/UDP 127.0.0.1:9898;branch=z9hG4bK"), is(true));
assertThat(invite.getViaHeader().toString().startsWith("Via: SIP/2.0/UDP 127.0.0.1:9898;branch=z9hG4bK"),
is(true));
diff --git a/pkts-sip/src/test/java/io/pkts/packet/sip/header/impl/WWWAuthenticateHeaderImplTest.java b/pkts-sip/src/test/java/io/pkts/packet/sip/header/impl/WWWAuthenticateHeaderImplTest.java
new file mode 100644
index 00000000..4ef1e9a9
--- /dev/null
+++ b/pkts-sip/src/test/java/io/pkts/packet/sip/header/impl/WWWAuthenticateHeaderImplTest.java
@@ -0,0 +1,77 @@
+package io.pkts.packet.sip.header.impl;
+
+import io.pkts.buffer.Buffer;
+import io.pkts.buffer.Buffers;
+import io.pkts.packet.sip.SipParseException;
+import io.pkts.packet.sip.header.ViaHeader;
+import io.pkts.packet.sip.header.WWWAuthenticateHeader;
+import org.junit.Test;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.CoreMatchers.nullValue;
+import static org.junit.Assert.*;
+
+
+public class WWWAuthenticateHeaderImplTest {
+
+ @Test
+ public void testBuild1() throws Exception {
+ final WWWAuthenticateHeader wwwAuthenticateHeader = new WWWAuthenticateHeader.Builder()
+ .withAlgorithm(Buffers.wrap("MD5"))
+ .withNonce(Buffers.wrap("bee3366b-cf59-476e-bc5e-334e0d65b386"))
+ .withQop(Buffers.wrap("auth"))
+ .withRealm(Buffers.wrap("10.32.26.25"))
+ .build();
+
+ assertEquals(wwwAuthenticateHeader.getAlgorithm(), Buffers.wrap("MD5"));
+ assertEquals(wwwAuthenticateHeader.getNonce(), Buffers.wrap("bee3366b-cf59-476e-bc5e-334e0d65b386"));
+ assertEquals(wwwAuthenticateHeader.getQop(), Buffers.wrap("auth"));
+ assertEquals(wwwAuthenticateHeader.getRealm(), Buffers.wrap("10.32.26.25"));
+
+ Buffer value = Buffers.wrap("Digest realm=\"10.32.26.25\", nonce=\"bee3366b-cf59-476e-bc5e-334e0d65b386\", algorithm=MD5, qop=\"auth\"");
+ assertTrue(wwwAuthenticateHeader.getValue().equalsIgnoreCase(value));
+ }
+
+ @Test
+ public void testBuild2() throws Exception {
+ final WWWAuthenticateHeader wwwAuthenticateHeader = new WWWAuthenticateHeader.Builder()
+ .withNonce(Buffers.wrap("bee3366b-cf59-476e-bc5e-334e0d65b386"))
+ .withRealm(Buffers.wrap("10.32.26.25"))
+ .build();
+
+ assertEquals(wwwAuthenticateHeader.getAlgorithm(), null);
+ assertEquals(wwwAuthenticateHeader.getNonce(), Buffers.wrap("bee3366b-cf59-476e-bc5e-334e0d65b386"));
+ assertEquals(wwwAuthenticateHeader.getQop(), null);
+ assertEquals(wwwAuthenticateHeader.getRealm(), Buffers.wrap("10.32.26.25"));
+
+ Buffer value = Buffers.wrap("Digest realm=\"10.32.26.25\", nonce=\"bee3366b-cf59-476e-bc5e-334e0d65b386\"");
+ assertTrue(wwwAuthenticateHeader.getValue().equalsIgnoreCase(value));
+ }
+
+ @Test
+ public void testFrame1() throws Exception {
+ Buffer value = Buffers.wrap("Digest realm=\"10.32.26.25\", nonce=\"bee3366b-cf59-476e-bc5e-334e0d65b386\", algorithm=MD5, qop=\"auth\"");
+ final WWWAuthenticateHeader wwwAuthenticateHeader = new WWWAuthenticateHeaderImpl(value);
+ assertEquals(wwwAuthenticateHeader.getAlgorithm(), Buffers.wrap("MD5"));
+ assertEquals(wwwAuthenticateHeader.getNonce(), Buffers.wrap("bee3366b-cf59-476e-bc5e-334e0d65b386"));
+ assertEquals(wwwAuthenticateHeader.getQop(), Buffers.wrap("auth"));
+ assertEquals(wwwAuthenticateHeader.getRealm(), Buffers.wrap("10.32.26.25"));
+ }
+
+ @Test
+ public void testFrame2() throws Exception {
+ Buffer realm = Buffers.wrap("10.32.26.25");
+ Buffer nonce = Buffers.wrap("bee3366b-cf59-476e-bc5e-334e0d65b386");
+ final WWWAuthenticateHeader wwwAuthenticateHeader = new WWWAuthenticateHeaderImpl(realm, nonce, null, null);
+
+ assertEquals(wwwAuthenticateHeader.getAlgorithm(), null);
+ assertEquals(wwwAuthenticateHeader.getNonce(), Buffers.wrap("bee3366b-cf59-476e-bc5e-334e0d65b386"));
+ assertEquals(wwwAuthenticateHeader.getQop(), null);
+ assertEquals(wwwAuthenticateHeader.getRealm(), Buffers.wrap("10.32.26.25"));
+
+ Buffer value = Buffers.wrap("Digest realm=\"10.32.26.25\", nonce=\"bee3366b-cf59-476e-bc5e-334e0d65b386\"");
+ assertTrue(wwwAuthenticateHeader.getValue().equalsIgnoreCase(value));
+ }
+
+
+}