/** * Parses a header value. This is called from the generated bytecode. * * @param buffer The buffer * @param remaining The number of bytes remaining * @param state The current state * @param builder The exchange builder * @return The number of bytes remaining */ @SuppressWarnings("unused") final int handleHeaderValue( ByteBuffer buffer, int remaining, ParseState state, HttpExchangeBuilder builder) { StringBuilder stringBuilder = state.stringBuilder; if (stringBuilder == null) { stringBuilder = new StringBuilder(); } int parseState = state.parseState; while (remaining > 0) { final byte next = buffer.get(); --remaining; switch (parseState) { case NORMAL: { if (next == '\r') { parseState = BEGIN_LINE_END; } else if (next == '\n') { parseState = LINE_END; } else if (next == ' ' || next == '\t') { parseState = WHITESPACE; } else { stringBuilder.append((char) next); } break; } case WHITESPACE: { if (next == '\r') { parseState = BEGIN_LINE_END; } else if (next == '\n') { parseState = LINE_END; } else if (next == ' ' || next == '\t') { } else { if (stringBuilder.length() > 0) { stringBuilder.append(' '); } stringBuilder.append((char) next); parseState = NORMAL; } break; } case LINE_END: case BEGIN_LINE_END: { if (next == '\n' && parseState == BEGIN_LINE_END) { parseState = LINE_END; } else if (next == '\t' || next == ' ') { // this is a continuation parseState = WHITESPACE; } else { // we have a header String nextStandardHeader = state.nextHeader; builder.headers.put(nextStandardHeader, stringBuilder.toString()); state.nextHeader = null; state.leftOver = next; state.stringBuilder = null; if (next == '\r') { parseState = AWAIT_DATA_END; } else { state.state = ParseState.HEADER; state.parseState = 0; return remaining; } } break; } case AWAIT_DATA_END: { state.state = ParseState.PARSE_COMPLETE; return remaining; } } } // we only write to the state if we did not finish parsing state.parseState = parseState; state.stringBuilder = stringBuilder; return remaining; }
protected void parse(byte[] buf, int len) throws ParseException { int i; byte b; boolean error = false; // if (len > buf.length) { // throw new ParseException(String.format("Parse length(%d) > actual buffer length(%d)\n", // len, buf.length),0); // } // String tmpStr = new String(buf, 0, len); // System.err.printf("##### Parsing buf=[%s], ps.argBuf=[%s]\n", tmpStr.trim(), ps.argBuf); for (i = 0; i < len; i++) { b = buf[i]; // printStatus(buf, i); switch (ps.state) { case OP_START: switch (b) { case 'M': case 'm': ps.state = NatsOp.OP_M; break; case 'P': case 'p': ps.state = NatsOp.OP_P; break; case '+': ps.state = NatsOp.OP_PLUS; break; case '-': ps.state = NatsOp.OP_MINUS; break; default: error = true; break; } break; case OP_M: switch (b) { case 'S': case 's': ps.state = NatsOp.OP_MS; break; default: error = true; break; } break; case OP_MS: switch (b) { case 'G': case 'g': ps.state = NatsOp.OP_MSG; break; default: error = true; break; } break; case OP_MSG: switch (b) { case ' ': case '\t': ps.state = NatsOp.OP_MSG_SPC; break; default: error = true; break; } break; case OP_MSG_SPC: switch (b) { case ' ': case '\t': continue; default: ps.state = NatsOp.MSG_ARG; ps.as = i; break; } break; case MSG_ARG: switch (b) { case '\r': ps.drop = 1; break; case '\n': ByteBuffer arg = null; if (ps.argBuf != null) { // End of args arg = ps.argBuf; arg.flip(); processMsgArgs(arg.array(), arg.arrayOffset(), arg.limit()); } else { // arg = ByteBuffer.wrap(buf, ps.as, // i-ps.drop-ps.as).slice(); processMsgArgs(buf, ps.as, i - ps.drop - ps.as); } // System.err.printf("arrayOffset=%d, length=%d\n", // arg.arrayOffset(), arg.limit()); // processMsgArgs(arg.array(), arg.arrayOffset(), arg.limit()); ps.drop = 0; ps.as = i + 1; ps.state = NatsOp.MSG_PAYLOAD; // jump ahead with the index. If this overruns // what is left we fall out and process split // buffer. i = ps.as + ps.ma.size - 1; break; default: // We have a leftover argBuf we'll continuing filling if (ps.argBuf != null) { try { ps.argBuf.put(b); } catch (Exception e) { System.err.printf("i=%d, b=%c, ps.argBuf=%s\n", i, (char) b, ps.argBuf); e.printStackTrace(); } } break; } break; case MSG_PAYLOAD: boolean done = false; // System.err.printf("MSG_PAYLOAD: ps.ma.size = %d\n", ps.ma.size); // System.err.printf("ps.msgBuf.position=%d, i=%d, ps.as=%d, // ps.ma.size=%d\n", // ps.msgBuf.position(), i, ps.as, ps.ma.size); if (ps.msgBuf != null) { // System.err.printf("ps.msgBuf.position=%d, i=%d, ps.as=%d, // ps.ma.size=%d\n", // ps.msgBuf.position(), i, ps.as, ps.ma.size); // Already have bytes in the buffer if (ps.msgBuf.position() >= ps.ma.size) { ps.msgBuf.flip(); nc.processMsg(ps.msgBuf.array(), 0, ps.msgBuf.limit()); done = true; } else { // copy as much as we can to the buffer and skip ahead. int toCopy = ps.ma.size - ps.msgBuf.limit(); int avail = len - i; if (avail < toCopy) { toCopy = avail; } // System.err.printf("msgBuf=%s(remaining=%d), i=%d, len=%d, // ps.ma.size=%d, avail = %d, toCopy=%d," // + " buf.length=%d\n", // ps.msgBuf, ps.msgBuf.remaining(), i, len, ps.ma.size, avail, // toCopy, buf.length); if (toCopy > 0) { // System.err.printf("msgBuf=%s(remaining=%d), i=%d, len=%d, // ps.ma.size=%d, avail = %d, toCopy=%d," // + " buf.length=%d\n", // ps.msgBuf, ps.msgBuf.remaining(), i, len, ps.ma.size, avail, // toCopy, buf.length); ps.msgBuf.put(buf, i, toCopy); // Update our index i += toCopy - 1; } else { ps.msgBuf.put(b); } } } else if (i - ps.as >= ps.ma.size) { // System.err.printf("i=%d, ps.as=%d, ps.ma.size=%d\n", i, ps.as, // ps.ma.size); // If we are at or past the end of the payload, go ahead and process it, no // buffering needed. nc.processMsg(buf, ps.as, i - ps.as); // pass offset and length done = true; } if (done) { ps.argBuf = null; // ps.argBuf.clear(); ps.msgBuf = null; // ps.msgBuf.clear(); ps.state = NatsOp.MSG_END; } break; case MSG_END: switch (b) { case '\n': ps.drop = 0; ps.as = i + 1; ps.state = NatsOp.OP_START; break; default: continue; } break; case OP_PLUS: switch (b) { case 'O': case 'o': ps.state = NatsOp.OP_PLUS_O; break; default: error = true; break; } break; case OP_PLUS_O: switch (b) { case 'K': case 'k': ps.state = NatsOp.OP_PLUS_OK; break; default: error = true; break; } break; case OP_PLUS_OK: switch (b) { case '\n': nc.processOK(); ps.drop = 0; ps.state = NatsOp.OP_START; break; } break; case OP_MINUS: switch (b) { case 'E': case 'e': ps.state = NatsOp.OP_MINUS_E; break; default: error = true; break; } break; case OP_MINUS_E: switch (b) { case 'R': case 'r': ps.state = NatsOp.OP_MINUS_ER; break; default: error = true; break; } break; case OP_MINUS_ER: switch (b) { case 'R': case 'r': ps.state = NatsOp.OP_MINUS_ERR; break; default: error = true; break; } break; case OP_MINUS_ERR: switch (b) { case ' ': case '\t': ps.state = NatsOp.OP_MINUS_ERR_SPC; break; default: error = true; break; } break; case OP_MINUS_ERR_SPC: switch (b) { case ' ': case '\t': continue; default: ps.state = NatsOp.MINUS_ERR_ARG; ps.as = i; break; } break; case MINUS_ERR_ARG: switch (b) { case '\r': ps.drop = 1; break; case '\n': ByteBuffer arg = null; if (ps.argBuf != null) { arg = ps.argBuf; ps.argBuf = null; } else { arg = ByteBuffer.wrap(buf, ps.as, i - ps.as); } nc.processErr(arg); ps.drop = 0; ps.as = i + 1; ps.state = NatsOp.OP_START; break; default: if (ps.argBuf != null) { ps.argBuf.put(b); } break; } break; case OP_P: switch (b) { case 'I': case 'i': ps.state = NatsOp.OP_PI; break; case 'O': case 'o': ps.state = NatsOp.OP_PO; break; default: error = true; break; } break; case OP_PO: switch (b) { case 'N': case 'n': ps.state = NatsOp.OP_PON; break; default: // parseError(buf, i); error = true; break; } break; case OP_PON: switch (b) { case 'G': case 'g': ps.state = NatsOp.OP_PONG; break; default: error = true; break; } break; case OP_PONG: switch (b) { case '\n': nc.processPong(); ps.drop = 0; ps.state = NatsOp.OP_START; break; default: break; } break; case OP_PI: switch (b) { case 'N': case 'n': ps.state = NatsOp.OP_PIN; break; default: error = true; break; } break; case OP_PIN: switch (b) { case 'G': case 'g': ps.state = NatsOp.OP_PING; break; default: error = true; break; } break; case OP_PING: switch (b) { case '\n': nc.processPing(); ps.drop = 0; ps.state = NatsOp.OP_START; break; } break; default: error = true; break; } // switch(ps.state) if (error) { error = false; throw new ParseException( String.format("nats: parse error [%s]: '%s'", ps.state, new String(buf, i, len - i)), i); } // System.err.printf("After processing index %d, ps.state=%s\n", i, ps.state ); } // for // We have processed the entire buffer // Check for split buffer scenarios if ((ps.state == NatsOp.MSG_ARG || ps.state == NatsOp.MINUS_ERR_ARG) && (ps.argBuf == null)) { ps.argBuf = ByteBuffer.wrap(ps.argBufStore); ps.argBuf.put(buf, ps.as, i - ps.drop - ps.as); // System.err.printf("split msg, no clone, ps.argBuf=%s\n", ps.argBuf); // FIXME, check max len } // Check for split msg if (ps.state == NatsOp.MSG_PAYLOAD && ps.msgBuf == null) { // We need to clone the msgArg if it is still referencing the // read buffer and we are not able to process the msg. if (ps.argBuf == null) { cloneMsgArg(); // System.err.printf("split msg, after clone, ps.argBuf=%s\n", ps.argBuf); } // If we will overflow the scratch buffer, just create a // new buffer to hold the split message. int lrem = len - ps.as; // portion of msg remaining in buffer if (ps.ma.size > ps.msgBufStore.length) { ps.msgBufStore = new byte[ps.ma.size]; } ps.msgBuf = ByteBuffer.wrap(ps.msgBufStore); // copy what's left in the buffer ps.msgBuf.put(buf, ps.as, lrem); } }
/** * Parses a path value. This is called from the generated bytecode. * * @param buffer The buffer * @param remaining The number of bytes remaining * @param state The current state * @param builder The exchange builder * @return The number of bytes remaining */ @SuppressWarnings("unused") final int handlePath( ByteBuffer buffer, int remaining, ParseState state, HttpExchangeBuilder builder) { StringBuilder stringBuilder = state.stringBuilder; int parseState = state.parseState; int canonicalPathStart = state.pos; int queryParamPos = state.queryParamPos; String nextQueryParam = state.nextHeader; if (stringBuilder == null) { state.stringBuilder = stringBuilder = new StringBuilder(); } while (remaining > 0) { final char next = (char) buffer.get(); --remaining; if (next == ' ' || next == '\t') { if (stringBuilder.length() != 0) { final String path = stringBuilder.toString(); builder.fullPath = path; if (parseState < HOST_DONE) { builder.relativePath = path; } else { builder.relativePath = path.substring(canonicalPathStart); } if (parseState == QUERY_PARAM_NAME) { builder.addQueryParam(stringBuilder.substring(queryParamPos), ""); } else if (parseState == QUERY_PARAM_VALUE) { builder.addQueryParam(nextQueryParam, stringBuilder.substring(queryParamPos)); } state.state = ParseState.VERSION; state.stringBuilder = null; state.parseState = 0; state.pos = 0; state.nextHeader = null; state.queryParamPos = 0; return remaining; } } else { if (next == ':' && parseState == START) { parseState = FIRST_COLON; } else if (next == '/' && parseState == FIRST_COLON) { parseState = FIRST_SLASH; } else if (next == '/' && parseState == FIRST_SLASH) { parseState = SECOND_SLASH; } else if (next == '/' && parseState == SECOND_SLASH) { parseState = HOST_DONE; canonicalPathStart = stringBuilder.length(); } else if (parseState == FIRST_COLON || parseState == FIRST_SLASH) { parseState = START; } else if (next == '?' && (parseState == START || parseState == HOST_DONE)) { parseState = QUERY_PARAM_NAME; queryParamPos = stringBuilder.length() + 1; } else if (next == '=' && parseState == QUERY_PARAM_NAME) { parseState = QUERY_PARAM_VALUE; nextQueryParam = stringBuilder.substring(queryParamPos); queryParamPos = stringBuilder.length() + 1; } else if (next == '&' && parseState == QUERY_PARAM_NAME) { parseState = QUERY_PARAM_NAME; builder.addQueryParam(stringBuilder.substring(queryParamPos), ""); nextQueryParam = null; queryParamPos = stringBuilder.length() + 1; } else if (next == '&' && parseState == QUERY_PARAM_VALUE) { parseState = QUERY_PARAM_NAME; builder.addQueryParam(nextQueryParam, stringBuilder.substring(queryParamPos)); nextQueryParam = null; queryParamPos = stringBuilder.length() + 1; } stringBuilder.append(next); } } state.stringBuilder = stringBuilder; state.parseState = parseState; state.pos = canonicalPathStart; state.nextHeader = nextQueryParam; state.queryParamPos = queryParamPos; return remaining; }