io.nats.client.Parser Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of jnats Show documentation
Show all versions of jnats Show documentation
Client library for working with the NATS messaging system.
/*******************************************************************************
* Copyright (c) 2015-2016 Apcera Inc. All rights reserved. This program and the accompanying
* materials are made available under the terms of the MIT License (MIT) which accompanies this
* distribution, and is available at http://opensource.org/licenses/MIT
*******************************************************************************/
package io.nats.client;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.nio.ByteBuffer;
import java.text.ParseException;
final class Parser {
final static Logger logger = LoggerFactory.getLogger(Parser.class);
final static int MAX_CONTROL_LINE_SIZE = 1024;
final static int MAX_MSG_ARGS = 4;
private ConnectionImpl nc;
// List args = new ArrayList();
protected class MsgArg {
// byte[] subjectBytes = new byte[MAX_CONTROL_LINE_SIZE];
ByteBuffer subject = ByteBuffer.allocate(MAX_CONTROL_LINE_SIZE);
// int subjectLength = 0;
// byte[] replyBytes = new byte [MAX_CONTROL_LINE_SIZE];
ByteBuffer reply = ByteBuffer.allocate(MAX_CONTROL_LINE_SIZE);
// int replyLength = 0;
long sid;
int size;
public String toString() {
return String.format("{subject=%s(len=%d), reply=%s(len=%d), sid=%d, size=%d}",
new String(subject.array(), 0, subject.limit()), subject.limit(),
reply == null ? "null" : new String(reply.array(), 0, reply.limit()),
reply.limit(), sid, size);
}
}
protected class ParseState {
NatsOp state = NatsOp.OP_START;
int as;
int drop;
MsgArg ma = new MsgArg();
byte[] argBufStore = new byte[ConnectionImpl.DEFAULT_BUF_SIZE];
ByteBuffer argBuf = null;
byte[] msgBufStore = new byte[ConnectionImpl.DEFAULT_BUF_SIZE];
ByteBuffer msgBuf = null;
// byte[] scratch = new byte[MAX_CONTROL_LINE_SIZE];
ByteBuffer[] args = new ByteBuffer[MAX_MSG_ARGS];
ParseState() {
for (int i = 0; i < MAX_MSG_ARGS; i++) {
args[i] = ByteBuffer.allocate(MAX_CONTROL_LINE_SIZE);
}
}
}
ParseState ps = new ParseState();
final static int ascii_0 = 48;
final static int ascii_9 = 57;
static enum NatsOp {
OP_START, OP_PLUS, OP_PLUS_O, OP_PLUS_OK, OP_MINUS, OP_MINUS_E, OP_MINUS_ER, OP_MINUS_ERR, OP_MINUS_ERR_SPC, MINUS_ERR_ARG, OP_M, OP_MS, OP_MSG, OP_MSG_SPC, MSG_ARG, MSG_PAYLOAD, MSG_END, OP_P, OP_PI, OP_PIN, OP_PING, OP_PO, OP_PON, OP_PONG
}
protected Parser(ConnectionImpl conn) {
this.nc = conn;
this.nc.ps = conn.ps;
}
// protected void printStatus(byte[] buf, int i) {
// String s = null;
// char b = (char)buf[i];
// if (b == '\r')
// s = "\\r";
// else if (b == '\n')
// s = "\\n";
// else if (b == '\t')
// s = "\\t";
// else
// s = String.format("%c", b);
//
// System.err.printf("ps.state = %s, new char buf[%d] = '%s' (0x%02X) argBuf=[%s]\n",
// ps.state, i , s,
// (int) b < 32 ? (int) b: b,
// bufToString(ps.argBuf));
// System.err.printf("ps.argBuf == %s\n", ps.argBuf);
// System.err.printf("ps.msgBuf == %s\n", ps.msgBuf);
// }
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) {
ps.argBuf.put(b);
}
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);
}
}
protected static String bufToString(ByteBuffer arg) {
if (arg == null) {
return null;
}
int pos = arg.position();
int len = arg.limit() - arg.position();
byte[] stringBytes = new byte[len];
arg.get(stringBytes, 0, len);
arg.position(pos);
String str = new String(stringBytes);
return str;
}
protected void processMsgArgs(byte[] arg, int offset, int length) throws ParseException {
// if (logger.isDebugEnabled()) {
// logger.trace("processMsgArgs (limit={}) content=[{}]\n", buffer.limit(),
// bufToString(buffer));
// }
// System.err.printf("offset=%d, length=%d\n", offset, length);
int argLen = 0;
int numArgs = 0;
int start = -1;
byte b;
int i;
for (i = offset; i < offset + length; i++) {
b = arg[i];
// System.err.println(String.format("Considering [%c]", (char)b));
switch (b) {
case ' ':
case '\t':
case '\r':
case '\n':
if (start >= 0) {
argLen = i - start;
// System.err.printf("start = %d, len = %d, ps.args[%d]=%s\n", start,
// argLen, numArgs, ps.args[numArgs]);
if (argLen > ps.args[numArgs].remaining()) {
ps.args[numArgs] = ByteBuffer.allocate(argLen);
}
ps.args[numArgs++].put(arg, start, argLen).flip();
start = -1;
}
break;
default:
if (start < 0) {
start = i;
}
break;
}
}
if (start >= 0) {
argLen = i - start;
ps.args[numArgs++].put(arg, start, argLen).flip();
}
// for (i = 0; i ascii_9) {
return -1;
}
n = (n * 10) + dec - ascii_0;
}
return n;
}
}