I've come up with a better way to handle headers that have a lot of different fields in it. Instead of hard coding methods for every field you can now annotate any Enum table and all the constants within it automatically become fields.
This greatly simplifies long headers such as Http which has hundreds of different possible fields. Here is the entire Http header definition with the new annotation:
public class Http2
extends AbstractMessageHeader {
/**
* HTTP Request fields
*
* @author Mark Bednarczyk
* @author Sly Technologies, Inc.
*/
@Field
public enum Request {
Accept,
Accept_Charset,
Accept_Encoding,
Accept_Ranges,
Authorization,
Cache_Control,
Connection,
Cookie,
Date,
Host,
If_Modified_Since,
If_None_Match,
Referrer,
User_Agent,
RequestVersion,
RequestMethod,
RequestUrl,
}
/**
* HTTP Response fields
*
* @author Mark Bednarczyk
* @author Sly Technologies, Inc.
*/
@Field
public enum Response {
Accept_Ranges,
Age,
Allow,
Cache_Control,
Content_Encoding,
Content_Length,
Content_Location,
Content_Disposition,
Content_MD5,
Content_Range,
Content_Type,
RequestVersion,
ResponseCode,
ResponseCodeMessage,
RequestUrl,
}
@Dynamic(Field.Property.CHECK)
public boolean hasField(Request field) {
return super.hasField(field);
}
@Dynamic(Field.Property.VALUE)
public String fieldValue(Request field) {
return super.fieldValue(String.class, field);
}
@Dynamic(Field.Property.CHECK)
public boolean hasField(Response field) {
return super.hasField(field);
}
@Dynamic(Field.Property.VALUE)
public String fieldValue(Response field) {
return super.fieldValue(String.class, field);
}
@Override
protected void decodeFirstLine(String line) {
String[] c = line.split(" ");
if (c[0].startsWith("HTTP")) {
super.setMessageType(MessageType.RESPONSE);
super.addField(Response.RequestVersion, c[0], line.indexOf(c[0]));
super.addField(Response.ResponseCode, c[1], line.indexOf(c[1]));
super.addField(Response.ResponseCodeMessage, c[2], line.indexOf(c[2]));
} else {
super.setMessageType(MessageType.REQUEST);
super.addField(Request.RequestMethod, c[0], line.indexOf(c[0]));
super.addField(Request.RequestUrl, c[1], line.indexOf(c[1]));
super.addField(Request.RequestVersion, c[2], line.indexOf(c[2]));
}
}
AbstractMessageHeader.class does a little bit of parsing for internet message type headers, but the main time saving with this header is the @Field annotation on the 2 enum maps. Essentially the only thing you have to provide are the dynamic value getter method and since all these fields are pretty much optional, a blanket field checker method.
Here is how you use the definition in a real class:
public void testNewMappedHeaderSyntax() {
Http2 http = new Http2();
PcapPacket packet = TestUtils.getPcapPacket("tests/test-http-jpeg.pcap", 5);
if (packet.hasHeader(http) && http.getMessageType() == MessageType.REQUEST) {
if (http.hasField(Request.Accept)) {
System.out.printf("accepted = %s\n", http.fieldValue(Request.Accept));
}
}
}
I don't think you can get any simpler than that for such a complicated header. SIP and MIME based mail headers are going to be just as simple.