package http_parser.lolevel;
import java.nio.*;
import java.io.*;
import java.util.*;
import http_parser.HTTPMethod;
import http_parser.HTTPParserUrl;
import http_parser.ParserType;
import http_parser.lolevel.TestLoaderNG.Header;
import http_parser.lolevel.TestLoaderNG.LastHeader;
import primitive.collection.ByteList;
import static http_parser.lolevel.Util.str;
public class Message {
String name;
byte [] raw;
ParserType type;
HTTPMethod method;
int status_code;
String request_path; // byte [] ?
String request_url;
String fragment ;
String query_string;
byte [] body;
int body_size;
int num_headers;
LastHeader last_header_element;
Map<String,String> header;
List<Header> headers;
boolean should_keep_alive;
byte[] upgrade;
boolean upgrade() {
return null != upgrade;
}
int http_major;
int http_minor;
boolean message_begin_called;
boolean headers_complete_called;
boolean message_complete_called;
boolean message_complete_on_eof;
Map<String,String> parsed_header;
String currHField;
String currHValue;
byte [] pbody;
int num_called;
public String toString() {
StringBuilder b = new StringBuilder();
b.append("NAME: "); b.append(name);b.append("\n");
b.append("type: "); b.append(type);b.append("\n");
b.append("method: "); b.append(method);b.append("\n");
b.append("status_code: "); b.append(status_code);b.append("\n");
b.append("request_path: "); b.append(request_path);b.append("\n");
b.append("request_url: "); b.append(request_url);b.append("\n");
b.append("fragment: "); b.append(fragment);b.append("\n");
b.append("query_string: "); b.append(query_string);b.append("\n");
b.append("body:\n"); b.append(new String(body));b.append("\n");
b.append("should_keep_alive: "); b.append(should_keep_alive);b.append("\n");
b.append("upgrade: "); b.append(upgrade);b.append("\n");
b.append("http_major: "); b.append(http_major);b.append("\n");
b.append("http_minor: "); b.append(http_minor);b.append("\n");
b.append("message_complete_called: "); b.append(message_complete_called);b.append("\n");
return b.toString();
}
Message () {
this.header = new HashMap<String, String>();
this.headers = new LinkedList<Header>();
reset();
}
/*
*prepare this Test Instance for reuse.
* */
void reset () {
this.parsed_header = new HashMap<String, String>();
this.pbody = null;
this.num_called = 0;
}
void check (boolean val, String mes) {
if (!val) {
//p(name+" : "+mes);
throw new RuntimeException(name+" : "+mes);
}
}
HTTPDataCallback getCB (final String value, final String mes, final TestSettings settings) {
return new HTTPDataCallback() {
public int cb (HTTPParser p, ByteBuffer b, int pos, int len){
// if ("url".equals(mes)){
// p("pos"+pos);
// p("len"+len);
// if (8==pos && 5 == len && "connect request".equals(name)) {
// //throw new RuntimeException(name);
// }
// }
//String str = str(b, pos, len);
ByteList list = settings.map.get(mes);
for (int i=0; i!=len; ++i) {
list.add(b.get(pos+i));
}
//settings.map.put(mes, prev_val + str);
//check(value.equals(str), "incorrect "+mes+": "+str);
if (-1 == pos) {
throw new RuntimeException("he?");
}
return 0;
}
};
}
void execute () {
p(name);
ByteBuffer buf = ByteBuffer.wrap(raw);
HTTPParser p = new HTTPParser();
TestSettings s = settings();
p.execute(s, buf);
if (!p.upgrade) {
// call execute again, else parser can't know message is done
// if no content length is set.
p.execute(s, buf);
}
if (!s.success) {
throw new RuntimeException("Test: "+name+" failed");
}
} // execute
void execute_permutations() {
/*
|-|---------------|
|--|--------------|
|---|-------------|
(...)
|---------------|-|
|-----------------|
*/
p(name);
for (int i = 2; i != raw.length; ++i) {
// p(i);
HTTPParser p = new HTTPParser();
TestSettings s = settings();
ByteBuffer buf = ByteBuffer.wrap(raw);
int olimit = buf.limit();
buf.limit(i);
parse(p,s,buf);
if (!p.upgrade) {
buf.position(i);
buf.limit(olimit);
parse(p,s,buf);
if (!p.upgrade) {
parse(p,s,buf);
} else {
if (!upgrade()) {
throw new RuntimeException("Test:"+name+"parsed as upgrade, is not");
}
}
} else {
if (!upgrade()) {
throw new RuntimeException("Test:"+name+"parsed as upgrade, is not");
}
}
if (!s.success) {
p(this);
throw new RuntimeException("Test: "+name+" failed");
}
reset();
}
//System.exit(0);
} // execute_permutations
void parse(HTTPParser p, ParserSettings s, ByteBuffer b) {
//p("About to parse: "+b.position() + "->" + b.limit());
p.execute(s, b);
}
TestSettings settings() {
final TestSettings s = new TestSettings();
s.on_url = getCB(request_url, "url", s);
s.on_message_begin = new HTTPCallback() {
public int cb (HTTPParser p) {
message_begin_called = true;
return -1;
}
};
s.on_header_field = new HTTPDataCallback() {
public int cb (HTTPParser p, ByteBuffer b, int pos, int len){
if (null != currHValue && null == currHField) {
throw new RuntimeException(name+": shouldn't happen");
}
if (null != currHField) {
if (null == currHValue) {
currHField += str(b,pos,len);
return 0;
} else {
parsed_header.put(currHField, currHValue);
currHField = null;
currHValue = null;
}
}
currHField = str(b,pos,len);
return 0;
}
};
s.on_header_value = new HTTPDataCallback() {
public int cb (HTTPParser p, ByteBuffer b, int pos, int len){
if (null == currHField) {
throw new RuntimeException(name+" :shouldn't happen field");
}
if (null == currHValue) {
currHValue = str(b,pos,len);
} else {
currHValue += str(b, pos, len);
}
return 0;
}
};
s.on_headers_complete = new HTTPCallback() {
public int cb (HTTPParser p) {
headers_complete_called = true;
String parsed_path = null;
String parsed_query = null;
String parsed_url = null;
String parsed_frag = null;
try {
parsed_url = new String(s.map.get("url").toArray(), "UTF8");
HTTPParserUrl u = new HTTPParserUrl();
HTTPParser pp = new HTTPParser();
ByteBuffer data = Util.buffer(parsed_url);
pp.parse_url(data,false, u);
parsed_path = u.getFieldValue(HTTPParser.UrlFields.UF_PATH, data);
parsed_query = u.getFieldValue(HTTPParser.UrlFields.UF_QUERY, data);
parsed_frag = u.getFieldValue(HTTPParser.UrlFields.UF_FRAGMENT, data);
} catch (java.io.UnsupportedEncodingException uee) {
throw new RuntimeException(uee);
}
if (!request_path.equals(parsed_path)) {
throw new RuntimeException(name+": invalid path: "+parsed_path+" should be: "+request_path);
}
if (!query_string.equals(parsed_query)) {
throw new RuntimeException(name+": invalid query: "+parsed_query+" should be: "+query_string);
}
if (!request_url.equals(parsed_url)) {
throw new RuntimeException(">"+name+"<: invalid url: >"+parsed_url+"< should be: >"+request_url+"<");
}
if (!fragment.equals(parsed_frag)) {
throw new RuntimeException(name+": invalid fragement: "+parsed_frag+" should be: "+fragment);
}
if (null != currHValue || null != currHField) {
if (null == currHField || null == currHValue) {
throw new RuntimeException("shouldn't happen");
}
}
if (null != currHField) {
//p(currHField);
//p(">"+currHValue+"<");
parsed_header.put(currHField, currHValue);
currHField = null;
currHValue = null;
}
return 0;
}
};
// s.on_headers_complete = new HTTPCallback() {
// public int cb (HTTPParser p) {
// p("Complete:"+name);
// return 0;
// }
// };
s.on_body = new HTTPDataCallback() {
public int cb (HTTPParser p, ByteBuffer b, int pos, int len){
int l = pbody == null ? len : len + pbody.length;
int off = pbody == null ? 0 : pbody.length;
byte [] nbody = new byte[l];
if (null != pbody) {
System.arraycopy(pbody, 0, nbody, 0, pbody.length);
}
int saved = b.position();
b.position(pos);
b.get(nbody, off, len);
b.position(saved);
pbody = nbody;
return 0;
}
};
s.on_message_complete = new HTTPCallback() {
public int cb(HTTPParser p) {
message_complete_called = true;
num_called += 1;
if ( p.http_minor != http_minor
|| p.http_major != http_major
|| p.status_code != status_code ) {
throw new RuntimeException("major/minor/status_code mismatch");
}
//check headers
if (header.keySet().size() != parsed_header.keySet().size()) {
p(parsed_header);
throw new RuntimeException(name+": different amount of headers");
}
for (String key : header.keySet()) {
String pvalue = parsed_header.get(key);
if (!header.get(key).equals(pvalue)) {
throw new RuntimeException(name+" : different values for :"+key+" is >"+pvalue+"< should: >"+header.get(key)+"<");
}
}
//check body
if (null == pbody && (null == body || body.length == 0 || body.length == 1)) {
s.success = true;
return 0;
}
if (null == pbody) {
throw new RuntimeException(name+": no body, should be: "+new String(body));
}
if (pbody.length != body.length) {
p(pbody.length);
p(body.length);
p(new String(pbody));
p(new String(body));
throw new RuntimeException(name+": incorrect body length");
}
for (int i = 0 ; i!= body.length; ++i) {
if (pbody[i] != body[i]) {
throw new RuntimeException("different body");
}
}
s.success = true;
return 0;
}
};
return s;
} // settings
static void p(Object o) {
System.out.println(o);
}
static class TestSettings extends ParserSettings {
public boolean success;
Map<String, ByteList> map;
TestSettings () {
map = new HashMap<String, ByteList>();
map.put("path", new ByteList());
map.put("query_string", new ByteList());
map.put("url", new ByteList());
map.put("fragment", new ByteList());
}
}
}