@Override
    protected void channelRead0(ChannelHandlerContext ctx, HttpRequest msg) throws Exception {
        CommandContext commandContext = HttpCommandDecoder.decode(msg);
        // return 404 when fail to construct command context
        if (commandContext == null) {
            log.warn(QOS_UNEXPECTED_EXCEPTION, "", "", "can not found commandContext, url: " + msg.uri());
            FullHttpResponse response = http(404);
            ctx.writeAndFlush(response).addListener(ChannelFutureListener.CLOSE);
        } else {
            commandContext.setRemote(ctx.channel());
            commandContext.setQosConfiguration(qosConfiguration);
            try {
                String result = commandExecutor.execute(commandContext);
                int httpCode = commandContext.getHttpCode();
                FullHttpResponse response = http(httpCode, result);
                ctx.writeAndFlush(response).addListener(ChannelFutureListener.CLOSE);
            } catch (NoSuchCommandException ex) {
                log.error(QOS_COMMAND_NOT_FOUND, "", "", "can not find command: " + commandContext, ex);
                FullHttpResponse response = http(404);
                ctx.writeAndFlush(response).addListener(ChannelFutureListener.CLOSE);
            } catch (PermissionDenyException ex) {
                log.error(
                        QOS_PERMISSION_DENY_EXCEPTION,
                        "",
                        "",
                        "permission deny to access command: " + commandContext,
                        ex);
                FullHttpResponse response = http(403);
                ctx.writeAndFlush(response).addListener(ChannelFutureListener.CLOSE);
            } catch (Exception qosEx) {
                log.error(
                        QOS_UNEXPECTED_EXCEPTION,
                        "",
                        "",
                        "execute commandContext: " + commandContext + " got exception",
                        qosEx);
                FullHttpResponse response = http(500, qosEx.getMessage());
                ctx.writeAndFlush(response).addListener(ChannelFutureListener.CLOSE);
            }
        }
    }
@Test
    void test2() throws Exception {
        ChannelHandlerContext context = mock(ChannelHandlerContext.class);
        ChannelFuture future = mock(ChannelFuture.class);
        when(context.writeAndFlush(any(FullHttpResponse.class))).thenReturn(future);
        HttpRequest message = Mockito.mock(HttpRequest.class);
        when(message.uri()).thenReturn("localhost:80/greeting");
        when(message.method()).thenReturn(HttpMethod.GET);
        HttpProcessHandler handler = new HttpProcessHandler(
                FrameworkModel.defaultModel(),
                QosConfiguration.builder()
                        .anonymousAccessPermissionLevel(PermissionLevel.NONE.name())
                        .build());
        handler.channelRead0(context, message);
        verify(future).addListener(ChannelFutureListener.CLOSE);
        ArgumentCaptor<FullHttpResponse> captor = ArgumentCaptor.forClass(FullHttpResponse.class);
        verify(context).writeAndFlush(captor.capture());
        FullHttpResponse response = captor.getValue();
        assertThat(response.status().code(), equalTo(200));
    }