ext/puma_http11/org/jruby/puma/Http11.java in piesync-puma-3.12.6.1 vs ext/puma_http11/org/jruby/puma/Http11.java in piesync-puma-5.4.0.1

- old
+ new

@@ -9,36 +9,46 @@ import org.jruby.RubyString; import org.jruby.anno.JRubyMethod; import org.jruby.runtime.ObjectAllocator; -import org.jruby.runtime.ThreadContext; import org.jruby.runtime.builtin.IRubyObject; import org.jruby.exceptions.RaiseException; import org.jruby.util.ByteList; /** * @author <a href="mailto:ola.bini@ki.se">Ola Bini</a> + * @author <a href="mailto:headius@headius.com">Charles Oliver Nutter</a> */ 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_REQUEST_PATH_LENGTH = 8192; + public final static String MAX_REQUEST_PATH_LENGTH_ERR = "HTTP element REQUEST_PATH is longer than the 8192 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); } @@ -51,132 +61,109 @@ RubyClass cHttpParser = mPuma.defineClassUnder("HttpParser",runtime.getObject(),ALLOCATOR); cHttpParser.defineAnnotatedMethods(Http11.class); } private Ruby runtime; - private RubyClass eHttpParserError; private Http11Parser hp; private RubyString body; public Http11(Ruby runtime, RubyClass clazz) { super(runtime,clazz); this.runtime = runtime; - this.eHttpParserError = (RubyClass)runtime.getModule("Puma").getConstant("HttpParserError"); this.hp = new Http11Parser(); - this.hp.parser.http_field = http_field; - this.hp.parser.request_method = request_method; - this.hp.parser.request_uri = request_uri; - this.hp.parser.fragment = fragment; - this.hp.parser.request_path = request_path; - this.hp.parser.query_string = query_string; - this.hp.parser.http_version = http_version; - this.hp.parser.header_done = header_done; this.hp.parser.init(); } - public void validateMaxLength(int len, int max, String msg) { + public static void validateMaxLength(Ruby runtime, int len, int max, String msg) { if(len>max) { - throw new RaiseException(runtime, eHttpParserError, msg, true); + throw newHTTPParserError(runtime, msg); } } - private Http11Parser.FieldCB http_field = new Http11Parser.FieldCB() { - public void call(Object data, int field, int flen, int value, int vlen) { - RubyHash req = (RubyHash)data; - RubyString f; - IRubyObject v; - validateMaxLength(flen, MAX_FIELD_NAME_LENGTH, MAX_FIELD_NAME_LENGTH_ERR); - validateMaxLength(vlen, MAX_FIELD_VALUE_LENGTH, MAX_FIELD_VALUE_LENGTH_ERR); + private static RaiseException newHTTPParserError(Ruby runtime, String msg) { + return runtime.newRaiseException(getHTTPParserError(runtime), msg); + } - ByteList b = new ByteList(Http11.this.hp.parser.buffer,field,flen); - for(int i = 0,j = b.length();i<j;i++) { - if((b.get(i) & 0xFF) == '-') { - b.set(i, (byte)'_'); - } else { - b.set(i, (byte)Character.toUpperCase((char)b.get(i))); - } - } + 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"); + } - String as = b.toString(); + 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); - if(as.equals("CONTENT_LENGTH") || as.equals("CONTENT_TYPE")) { - f = RubyString.newString(runtime, b); - } else { - f = RubyString.newString(runtime, "HTTP_"); - f.cat(b); - } - - b = new ByteList(Http11.this.hp.parser.buffer, value, vlen); - v = req.op_aref(req.getRuntime().getCurrentContext(), f); - if (v.isNil()) { - req.op_aset(req.getRuntime().getCurrentContext(), f, RubyString.newString(runtime, b)); - } else { - RubyString vs = v.convertToString(); - vs.cat(RubyString.newString(runtime, ", ")); - vs.cat(b); - } + ByteList b = new ByteList(buffer,field,flen); + for(int i = 0,j = b.length();i<j;i++) { + int bite = b.get(i) & 0xFF; + if(bite == '-') { + b.set(i, (byte)'_'); + } else { + b.set(i, (byte)Character.toUpperCase(bite)); } - }; + } - private Http11Parser.ElementCB request_method = new Http11Parser.ElementCB() { - public void call(Object data, int at, int length) { - RubyHash req = (RubyHash)data; - RubyString val = RubyString.newString(runtime,new ByteList(hp.parser.buffer,at,length)); - req.op_aset(req.getRuntime().getCurrentContext(), runtime.newString("REQUEST_METHOD"),val); - } - }; + while (vlen > 0 && Character.isWhitespace(buffer.get(value + vlen - 1))) vlen--; - private Http11Parser.ElementCB request_uri = new Http11Parser.ElementCB() { - public void call(Object data, int at, int length) { - RubyHash req = (RubyHash)data; - validateMaxLength(length, MAX_REQUEST_URI_LENGTH, MAX_REQUEST_URI_LENGTH_ERR); - RubyString val = RubyString.newString(runtime,new ByteList(hp.parser.buffer,at,length)); - req.op_aset(req.getRuntime().getCurrentContext(), runtime.newString("REQUEST_URI"),val); - } - }; + if (b.equals(CONTENT_LENGTH_BYTELIST) || b.equals(CONTENT_TYPE_BYTELIST)) { + f = RubyString.newString(runtime, b); + } else { + f = RubyString.newStringShared(runtime, HTTP_PREFIX_BYTELIST); + f.cat(b); + } - private Http11Parser.ElementCB fragment = new Http11Parser.ElementCB() { - public void call(Object data, int at, int length) { - RubyHash req = (RubyHash)data; - validateMaxLength(length, MAX_FRAGMENT_LENGTH, MAX_FRAGMENT_LENGTH_ERR); - RubyString val = RubyString.newString(runtime,new ByteList(hp.parser.buffer,at,length)); - req.op_aset(req.getRuntime().getCurrentContext(), runtime.newString("FRAGMENT"),val); - } - }; + b = new ByteList(buffer, value, vlen); + v = req.fastARef(f); + if (v == null || v.isNil()) { + req.fastASet(f, RubyString.newString(runtime, b)); + } else { + RubyString vs = v.convertToString(); + vs.cat(COMMA_SPACE_BYTELIST); + vs.cat(b); + } + } - private Http11Parser.ElementCB request_path = new Http11Parser.ElementCB() { - public void call(Object data, int at, int length) { - RubyHash req = (RubyHash)data; - validateMaxLength(length, MAX_REQUEST_PATH_LENGTH, MAX_REQUEST_PATH_LENGTH_ERR); - RubyString val = RubyString.newString(runtime,new ByteList(hp.parser.buffer,at,length)); - req.op_aset(req.getRuntime().getCurrentContext(), runtime.newString("REQUEST_PATH"),val); - } - }; + public static void request_method(Ruby runtime, RubyHash req, ByteList buffer, int at, int length) { + RubyString val = RubyString.newString(runtime,new ByteList(buffer,at,length)); + req.fastASet(RubyString.newStringShared(runtime, REQUEST_METHOD_BYTELIST),val); + } - private Http11Parser.ElementCB query_string = new Http11Parser.ElementCB() { - public void call(Object data, int at, int length) { - RubyHash req = (RubyHash)data; - validateMaxLength(length, MAX_QUERY_STRING_LENGTH, MAX_QUERY_STRING_LENGTH_ERR); - RubyString val = RubyString.newString(runtime,new ByteList(hp.parser.buffer,at,length)); - req.op_aset(req.getRuntime().getCurrentContext(), runtime.newString("QUERY_STRING"),val); - } - }; + public static void request_uri(Ruby runtime, RubyHash req, ByteList buffer, int at, int length) { + validateMaxLength(runtime, length, MAX_REQUEST_URI_LENGTH, MAX_REQUEST_URI_LENGTH_ERR); + RubyString val = RubyString.newString(runtime,new ByteList(buffer,at,length)); + req.fastASet(RubyString.newStringShared(runtime, REQUEST_URI_BYTELIST),val); + } - private Http11Parser.ElementCB http_version = new Http11Parser.ElementCB() { - public void call(Object data, int at, int length) { - RubyHash req = (RubyHash)data; - RubyString val = RubyString.newString(runtime,new ByteList(hp.parser.buffer,at,length)); - req.op_aset(req.getRuntime().getCurrentContext(), runtime.newString("HTTP_VERSION"),val); - } - }; + public static void fragment(Ruby runtime, RubyHash req, ByteList buffer, int at, int length) { + validateMaxLength(runtime, length, MAX_FRAGMENT_LENGTH, MAX_FRAGMENT_LENGTH_ERR); + RubyString val = RubyString.newString(runtime,new ByteList(buffer,at,length)); + req.fastASet(RubyString.newStringShared(runtime, FRAGMENT_BYTELIST),val); + } - private Http11Parser.ElementCB header_done = new Http11Parser.ElementCB() { - public void call(Object data, int at, int length) { - body = RubyString.newString(runtime, new ByteList(hp.parser.buffer, at, length)); - } - }; + public static void request_path(Ruby runtime, RubyHash req, ByteList buffer, int at, int length) { + validateMaxLength(runtime, length, MAX_REQUEST_PATH_LENGTH, MAX_REQUEST_PATH_LENGTH_ERR); + RubyString val = RubyString.newString(runtime,new ByteList(buffer,at,length)); + req.fastASet(RubyString.newStringShared(runtime, REQUEST_PATH_BYTELIST),val); + } + public static void query_string(Ruby runtime, RubyHash req, ByteList buffer, int at, int length) { + validateMaxLength(runtime, length, MAX_QUERY_STRING_LENGTH, MAX_QUERY_STRING_LENGTH_ERR); + RubyString val = RubyString.newString(runtime,new ByteList(buffer,at,length)); + req.fastASet(RubyString.newStringShared(runtime, QUERY_STRING_BYTELIST),val); + } + + public static void http_version(Ruby runtime, RubyHash req, ByteList buffer, int at, int length) { + RubyString val = RubyString.newString(runtime,new ByteList(buffer,at,length)); + req.fastASet(RubyString.newStringShared(runtime, HTTP_VERSION_BYTELIST),val); + } + + public void header_done(Ruby runtime, RubyHash req, ByteList buffer, int at, int length) { + body = RubyString.newStringShared(runtime, new ByteList(buffer, at, length)); + } + @JRubyMethod public IRubyObject initialize() { this.hp.parser.init(); return this; } @@ -193,23 +180,28 @@ return this.hp.is_finished() ? runtime.getTrue() : runtime.getFalse(); } @JRubyMethod public IRubyObject execute(IRubyObject req_hash, IRubyObject data, IRubyObject start) { - int from = 0; - from = RubyNumeric.fix2int(start); + int from = RubyNumeric.fix2int(start); ByteList d = ((RubyString)data).getByteList(); if(from >= d.length()) { - throw new RaiseException(runtime, eHttpParserError, "Requested start is after data buffer end.", true); + throw newHTTPParserError(runtime, "Requested start is after data buffer end."); } else { - this.hp.parser.data = req_hash; - this.hp.execute(d,from); - validateMaxLength(this.hp.parser.nread,MAX_HEADER_LENGTH, MAX_HEADER_LENGTH_ERR); - if(this.hp.has_error()) { - throw new RaiseException(runtime, eHttpParserError, "Invalid HTTP format, parsing fails.", true); + 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. Are you trying to open an SSL connection to a non-SSL Puma?"); } else { - return runtime.newFixnum(this.hp.parser.nread); + return runtime.newFixnum(parser.nread); } } } @JRubyMethod(name = "error?") @@ -224,10 +216,10 @@ @JRubyMethod public IRubyObject nread() { return runtime.newFixnum(this.hp.parser.nread); } - + @JRubyMethod public IRubyObject body() { return body; } }// Http11