package org.jruby.puma;
import org.jruby.Ruby;
import org.jruby.RubyClass;
import org.jruby.RubyHash;
import org.jruby.RubyModule;
import org.jruby.RubyNumeric;
import org.jruby.RubyObject;
import org.jruby.RubyString;
import org.jruby.anno.JRubyMethod;
import org.jruby.runtime.ObjectAllocator;
import org.jruby.runtime.builtin.IRubyObject;
import org.jruby.exceptions.RaiseException;
import org.jruby.util.ByteList;
/**
* @author Ola Bini
* @author Charles Oliver Nutter
*/
public class Http11 extends RubyObject {
public final static int MAX_FIELD_NAME_LENGTH = 256;
public final static String MAX_FIELD_NAME_LENGTH_ERR = "HTTP element FIELD_NAME is longer than the 256 allowed length.";
public final static int MAX_FIELD_VALUE_LENGTH = 80 * 1024;
public final static String MAX_FIELD_VALUE_LENGTH_ERR = "HTTP element FIELD_VALUE is longer than the 81920 allowed length.";
public final static int MAX_REQUEST_URI_LENGTH = 1024 * 12;
public final static String MAX_REQUEST_URI_LENGTH_ERR = "HTTP element REQUEST_URI is longer than the 12288 allowed length.";
public final static int MAX_FRAGMENT_LENGTH = 1024;
public final static String MAX_FRAGMENT_LENGTH_ERR = "HTTP element REQUEST_PATH is longer than the 1024 allowed length.";
public final static int MAX_REQUEST_PATH_LENGTH = 2048;
public final static String MAX_REQUEST_PATH_LENGTH_ERR = "HTTP element REQUEST_PATH is longer than the 2048 allowed length.";
public final static int MAX_QUERY_STRING_LENGTH = 1024 * 10;
public final static String MAX_QUERY_STRING_LENGTH_ERR = "HTTP element QUERY_STRING is longer than the 10240 allowed length.";
public final static int MAX_HEADER_LENGTH = 1024 * (80 + 32);
public final static String MAX_HEADER_LENGTH_ERR = "HTTP element HEADER is longer than the 114688 allowed length.";
public static final ByteList CONTENT_TYPE_BYTELIST = new ByteList(ByteList.plain("CONTENT_TYPE"));
public static final ByteList CONTENT_LENGTH_BYTELIST = new ByteList(ByteList.plain("CONTENT_LENGTH"));
public static final ByteList HTTP_PREFIX_BYTELIST = new ByteList(ByteList.plain("HTTP_"));
public static final ByteList COMMA_SPACE_BYTELIST = new ByteList(ByteList.plain(", "));
public static final ByteList REQUEST_METHOD_BYTELIST = new ByteList(ByteList.plain("REQUEST_METHOD"));
public static final ByteList REQUEST_URI_BYTELIST = new ByteList(ByteList.plain("REQUEST_URI"));
public static final ByteList FRAGMENT_BYTELIST = new ByteList(ByteList.plain("FRAGMENT"));
public static final ByteList REQUEST_PATH_BYTELIST = new ByteList(ByteList.plain("REQUEST_PATH"));
public static final ByteList QUERY_STRING_BYTELIST = new ByteList(ByteList.plain("QUERY_STRING"));
public static final ByteList HTTP_VERSION_BYTELIST = new ByteList(ByteList.plain("HTTP_VERSION"));
private static ObjectAllocator ALLOCATOR = new ObjectAllocator() {
public IRubyObject allocate(Ruby runtime, RubyClass klass) {
return new Http11(runtime, klass);
}
};
public static void createHttp11(Ruby runtime) {
RubyModule mPuma = runtime.defineModule("Puma");
mPuma.defineClassUnder("HttpParserError",runtime.getClass("IOError"),runtime.getClass("IOError").getAllocator());
RubyClass cHttpParser = mPuma.defineClassUnder("HttpParser",runtime.getObject(),ALLOCATOR);
cHttpParser.defineAnnotatedMethods(Http11.class);
}
private Ruby runtime;
private Http11Parser hp;
private RubyString body;
public Http11(Ruby runtime, RubyClass clazz) {
super(runtime,clazz);
this.runtime = runtime;
this.hp = new Http11Parser();
this.hp.parser.init();
}
public static void validateMaxLength(Ruby runtime, int len, int max, String msg) {
if(len>max) {
throw newHTTPParserError(runtime, msg);
}
}
private static RaiseException newHTTPParserError(Ruby runtime, String msg) {
return runtime.newRaiseException(getHTTPParserError(runtime), msg);
}
private static RubyClass getHTTPParserError(Ruby runtime) {
// Cheaper to look this up lazily than cache eagerly and consume a field, since it's rarely encountered
return (RubyClass)runtime.getModule("Puma").getConstant("HttpParserError");
}
public static void http_field(Ruby runtime, RubyHash req, ByteList buffer, int field, int flen, int value, int vlen) {
RubyString f;
IRubyObject v;
validateMaxLength(runtime, flen, MAX_FIELD_NAME_LENGTH, MAX_FIELD_NAME_LENGTH_ERR);
validateMaxLength(runtime, vlen, MAX_FIELD_VALUE_LENGTH, MAX_FIELD_VALUE_LENGTH_ERR);
ByteList b = new ByteList(buffer,field,flen);
for(int i = 0,j = b.length();i= d.length()) {
throw newHTTPParserError(runtime, "Requested start is after data buffer end.");
} else {
Http11Parser hp = this.hp;
Http11Parser.HttpParser parser = hp.parser;
parser.data = (RubyHash) req_hash;
hp.execute(runtime, this, d,from);
validateMaxLength(runtime, parser.nread,MAX_HEADER_LENGTH, MAX_HEADER_LENGTH_ERR);
if(hp.has_error()) {
throw newHTTPParserError(runtime, "Invalid HTTP format, parsing fails.");
} else {
return runtime.newFixnum(parser.nread);
}
}
}
@JRubyMethod(name = "error?")
public IRubyObject has_error() {
return this.hp.has_error() ? runtime.getTrue() : runtime.getFalse();
}
@JRubyMethod(name = "finished?")
public IRubyObject is_finished() {
return this.hp.is_finished() ? runtime.getTrue() : runtime.getFalse();
}
@JRubyMethod
public IRubyObject nread() {
return runtime.newFixnum(this.hp.parser.nread);
}
@JRubyMethod
public IRubyObject body() {
return body;
}
}// Http11