Search in sources :

Example 1 with BigEndianBinaryReader

use of net.morimekta.util.io.BigEndianBinaryReader in project providence by morimekta.

the class NonblockingSocketServer method handleRead.

@SuppressWarnings("unchecked")
private void handleRead(SelectionKey key, Context context) throws IOException {
    long startTime = System.nanoTime();
    // part a: read into the readBuffer.
    if (context.currentFrameSize == 0) {
        // read frame size.
        try {
            if (context.channel.read(context.sizeBuffer) < 0) {
                context.close();
                key.cancel();
                return;
            }
            if (context.sizeBuffer.position() < 4) {
                return;
            }
        } catch (IOException e) {
            // LOGGER.error(e.getMessage(), e);
            context.close();
            key.cancel();
            return;
        }
        context.sizeBuffer.flip();
        try (ByteBufferInputStream in = new ByteBufferInputStream(context.sizeBuffer);
            BigEndianBinaryReader reader = new BigEndianBinaryReader(in)) {
            context.currentFrameSize = reader.expectInt();
        }
        context.sizeBuffer.rewind();
        if (context.currentFrameSize > maxFrameSizeInBytes) {
            LOGGER.warn("Attempting message of " + context.currentFrameSize + " > " + maxFrameSizeInBytes);
            context.close();
            key.cancel();
            return;
        }
        if (context.currentFrameSize < 1) {
            LOGGER.warn("Attempting message of " + context.currentFrameSize);
            context.close();
            key.cancel();
            return;
        }
        context.readBuffer.rewind();
        context.readBuffer.limit(context.currentFrameSize);
    }
    try {
        if (context.channel.read(context.readBuffer) < 0) {
            LOGGER.warn("Closed connection while reading frame");
            context.close();
            key.cancel();
            return;
        }
    } catch (IOException e) {
        LOGGER.warn("Exception reading frame: {}", e.getMessage(), e);
        context.close();
        key.cancel();
        return;
    }
    if (context.readBuffer.position() < context.readBuffer.limit()) {
        // wait until next read, and see if remaining of frame has arrived.
        return;
    }
    // part b: if the read buffer is complete, handle the content.
    PServiceCall call;
    try {
        context.currentFrameSize = 0;
        context.readBuffer.flip();
        call = serializer.deserialize(new ByteBufferInputStream(context.readBuffer), processor.getDescriptor());
        context.readBuffer.clear();
        workerExecutor.submit(() -> {
            PServiceCall reply;
            try {
                reply = processor.handleCall(call);
            } catch (Exception e) {
                reply = new PServiceCall<>(call.getMethod(), PServiceCallType.EXCEPTION, call.getSequence(), PApplicationException.builder().setMessage(e.getMessage()).setId(PApplicationExceptionType.INTERNAL_ERROR).initCause(e).build());
            }
            synchronized (context.writeQueue) {
                context.writeQueue.offer(new WriteEntry(startTime, call, reply));
                key.interestOps(key.interestOps() | SelectionKey.OP_WRITE);
                selector.wakeup();
            }
        });
    } catch (IOException e) {
        double duration = ((double) System.nanoTime() - startTime) / NS_IN_MILLIS;
        instrumentation.onTransportException(e, duration, null, null);
    }
}
Also used : PServiceCall(net.morimekta.providence.PServiceCall) ByteBufferInputStream(net.morimekta.util.io.ByteBufferInputStream) BigEndianBinaryReader(net.morimekta.util.io.BigEndianBinaryReader) IOException(java.io.IOException) UncheckedIOException(java.io.UncheckedIOException) PApplicationException(net.morimekta.providence.PApplicationException) IOException(java.io.IOException) UncheckedIOException(java.io.UncheckedIOException)

Example 2 with BigEndianBinaryReader

use of net.morimekta.util.io.BigEndianBinaryReader in project providence by morimekta.

the class BinaryFormatUtils method readMessage.

/**
 * Read message from reader.
 *
 * @param input The input reader.
 * @param descriptor The message descriptor.
 * @param strict If the message should be read in strict mode.
 * @param <Message> The message type.
 * @param <Field> The field type.
 * @return The read and parsed message.
 * @throws IOException If read failed.
 */
public static <Message extends PMessage<Message, Field>, Field extends PField> Message readMessage(BigEndianBinaryReader input, PMessageDescriptor<Message, Field> descriptor, boolean strict) throws IOException {
    PMessageBuilder<Message, Field> builder = descriptor.builder();
    if (builder instanceof BinaryReader) {
        ((BinaryReader) builder).readBinary(input, strict);
    } else {
        FieldInfo fieldInfo = readFieldInfo(input);
        while (fieldInfo != null) {
            PField field = descriptor.findFieldById(fieldInfo.getId());
            if (field != null) {
                Object value = readFieldValue(input, fieldInfo, field.getDescriptor(), strict);
                builder.set(field.getId(), value);
            } else {
                readFieldValue(input, fieldInfo, null, false);
            }
            fieldInfo = readFieldInfo(input);
        }
        if (strict) {
            try {
                builder.validate();
            } catch (IllegalStateException e) {
                throw new SerializerException(e, e.getMessage());
            }
        }
    }
    return builder.build();
}
Also used : PField(net.morimekta.providence.descriptor.PField) PMessage(net.morimekta.providence.PMessage) PField(net.morimekta.providence.descriptor.PField) BigEndianBinaryReader(net.morimekta.util.io.BigEndianBinaryReader) SerializerException(net.morimekta.providence.serializer.SerializerException)

Example 3 with BigEndianBinaryReader

use of net.morimekta.util.io.BigEndianBinaryReader in project providence by morimekta.

the class BinarySerializer method deserialize.

@Nonnull
@Override
@SuppressWarnings("unchecked")
public <Message extends PMessage<Message, Field>, Field extends PField> PServiceCall<Message, Field> deserialize(@Nonnull InputStream is, @Nonnull PService service) throws IOException {
    BigEndianBinaryReader in = new BigEndianBinaryReader(is);
    String methodName = null;
    int sequence = 0;
    PServiceCallType type = null;
    try {
        int methodNameLen = in.expectInt();
        int typeKey;
        // versioned
        if (methodNameLen <= 0) {
            int version = methodNameLen & VERSION_MASK;
            if (version == VERSION_1) {
                typeKey = methodNameLen & 0xFF;
                methodNameLen = in.expectInt();
                if (methodNameLen > MAX_METHOD_NAME_LEN) {
                    throw new SerializerException("Exceptionally long method name of %s bytes", methodNameLen).setExceptionType(PApplicationExceptionType.PROTOCOL_ERROR);
                }
                if (methodNameLen < 1) {
                    throw new SerializerException("Exceptionally short method name of %s bytes", methodNameLen).setExceptionType(PApplicationExceptionType.PROTOCOL_ERROR);
                }
                methodName = new String(in.expectBytes(methodNameLen), UTF_8);
            } else {
                throw new SerializerException("Bad protocol version: %08x", version >>> 16).setExceptionType(PApplicationExceptionType.INVALID_PROTOCOL);
            }
        } else {
            if (strict && versioned) {
                throw new SerializerException("Missing protocol version").setExceptionType(PApplicationExceptionType.INVALID_PROTOCOL);
            }
            if (methodNameLen > MAX_METHOD_NAME_LEN) {
                if (methodNameLen >>> 24 == '<') {
                    throw new SerializerException("Received HTML in service call").setExceptionType(PApplicationExceptionType.PROTOCOL_ERROR);
                }
                if (methodNameLen >>> 24 == '{' || methodNameLen >>> 24 == '[') {
                    throw new SerializerException("Received JSON in service call").setExceptionType(PApplicationExceptionType.PROTOCOL_ERROR);
                }
                throw new SerializerException("Exceptionally long method name of %s bytes", methodNameLen).setExceptionType(PApplicationExceptionType.PROTOCOL_ERROR);
            }
            methodName = new String(in.expectBytes(methodNameLen), UTF_8);
            typeKey = in.expectByte();
        }
        sequence = in.expectInt();
        type = PServiceCallType.findById(typeKey);
        PServiceMethod method = service.getMethod(methodName);
        if (type == null) {
            throw new SerializerException("Invalid call type " + typeKey).setExceptionType(PApplicationExceptionType.INVALID_MESSAGE_TYPE);
        } else if (type == PServiceCallType.EXCEPTION) {
            PApplicationException ex = readMessage(in, PApplicationException.kDescriptor, strict);
            return (PServiceCall<Message, Field>) new PServiceCall<>(methodName, type, sequence, ex);
        } else if (method == null) {
            throw new SerializerException("No such method " + methodName + " on " + service.getQualifiedName()).setExceptionType(PApplicationExceptionType.UNKNOWN_METHOD);
        }
        @SuppressWarnings("unchecked") PMessageDescriptor<Message, Field> descriptor = isRequestCallType(type) ? method.getRequestType() : method.getResponseType();
        Message message = readMessage(in, descriptor, strict);
        return new PServiceCall<>(methodName, type, sequence, message);
    } catch (SerializerException se) {
        throw new SerializerException(se).setMethodName(methodName).setCallType(type).setSequenceNo(sequence);
    } catch (IOException e) {
        throw new SerializerException(e, e.getMessage()).setMethodName(methodName).setCallType(type).setSequenceNo(sequence);
    }
}
Also used : BinaryFormatUtils.writeMessage(net.morimekta.providence.serializer.binary.BinaryFormatUtils.writeMessage) BinaryFormatUtils.readMessage(net.morimekta.providence.serializer.binary.BinaryFormatUtils.readMessage) PMessage(net.morimekta.providence.PMessage) PServiceCallType(net.morimekta.providence.PServiceCallType) BigEndianBinaryReader(net.morimekta.util.io.BigEndianBinaryReader) IOException(java.io.IOException) PField(net.morimekta.providence.descriptor.PField) PApplicationException(net.morimekta.providence.PApplicationException) PServiceCall(net.morimekta.providence.PServiceCall) PServiceMethod(net.morimekta.providence.descriptor.PServiceMethod) Nonnull(javax.annotation.Nonnull)

Example 4 with BigEndianBinaryReader

use of net.morimekta.util.io.BigEndianBinaryReader in project providence by morimekta.

the class FramedBufferInputStream method readFrame.

private int readFrame() throws IOException {
    frameSizeBuffer.rewind();
    in.read(frameSizeBuffer);
    if (frameSizeBuffer.position() == 0) {
        return -1;
    }
    if (frameSizeBuffer.position() < Integer.BYTES) {
        throw new IOException("Not enough bytes for frame size: " + frameSizeBuffer.position());
    }
    frameSizeBuffer.flip();
    int frameSize;
    try (ByteBufferInputStream in = new ByteBufferInputStream(frameSizeBuffer);
        BigEndianBinaryReader reader = new BigEndianBinaryReader(in)) {
        frameSize = reader.expectInt();
    }
    if (frameSize < 1) {
        throw new IOException("Invalid frame size " + frameSize);
    } else if (frameSize > buffer.capacity()) {
        IOException ex = new IOException("Frame size too large " + frameSize + " > " + buffer.capacity());
        try {
            // Try to consume the frame so we can continue with the next.
            while (frameSize > 0) {
                buffer.rewind();
                buffer.limit(Math.max(frameSize, buffer.capacity()));
                int r = in.read(buffer);
                if (r > 0) {
                    frameSize -= r;
                } else {
                    break;
                }
            }
        } catch (Exception e) {
            ex.addSuppressed(e);
        }
        throw ex;
    }
    buffer.rewind();
    buffer.limit(frameSize);
    while (in.read(buffer) > 0) {
        if (buffer.position() == frameSize) {
            break;
        }
        LOGGER.debug("still not enough:  " + buffer.position() + " of " + frameSize);
    }
    if (buffer.position() < frameSize) {
        throw new IOException();
    }
    buffer.flip();
    return frameSize;
}
Also used : ByteBufferInputStream(net.morimekta.util.io.ByteBufferInputStream) BigEndianBinaryReader(net.morimekta.util.io.BigEndianBinaryReader) IOException(java.io.IOException) IOException(java.io.IOException)

Aggregations

BigEndianBinaryReader (net.morimekta.util.io.BigEndianBinaryReader)4 IOException (java.io.IOException)3 PApplicationException (net.morimekta.providence.PApplicationException)2 PMessage (net.morimekta.providence.PMessage)2 PServiceCall (net.morimekta.providence.PServiceCall)2 PField (net.morimekta.providence.descriptor.PField)2 ByteBufferInputStream (net.morimekta.util.io.ByteBufferInputStream)2 UncheckedIOException (java.io.UncheckedIOException)1 Nonnull (javax.annotation.Nonnull)1 PServiceCallType (net.morimekta.providence.PServiceCallType)1 PServiceMethod (net.morimekta.providence.descriptor.PServiceMethod)1 SerializerException (net.morimekta.providence.serializer.SerializerException)1 BinaryFormatUtils.readMessage (net.morimekta.providence.serializer.binary.BinaryFormatUtils.readMessage)1 BinaryFormatUtils.writeMessage (net.morimekta.providence.serializer.binary.BinaryFormatUtils.writeMessage)1