Пример #1
0
  @Test
  public void testEndianness() {
    ChannelBuffer buffer;
    int value = 12;

    // ByteOrder.BIG_ENDIAN
    buffer = ChannelBuffers.buffer(ByteOrder.BIG_ENDIAN, 4);
    buffer.writeInt(value);
    for (int i = 0; i < 4; i++) {
      byte b = buffer.readByte();
      System.out.println(b);
    }

    // ByteOrder.LITTLE_ENDIAN
    buffer = ChannelBuffers.buffer(ByteOrder.LITTLE_ENDIAN, 4);
    buffer.writeInt(value);
    for (int i = 0; i < 4; i++) {
      byte b = buffer.readByte();
      System.out.println(b);
    }

    // ByteOrder.nativeOrder()
    buffer = ChannelBuffers.buffer(ByteOrder.nativeOrder(), 4);
    buffer.writeInt(value);
    for (int i = 0; i < 4; i++) {
      byte b = buffer.readByte();
      System.out.println(b);
    }
  }
  @Test
  public void setmst() {
    String host = "host";
    int port = 1978;
    long timestamp = System.currentTimeMillis();
    int opts = RDB.ROCHKCON;
    Setmst dut = new Setmst(host, port, timestamp, opts);

    ChannelBuffer request = ChannelBuffers.buffer(2 + 4 + 4 + 8 + 4 + host.getBytes().length);
    request.writeBytes(new byte[] {(byte) 0xC8, (byte) 0x78});
    request.writeInt(host.getBytes().length);
    request.writeInt(port);
    request.writeLong(timestamp);
    request.writeInt(opts);
    request.writeBytes(host.getBytes());
    ChannelBuffer actual = ChannelBuffers.buffer(request.capacity());
    dut.encode(actual);
    assertEquals(request, actual);

    ChannelBuffer response = ChannelBuffers.buffer(1);
    assertFalse(dut.decode(response));

    response.writeByte(Command.ESUCCESS);
    assertTrue(dut.decode(response));
    assertTrue(dut.getReturnValue());

    // error
    response.clear();
    response.writeByte(Command.EUNKNOWN);
    assertTrue(dut.decode(response));
    assertFalse(dut.getReturnValue());
  }
Пример #3
0
  @Test
  public void iternext() {
    Iternext dut = new Iternext(transcoder, transcoder);

    ChannelBuffer request = ChannelBuffers.buffer(2);
    request.writeBytes(new byte[] {(byte) 0xC8, (byte) 0x51});
    ChannelBuffer actual = ChannelBuffers.buffer(request.capacity());
    dut.encode(actual);
    assertEquals(request, actual);

    ChannelBuffer response = ChannelBuffers.buffer(1 + 4 + value.length);
    assertFalse(dut.decode(response));

    response.writeByte(BinaryCommand.ESUCCESS);
    assertFalse(dut.decode(response));
    response.resetReaderIndex();

    response.writeInt(value.length);
    response.writeBytes(value);
    assertTrue(dut.decode(response));
    assertArrayEquals(value, (byte[]) dut.getReturnValue());

    // error
    response.clear();
    response.writeByte(BinaryCommand.EUNKNOWN);
    assertTrue(dut.decode(response));
    assertNull(dut.getReturnValue());
  }
  /**
   * Create a {@link ChannelBuffer} which is terminated with a CRLF.CRLF sequence
   *
   * @param data
   * @return buffer
   */
  private static ChannelBuffer createDataTerminatingChannelBuffer(byte[] data) {
    int length = data.length;
    if (length < 1) {
      return ChannelBuffers.wrappedBuffer(CRLF_DOT_CRLF);
    } else {
      byte[] terminating;

      byte last = data[length - 1];

      if (length == 1) {
        if (last == CR) {
          terminating = LF_DOT_CRLF;
        } else {
          terminating = CRLF_DOT_CRLF;
        }
      } else {
        byte prevLast = data[length - 2];

        if (last == LF) {
          if (prevLast == CR) {
            terminating = DOT_CRLF;
          } else {
            terminating = CRLF_DOT_CRLF;
          }
        } else if (last == CR) {
          terminating = LF_DOT_CRLF;
        } else {
          terminating = CRLF_DOT_CRLF;
        }
      }
      return ChannelBuffers.wrappedBuffer(data, terminating);
    }
  }
  private void sendReply(Channel channel, ChannelBuffer data) {
    ChannelBuffer header = ChannelBuffers.directBuffer(ByteOrder.LITTLE_ENDIAN, 16);
    header.writeBytes(ChannelBuffers.copiedBuffer(ByteOrder.LITTLE_ENDIAN, prefix, CHARSET));
    header.writeInt((int) deviceUniqueId);
    header.writeInt((int) serverId);
    header.writeShort(data.readableBytes());
    header.writeByte(checksum(data));
    header.writeByte(checksum(header));

    if (channel != null) {
      channel.write(ChannelBuffers.copiedBuffer(header, data));
    }
  }
  private Object processSingle(Channel channel, ChannelBuffer buf) {
    ParseResult result = parsePosition(buf);

    ChannelBuffer response = ChannelBuffers.dynamicBuffer(ByteOrder.LITTLE_ENDIAN, 8);
    response.writeBytes(ChannelBuffers.copiedBuffer(ByteOrder.LITTLE_ENDIAN, "*<T", CHARSET));
    response.writeInt((int) result.getId());
    sendReply(channel, response);

    if (result.getPosition().getFixTime() == null) {
      return null;
    }

    return result.getPosition();
  }
  @Override
  public ChannelBuffer toChannelBuffer() {
    ChannelBuffer[] buffers;
    ChannelBuffer currentBuffer = null;
    BytesRef ref = new BytesRef();
    int pos = 0;

    // are we a slice?
    if (offset != 0) {
      // remaining size of page fragment at offset
      int fragmentSize = Math.min(length, PAGE_SIZE - (offset % PAGE_SIZE));
      bytearray.get(offset, fragmentSize, ref);
      currentBuffer = ChannelBuffers.wrappedBuffer(ref.bytes, ref.offset, fragmentSize);
      pos += fragmentSize;
    }

    // no need to create a composite buffer for a single page
    if (pos == length && currentBuffer != null) {
      return currentBuffer;
    }

    // a slice > pagesize will likely require extra buffers for initial/trailing fragments
    int numBuffers = countRequiredBuffers((currentBuffer != null ? 1 : 0), length - pos);

    buffers = new ChannelBuffer[numBuffers];
    int bufferSlot = 0;

    if (currentBuffer != null) {
      buffers[bufferSlot] = currentBuffer;
      bufferSlot++;
    }

    // handle remainder of pages + trailing fragment
    while (pos < length) {
      int remaining = length - pos;
      int bulkSize = (remaining > PAGE_SIZE) ? PAGE_SIZE : remaining;
      bytearray.get(offset + pos, bulkSize, ref);
      currentBuffer = ChannelBuffers.wrappedBuffer(ref.bytes, ref.offset, bulkSize);
      buffers[bufferSlot] = currentBuffer;
      bufferSlot++;
      pos += bulkSize;
    }

    // this would indicate that our numBuffer calculation is off by one.
    assert (numBuffers == bufferSlot);

    // we can use gathering writes from the ChannelBuffers, but only if they are
    // moderately small to prevent OOMs due to DirectBuffer allocations.
    return ChannelBuffers.wrappedBuffer(length <= NIO_GATHERING_LIMIT, buffers);
  }
Пример #8
0
 @Override
 protected Object decode(ChannelHandlerContext ctx, Channel channel, ChannelBuffer buffer)
     throws Exception {
   if (opcode == -1) {
     if (buffer.readableBytes() >= 1) {
       opcode = buffer.readByte() & 0xFF;
       opcode = (opcode - cipher.getNextValue()) & 0xFF;
       size = Client.PACKET_SIZES[opcode];
     } else {
       return null;
     }
   }
   if (size == -1) {
     if (buffer.readableBytes() >= 1) {
       size = buffer.readByte() & 0xFF;
     } else {
       return null;
     }
   }
   if (buffer.readableBytes() >= size) {
     final byte[] data = new byte[size];
     buffer.readBytes(data);
     final ChannelBuffer payload = ChannelBuffers.buffer(size);
     payload.writeBytes(data);
     try {
       return new Packet(opcode, Type.FIXED, payload);
     } finally {
       opcode = -1;
       size = -1;
     }
   }
   return null;
 }
Пример #9
0
 @Override
 public ChannelBuffer encode(CommandMessage message) {
   ChannelBuffer buffer = ChannelBuffers.dynamicBuffer();
   buffer.writeInt(message.getCommand());
   ChannelBufferUtils.writeCommandArguments(buffer, message.getArguments());
   return buffer;
 }
Пример #10
0
 @Override
 public ChannelBuffer encode(StateChangeMessage message) throws IOException {
   ChannelBuffer buffer = ChannelBuffers.buffer(3);
   buffer.writeByte(message.getState());
   buffer.writeByte(message.getGameMode());
   return buffer;
 }
Пример #11
0
  private static void sendStatus(
      HttpResponse response,
      @Nullable HttpRequest request,
      Channel channel,
      @Nullable String description) {
    response.setHeader(CONTENT_TYPE, "text/html");
    if (request == null || request.getMethod() != HttpMethod.HEAD) {
      String message = response.getStatus().toString();

      StringBuilder builder = new StringBuilder();
      builder
          .append("<!doctype html><title>")
          .append(message)
          .append("</title>")
          .append("<h1 style=\"text-align: center\">")
          .append(message)
          .append("</h1>");
      if (description != null) {
        builder.append("<p>").append(description).append("</p>");
      }
      builder
          .append("<hr/><p style=\"text-align: center\">")
          .append(StringUtil.notNullize(getServerHeaderValue(), ""))
          .append("</p>");

      response.setContent(ChannelBuffers.copiedBuffer(builder, CharsetUtil.UTF_8));
    }
    send(response, channel, request);
  }
Пример #12
0
 @Override
 public void createHeader(LocalChannelReference lcr) throws OpenR66ProtocolPacketException {
   if (hostId == null) {
     throw new OpenR66ProtocolPacketException("Not enough data");
   }
   header = ChannelBuffers.wrappedBuffer(hostId.getBytes());
 }
  @Test
  public void testReadWrite() throws Exception {
    ChannelBuffer input = ChannelBuffers.copiedBuffer(OXM_IN_PHY_PORT_SERIALIZED);

    // FIXME should invoke the overall reader once implemented
    OFOxmInPhyPort oxmInPhyPort = OFOxmInPhyPortVer13.READER.readFrom(input);
    assertEquals(OXM_IN_PHY_PORT_SERIALIZED.length, input.readerIndex());

    // write message again
    ChannelBuffer bb = ChannelBuffers.dynamicBuffer();
    oxmInPhyPort.writeTo(bb);
    byte[] written = new byte[bb.readableBytes()];
    bb.readBytes(written);

    assertThat(written, CoreMatchers.equalTo(OXM_IN_PHY_PORT_SERIALIZED));
  }
Пример #14
0
  /*
   * (non-Javadoc)
   *
   * @see org.jboss.netty.handler.codec.oneone.OneToOneEncoder#encode(org.jboss
   * .netty.channel.ChannelHandlerContext, org.jboss.netty.channel.Channel, java.lang.Object)
   */
  @Override
  protected Object encode(final ChannelHandlerContext ctx, final Channel channel, final Object msg)
      throws Exception {

    final ChannelBuffer message = (ChannelBuffer) msg;

    final AuthToClientChannelHandler channelHandler =
        (AuthToClientChannelHandler) ctx.getPipeline().getLast();
    final int opcode = message.readUnsignedByte();
    final int size = message.readableBytes();

    final ChannelBuffer frame = ChannelBuffers.buffer(ByteOrder.LITTLE_ENDIAN, (size + 3));
    frame.writeByte(opcode);
    frame.writeShort(size);

    final byte[] tmpa = new byte[message.readableBytes()];
    message.readBytes(tmpa);
    frame.writeBytes(channelHandler.getCrypt().encrypt(tmpa));

    log.debug(String.format("[SEND PACKET] :  0x%02X", opcode));
    final List<String> d =
        breakStringInChunks(new BigInteger(1, tmpa).toString(16).toUpperCase(), 16);
    for (final String string : d) {
      log.debug(string);
    }
    return frame;
  }
 public OutboundServiceResponse(HttpVersion version) {
   this.nettyResponse = new DefaultHttpResponse(version, OK);
   this.nettyResponse.setChunked(false);
   this.nettyResponse.setContent(ChannelBuffers.dynamicBuffer());
   this.headerWrapper = new NettyHeaderWrapper(this.nettyResponse);
   this.outputStream = new ChannelBufferOutputStream(this.nettyResponse.getContent());
 }
Пример #16
0
  /*
   * (non-Javadoc)
   *
   * @see
   * org.jboss.netty.handler.codec.oneone.OneToOneDecoder#decode(org.jboss
   * .netty.channel.ChannelHandlerContext, org.jboss.netty.channel.Channel,
   * java.lang.Object)
   */
  @Override
  protected Object decode(ChannelHandlerContext ctx, Channel channel, Object msg) throws Exception {
    Message message = (Message) msg;
    long commandId = ((Long) message.getHeader().getCommandId()).longValue();

    if (packetType.getCommandId() != commandId) return msg;

    ReportRequestMessage requestMessage = new ReportRequestMessage();

    requestMessage.setHeader(message.getHeader());
    requestMessage.setBodyBuffer(message.getBodyBuffer());

    ChannelBuffer bodyBuffer = ChannelBuffers.copiedBuffer(message.getBodyBuffer());

    requestMessage.setSequenceNumber(
        DefaultSequenceNumberUtil.bytes2SequenceN(
            bodyBuffer.readBytes(ReportRequest.SUBMITSEQUENCENUMBER.getLength()).array()));
    requestMessage.setReporttype(bodyBuffer.readUnsignedByte());
    requestMessage.setUsernumber(
        bodyBuffer
            .readBytes(ReportRequest.USERNUMBER.getLength())
            .toString(GlobalVars.defaultTransportCharset));
    requestMessage.setState(bodyBuffer.readUnsignedByte());
    requestMessage.setErrorcode(bodyBuffer.readUnsignedByte());
    requestMessage.setReserve(
        bodyBuffer
            .readBytes(ReportRequest.RESERVE.getLength())
            .toString(GlobalVars.defaultTransportCharset));

    return requestMessage;
  }
Пример #17
0
 /**
  * encode the current Control Message into a channel buffer
  *
  * @throws Exception
  */
 ChannelBuffer buffer() throws Exception {
   ChannelBufferOutputStream bout =
       new ChannelBufferOutputStream(ChannelBuffers.directBuffer(encodeLength()));
   write(bout);
   bout.close();
   return bout.buffer();
 }
Пример #18
0
 public ChannelBuffer getContent() {
   ChannelBuffer cb = ChannelBuffers.dynamicBuffer();
   for (Frame frame : frames) {
     cb.writeBytes(frame.getPayload(), frame.getFrameSize() - 12);
   }
   return cb;
 }
Пример #19
0
  protected static void writeResponse(
      ChannelHandlerContext ctx,
      Response response,
      HttpResponse nettyResponse,
      HttpRequest nettyRequest) {
    Logger.trace("writeResponse: begin");
    byte[] content = null;

    final boolean keepAlive = isKeepAlive(nettyRequest);
    if (nettyRequest.getMethod().equals(HttpMethod.HEAD)) {
      content = new byte[0];
    } else {
      content = response.out.toByteArray();
    }

    ChannelBuffer buf = ChannelBuffers.copiedBuffer(content);
    nettyResponse.setContent(buf);

    if (keepAlive) {
      // Add 'Content-Length' header only for a keep-alive connection.
      Logger.trace("writeResponse: content length [" + response.out.size() + "]");
      setContentLength(nettyResponse, response.out.size());
    }

    ChannelFuture f = ctx.getChannel().write(nettyResponse);

    // Decide whether to close the connection or not.
    if (!keepAlive) {
      // Close the connection when the whole content is written out.
      f.addListener(ChannelFutureListener.CLOSE);
    }
    Logger.trace("writeResponse: end");
  }
Пример #20
0
 @Override
 public ChannelBuffer encode(CollectItemMessage message) throws IOException {
   ChannelBuffer buffer = ChannelBuffers.buffer(8);
   buffer.writeInt(message.getId());
   buffer.writeInt(message.getCollector());
   return buffer;
 }
Пример #21
0
  public static HttpResponse buildHttpServletResponse(HttpResponseExchange forwardResponse)
      throws IOException {

    if (null == forwardResponse) {
      return new DefaultHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.REQUEST_TIMEOUT);
    }
    HttpResponse response =
        new DefaultHttpResponse(
            HttpVersion.HTTP_1_1, HttpResponseStatus.valueOf(forwardResponse.getResponseCode()));

    List<String[]> headers = forwardResponse.getHeaders();
    for (String[] header : headers) {
      if (header[0].equalsIgnoreCase(HttpHeaders.Names.SET_COOKIE)
          || header[0].equalsIgnoreCase(HttpHeaders.Names.SET_COOKIE2)) {
        List<SetCookieHeaderValue> cookies = SetCookieHeaderValue.parse(header[1]);
        for (SetCookieHeaderValue cookie : cookies) {
          response.addHeader(header[0], cookie.toString());
        }
      } else {
        response.addHeader(header[0], header[1]);
      }
    }
    byte[] content = forwardResponse.getBody();
    if (null != content) {
      ChannelBuffer bufer = ChannelBuffers.wrappedBuffer(content);
      response.setContent(bufer);
    }

    return response;
  }
Пример #22
0
 @Override
 public ChannelBuffer encode(LevelColorMessage message) throws IOException {
   ChannelBuffer buffer = ChannelBuffers.dynamicBuffer();
   ChannelBufferUtils.writeString(buffer, message.getType());
   buffer.writeInt(message.getValue());
   return buffer;
 }
 @Override
 public ChannelBuffer getEncodedMessage() {
   ChannelBuffer encodedMessage = ChannelBuffers.dynamicBuffer();
   encodedMessage.writeBytes("Command: StatusList\n".getBytes(CharsetUtil.UTF_8));
   for (ExtensionStatus es : statuses) {
     encodedMessage.writeBytes(
         ("ExtensionStatus: "
                 + new StringBuilder()
                     .append(es.getExtension())
                     .append(" ")
                     .append(
                         es.getStatus() == null
                             ? DEFAULT_STATUS
                             : new StringBuilder()
                                 .append(es.getStatus())
                                 .append(" ")
                                 .append(this.dateFormat.format(es.getSince()))
                                 .toString())
                     .toString()
                 + "\n")
             .getBytes(CharsetUtil.UTF_8));
   }
   encodedMessage.writeBytes("\n".getBytes(CharsetUtil.UTF_8));
   encodedMessage = encodedMessage.slice(0, encodedMessage.writerIndex());
   return encodedMessage;
 }
 private Object processHandshake(Channel channel, ChannelBuffer buf) {
   buf.readByte(); // semicolon symbol
   if (identify(buf.toString(Charset.defaultCharset()), channel)) {
     sendReply(channel, ChannelBuffers.copiedBuffer(ByteOrder.LITTLE_ENDIAN, "*<S", CHARSET));
   }
   return null;
 }
  @Override
  public AsyncIOWriter write(byte[] data, int offset, int length) throws IOException {

    if (channel.isOpen()) {
      pendingWrite.incrementAndGet();
      final ChannelBuffer buffer = ChannelBuffers.dynamicBuffer();

      ChannelBufferOutputStream c = new ChannelBufferOutputStream(buffer);

      if (headerWritten) {
        c.write(Integer.toHexString(length - offset).getBytes("UTF-8"));
        c.write(CHUNK_DELIMITER);
      }

      c.write(data, offset, length);
      if (headerWritten) {
        c.write(CHUNK_DELIMITER);
      }

      channel.write(c.buffer()).addListener(listener);
      byteWritten = true;
      lastWrite = System.currentTimeMillis();
    } else {
      logger.warn("Trying to write on a closed channel {}", channel);
    }
    headerWritten = true;
    return this;
  }
Пример #26
0
  @Override
  protected final Object encode(
      final ChannelHandlerContext ctx, final Channel channel, final Object msg) throws Exception {

    // 不是计费类型的消息,不进行解析, 直接返回
    if (!(msg instanceof BaseMessage)) {
      // Ignore what this encoder can't encode.
      return msg;
    }

    //
    // 开始解析
    //

    // 将REPLY消息封装
    JsonElement element = gson.toJsonElement(msg);
    Mcpack mcpack = new Mcpack();
    byte[] dataByte = mcpack.toMcpack(ConanCommonConstants.ENCODING, element);

    // 设置消息头
    NsHead nsHead = new NsHead();
    nsHead.setReserved(ConanCommonConstants.DEFAULT_RESERVED);
    nsHead.setType((short) ConanMessageType.CONAN_REPLY_TYPE_DATA.getValue());
    nsHead.setVersion(ConanCommonConstants.CURRENT_VERSION);
    nsHead.setBodyLength(dataByte.length);

    // 发送消息
    byte[] headerByte = nsHead.toBytes();
    ChannelBuffer cb =
        ChannelBuffers.wrappedBuffer(ChannelBuffers.LITTLE_ENDIAN, headerByte, dataByte);

    LOGGER.debug("server encode: " + element.toString());

    return cb;
  }
Пример #27
0
  public void sendResponse(final MessageEvent e) {
    // Build the response object.
    final HttpResponse response = new DefaultHttpResponse(HTTP_1_1, OK);

    final ByteArrayOutputStream bos = receivedData;
    try {
      bos.write(" successfully received by server".getBytes("UTF-8"));
    } catch (final UnsupportedEncodingException e1) {
      e1.printStackTrace();
    } catch (final IOException e1) {
      e1.printStackTrace();
    }
    final ChannelBuffer buffer = ChannelBuffers.wrappedBuffer(bos.toByteArray());
    response.setContent(buffer);
    // response.setContent(arg0)
    // response.setContent(ChannelBuffers.copiedBuffer(buf.toString(),
    // CharsetUtil.UTF_8));
    response.setHeader(CONTENT_TYPE, "application/octet-stream");

    final ChannelFuture future = e.getChannel().write(response);

    // Close the non-keep-alive connection after the write operation is
    // done.
    // if (!keepAlive) {
    future.addListener(ChannelFutureListener.CLOSE);
    // }
  }
  /**
   * Writes the specified TaskResult data to the channel output. Only the raw output data is written
   * and rest of the TaskResult fields are ignored
   *
   * @param ctx the ChannelHandlerContext
   * @param event the ChannelEvent
   * @throws Exception in case of any errors
   */
  private void writeCommandExecutionResponse(
      ChannelHandlerContext ctx, ChannelEvent event, HttpResponse response) throws Exception {
    // Don't write anything if the response is null
    if (response == null) {
      // write empty response
      event
          .getChannel()
          .write(new DefaultHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.NO_CONTENT))
          .addListener(ChannelFutureListener.CLOSE);
      return;
    }
    org.jboss.netty.handler.codec.http.HttpResponse httpResponse =
        new DefaultHttpResponse(
            HttpVersion.HTTP_1_1,
            HttpResponseStatus.valueOf(response.getStatusLine().getStatusCode()));
    // write headers
    for (Header header : response.getAllHeaders()) {
      if (!RoutingHttpChannelHandler.REMOVE_HEADERS.contains(header.getName())) {
        httpResponse.setHeader(header.getName(), header.getValue());
      }
    }

    // write entity
    HttpEntity responseEntity = response.getEntity();
    byte[] responseData = EntityUtils.toByteArray(responseEntity);
    httpResponse.setContent(ChannelBuffers.copiedBuffer(responseData));
    // write response
    event.getChannel().write(httpResponse).addListener(ChannelFutureListener.CLOSE);
  }
Пример #29
0
 @Override
 public void createMiddle(LocalChannelReference lcr) throws OpenR66ProtocolPacketException {
   if (key == null) {
     throw new OpenR66ProtocolPacketException("Not enough data");
   }
   middle = ChannelBuffers.wrappedBuffer(key);
 }
Пример #30
0
  public static void serve404(
      NotFound e, ChannelHandlerContext ctx, Request request, HttpRequest nettyRequest) {
    Logger.trace("serve404: begin");
    HttpResponse nettyResponse =
        new DefaultHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.NOT_FOUND);
    nettyResponse.setHeader(SERVER, signature);

    nettyResponse.setHeader(CONTENT_TYPE, "text/html");
    Map<String, Object> binding = getBindingForErrors(e, false);

    String format = Request.current().format;
    if (format == null) {
      format = "txt";
    }
    nettyResponse.setHeader(
        CONTENT_TYPE, (MimeTypes.getContentType("404." + format, "text/plain")));

    String errorHtml = TemplateLoader.load("errors/404." + format).render(binding);
    try {
      ChannelBuffer buf = ChannelBuffers.copiedBuffer(errorHtml.getBytes("utf-8"));
      nettyResponse.setContent(buf);
      ChannelFuture writeFuture = ctx.getChannel().write(nettyResponse);
      writeFuture.addListener(ChannelFutureListener.CLOSE);
    } catch (UnsupportedEncodingException fex) {
      Logger.error(fex, "(utf-8 ?)");
    }
    Logger.trace("serve404: end");
  }