package org.dynjs.debugger.agent; import com.fasterxml.jackson.core.JsonParser; import com.fasterxml.jackson.databind.*; import com.fasterxml.jackson.databind.module.SimpleModule; import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBufInputStream; import io.netty.channel.ChannelHandlerContext; import io.netty.handler.codec.ReplayingDecoder; import org.dynjs.debugger.Debugger; import org.dynjs.debugger.agent.serializers.*; import org.dynjs.debugger.commands.AbstractCommand; import org.dynjs.debugger.requests.NoArgumentsRequest; import org.dynjs.debugger.requests.Request; import org.dynjs.debugger.requests.ScriptsRequest; import org.dynjs.debugger.requests.SourceRequest; import java.nio.charset.Charset; import java.util.List; import java.util.concurrent.atomic.AtomicInteger; /** * @author Bob McWhirter */ public class JSONDecoder extends ReplayingDecoder<JSONDecoder.State> { private static String CONTENT_LENGTH = "Content-Length:"; private static Charset UTF8 = Charset.forName("UTF8"); private final Debugger debugger; private final ObjectMapper mapper; private int length; public enum State { HEADER, BODY, } public JSONDecoder(Debugger debugger) { super(State.HEADER); this.debugger = debugger; SimpleModule module = new SimpleModule(); module.addDeserializer(SourceRequest.class, new SourceRequestDeserializer()); this.mapper = new ObjectMapper(); this.mapper.registerModule(module); this.mapper.configure(JsonParser.Feature.ALLOW_NON_NUMERIC_NUMBERS, true); this.mapper.disable(SerializationFeature.FAIL_ON_EMPTY_BEANS); } @Override protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception { if (state() == State.HEADER) { int nLoc = in.bytesBefore((byte) '\n'); ByteBuf headerBuf = in.readBytes(nLoc + 1); String headerLine = headerBuf.toString(UTF8).trim(); nLoc = in.bytesBefore((byte) '\n'); in.readBytes(nLoc); in.readByte(); if (headerLine.startsWith(CONTENT_LENGTH)) { String lenStr = headerLine.substring(CONTENT_LENGTH.length()).trim(); this.length = Integer.parseInt(lenStr); state(State.BODY); return; } } if (state() == State.BODY) { JsonNode node = mapper.readTree(new ByteBufInputStream(in, this.length)); String type = node.get("type").asText(); if ("request".equals(type)) { String commandStr = node.get("command").asText(); AbstractCommand command = this.debugger.getCommand(commandStr); if (command != null) { Request request = command.newRequest(); request.setSeq( node.get( "seq" ).asInt() ); ObjectReader reader = mapper.reader().withValueToUpdate(request); if ( node.has("arguments" ) ) { reader.readValue(node.get("arguments")); } else if ( request instanceof NoArgumentsRequest ) { reader.readValue(node); } out.add(request); } } state(State.HEADER); this.length = -1; } } }