/** Example of reading request by chunk and getting values from chunk to chunk */
 private void readHttpDataChunkByChunk() {
   try {
     while (decoder.hasNext()) {
       InterfaceHttpData data = decoder.next();
       if (data != null) {
         // check if current HttpData is a FileUpload and previously set as partial
         if (partialContent == data) {
           logger.info(" 100% (FinalSize: " + partialContent.length() + ")");
           partialContent = null;
         }
         try {
           // new value
           writeHttpData(data);
         } finally {
           data.release();
         }
       }
     }
     // Check partial decoding for a FileUpload
     InterfaceHttpData data = decoder.currentPartialHttpData();
     if (data != null) {
       StringBuilder builder = new StringBuilder();
       if (partialContent == null) {
         partialContent = (HttpData) data;
         if (partialContent instanceof FileUpload) {
           builder
               .append("Start FileUpload: ")
               .append(((FileUpload) partialContent).getFilename())
               .append(" ");
         } else {
           builder.append("Start Attribute: ").append(partialContent.getName()).append(" ");
         }
         builder.append("(DefinedSize: ").append(partialContent.definedLength()).append(")");
       }
       if (partialContent.definedLength() > 0) {
         builder
             .append(" ")
             .append(partialContent.length() * 100 / partialContent.definedLength())
             .append("% ");
         logger.info(builder.toString());
       } else {
         builder.append(" ").append(partialContent.length()).append(" ");
         logger.info(builder.toString());
       }
     }
   } catch (EndOfDataDecoderException e1) {
     // end
     responseContent.append("\r\n\r\nEND OF CONTENT CHUNK BY CHUNK\r\n\r\n");
   }
 }
  private void reset() {
    request = null;

    // destroy the decoder to release all resources
    decoder.destroy();
    decoder = null;
  }
 /** Example of reading request by chunk and getting values from chunk to chunk */
 private void readHttpDataChunkByChunk() {
   try {
     while (decoder.hasNext()) {
       InterfaceHttpData data = decoder.next();
       if (data != null) {
         try {
           // new value
           writeHttpData(data);
         } finally {
           data.release();
         }
       }
     }
   } catch (EndOfDataDecoderException e1) {
     // end
     responseContent.append("\r\n\r\nEND OF CONTENT CHUNK BY CHUNK\r\n\r\n");
   }
 }
 private void readPostData() {
   try {
     decoder = new HttpPostRequestDecoder(factory, request); // 1
     for (InterfaceHttpData data : decoder.getBodyHttpDatas()) {
       if (HttpDataType.Attribute == data.getHttpDataType()) {
         try {
           Attribute attribute = (Attribute) data; // 2
           reqData.put(attribute.getName(), attribute.getValue()); // 3
         } catch (IOException e) {
           System.out.println("BODY Attribute: " + data.getHttpDataType().name() + e);
         }
       } else {
         System.out.println("BODY data : " + data.getHttpDataType().name() + ":" + data);
       }
     }
   } catch (ErrorDataDecoderException e) {
     System.out.println(e);
   } finally {
     if (decoder != null) {
       decoder.destroy();
     }
   }
 }
  @Override
  public void messageReceived(ChannelHandlerContext ctx, HttpObject msg) throws Exception {
    if (msg instanceof HttpRequest) {
      HttpRequest request = this.request = (HttpRequest) msg;
      URI uri = new URI(request.uri());
      if (!uri.getPath().startsWith("/form")) {
        // Write Menu
        writeMenu(ctx);
        return;
      }
      responseContent.setLength(0);
      responseContent.append("WELCOME TO THE WILD WILD WEB SERVER\r\n");
      responseContent.append("===================================\r\n");

      responseContent.append("VERSION: " + request.protocolVersion().text() + "\r\n");

      responseContent.append("REQUEST_URI: " + request.uri() + "\r\n\r\n");
      responseContent.append("\r\n\r\n");

      // new getMethod
      for (Entry<CharSequence, CharSequence> entry : request.headers()) {
        responseContent.append("HEADER: " + entry.getKey() + '=' + entry.getValue() + "\r\n");
      }
      responseContent.append("\r\n\r\n");

      // new getMethod
      Set<Cookie> cookies;
      String value = request.headers().getAndConvert(HttpHeaderNames.COOKIE);
      if (value == null) {
        cookies = Collections.emptySet();
      } else {
        cookies = ServerCookieDecoder.STRICT.decode(value);
      }
      for (Cookie cookie : cookies) {
        responseContent.append("COOKIE: " + cookie + "\r\n");
      }
      responseContent.append("\r\n\r\n");

      QueryStringDecoder decoderQuery = new QueryStringDecoder(request.uri());
      Map<String, List<String>> uriAttributes = decoderQuery.parameters();
      for (Entry<String, List<String>> attr : uriAttributes.entrySet()) {
        for (String attrVal : attr.getValue()) {
          responseContent.append("URI: " + attr.getKey() + '=' + attrVal + "\r\n");
        }
      }
      responseContent.append("\r\n\r\n");

      // if GET Method: should not try to create a HttpPostRequestDecoder
      if (request.method().equals(HttpMethod.GET)) {
        // GET Method: should not try to create a HttpPostRequestDecoder
        // So stop here
        responseContent.append("\r\n\r\nEND OF GET CONTENT\r\n");
        // Not now: LastHttpContent will be sent writeResponse(ctx.channel());
        return;
      }
      try {
        decoder = new HttpPostRequestDecoder(factory, request);
      } catch (ErrorDataDecoderException e1) {
        e1.printStackTrace();
        responseContent.append(e1.getMessage());
        writeResponse(ctx.channel());
        ctx.channel().close();
        return;
      }

      readingChunks = HttpHeaderUtil.isTransferEncodingChunked(request);
      responseContent.append("Is Chunked: " + readingChunks + "\r\n");
      responseContent.append("IsMultipart: " + decoder.isMultipart() + "\r\n");
      if (readingChunks) {
        // Chunk version
        responseContent.append("Chunks: ");
        readingChunks = true;
      }
    }

    // check if the decoder was constructed before
    // if not it handles the form get
    if (decoder != null) {
      if (msg instanceof HttpContent) {
        // New chunk is received
        HttpContent chunk = (HttpContent) msg;
        try {
          decoder.offer(chunk);
        } catch (ErrorDataDecoderException e1) {
          e1.printStackTrace();
          responseContent.append(e1.getMessage());
          writeResponse(ctx.channel());
          ctx.channel().close();
          return;
        }
        responseContent.append('o');
        // example of reading chunk by chunk (minimize memory usage due to
        // Factory)
        readHttpDataChunkByChunk();
        // example of reading only if at the end
        if (chunk instanceof LastHttpContent) {
          writeResponse(ctx.channel());
          readingChunks = false;

          reset();
        }
      }
    } else {
      writeResponse(ctx.channel());
    }
  }
 @Override
 public void channelInactive(ChannelHandlerContext ctx) throws Exception {
   if (decoder != null) {
     decoder.cleanFiles();
   }
 }
  @Override
  public void channelRead0(ChannelHandlerContext ctx, HttpObject msg) throws Exception {
    try {
      if (msg instanceof FullHttpRequest) {
        FullHttpRequest fullRequest = (FullHttpRequest) msg;
        if (fullRequest.getUri().startsWith("/kctupload")) {

          if (fullRequest.getMethod().equals(HttpMethod.GET)) {
            // HTTP Get request!
            // Write the HTML page with the form
            writeMenu(ctx);
          } else if (fullRequest.getMethod().equals(HttpMethod.POST)) {
            /*
                 * HTTP Post request! Handle the uploaded form
                 * HTTP parameters:

            /kctupload
            username (should match player's Minecraft name)
            language (java, python, etc)
            jsonfile (a file upload, or empty)
            sourcefile (a file upload, or empty)
            jsontext (a JSON string, or empty)
            sourcetext (code as a String, or empty)
                 */

            String language = null;
            String playerName = null;
            String client = null;
            String jsonText = null;
            String sourceText = null;
            Map<String, UploadedFile> files = new LinkedHashMap<String, UploadedFile>();

            HttpPostRequestDecoder decoder = new HttpPostRequestDecoder(fullRequest);
            try {
              logger.trace("is multipart? " + decoder.isMultipart());
              while (decoder.hasNext()) {
                InterfaceHttpData data = decoder.next();
                if (data == null) continue;

                try {
                  if (data.getHttpDataType() == HttpDataType.Attribute) {
                    Attribute attribute = (Attribute) data;
                    String name = attribute.getName();
                    String value = attribute.getValue();
                    logger.trace(String.format("http attribute: %s => %s", name, value));
                    if (name.equals("language")) {
                      language = value;
                    } else if (name.equals("playerName")) {
                      playerName = value;
                    } else if (name.equals("client")) {
                      client = value;
                    } else if (name.equals("jsontext")) {
                      jsonText = value;
                    } else if (name.equals("sourcetext")) {
                      sourceText = value;
                    } else {
                      logger.warn(
                          String.format("Unknown kctupload attribute: %s => %s", name, value));
                    }
                  } else if (data.getHttpDataType() == HttpDataType.FileUpload) {
                    // Handle file upload
                    // We may have json, source, or both
                    FileUpload fileUpload = (FileUpload) data;
                    logger.debug(
                        String.format(
                            "http file upload name %s, filename: ",
                            data.getName(), fileUpload.getFilename()));
                    String filename = fileUpload.getFilename();
                    ByteBuf buf = fileUpload.getByteBuf();
                    String fileBody = new String(buf.array(), "UTF-8");
                    files.put(data.getName(), new UploadedFile(filename, fileBody));
                  }
                } finally {
                  data.release();
                }
              }
            } finally {
              if (decoder != null) {
                // clean up resources
                decoder.cleanFiles();
                decoder.destroy();
              }
            }

            /*
             * Error checking here makes the most sense, since we can send back a reasonable error message
             * to the uploading client at this point. Makes less sense to wait to compile.
             *
             * Upload possibilities:
             *
             * bluej: file1, file2, etc. All source code. Language should be set to Java.
             * Convert to JSON, then to KCTScript. Signal an error if one happens.
             *
             * web: jsontext and/or sourcetext. json-only is OK; source-only is OK if it's Java.
             * Cannot send source-only for non-Java languages, since we can't build them (yet).
             *
             * anything else: convert to Json and hope for the best
             */
            try {
              KCTUploadHook hook = new KCTUploadHook();
              StringBuilder res = new StringBuilder();

              if (playerName == null || playerName.equals("")) {
                // XXX How do we know that the playerName is valid?
                // TODO: authenticate against Mojang's server?
                throw new TurtleException("You must specify your MineCraft player name!");
              }

              if (client == null) {
                throw new TurtleException(
                    "Your uploading and submission system must specify "
                        + "the type of client used for the upload (i.e. bluej, web, pykc, etc)");
              }

              hook.setPlayerName(playerName);
              res.append(
                  String.format("Hello %s! Thanks for using KnoxCraft Turtles\n\n", playerName));

              TurtleCompiler turtleCompiler = new TurtleCompiler(logger);
              int success = 0;
              int failure = 0;
              if (client.equalsIgnoreCase("web")
                  || client.equalsIgnoreCase("testclient")
                  || client.startsWith("pykc")) {
                // WEB OR PYTHON UPLOAD
                logger.trace("Upload from web");
                // must have both Json and source, either in text area or as uploaded files
                // XXX Conlfict of comments of the top and here??? What do we need both/ only JSon?
                // Is there a want we want, thus forcing it
                if (sourceText != null && jsonText != null) {
                  KCTScript script = turtleCompiler.parseFromJson(jsonText);
                  script.setLanguage(language);
                  script.setSourceCode(sourceText);
                  res.append(
                      String.format(
                          "Successfully uploaded KnoxCraft Turtle program "
                              + "named %s, in programming language %s\n",
                          script.getScriptName(), script.getLanguage()));
                  success++;
                  hook.addScript(script);
                } else if (files.containsKey("jsonfile") && files.containsKey("sourcefile")) {
                  UploadedFile sourceUpload = files.get("sourcefile");
                  UploadedFile jsonUpload = files.get("jsonfile");
                  KCTScript script = turtleCompiler.parseFromJson(jsonUpload.body);
                  script.setLanguage(language);
                  script.setSourceCode(sourceUpload.body);
                  res.append(
                      String.format(
                          "Successfully uploaded KnoxCraft Turtle program "
                              + "named %s, in programming language %s\n",
                          script.getScriptName(), script.getLanguage()));
                  success++;
                  hook.addScript(script);
                } else {
                  throw new TurtleException(
                      "You must upload BOTH json and the corresponding source code "
                          + " (either as files or pasted into the text areas)");
                }
              } else if ("bluej".equalsIgnoreCase(client)) {
                // BLUEJ UPLOAD
                logger.trace("Upload from bluej");
                for (Entry<String, UploadedFile> entry : files.entrySet()) {
                  try {
                    UploadedFile uploadedFile = entry.getValue();
                    res.append(
                        String.format(
                            "Trying to upload and compile file %s\n", uploadedFile.filename));
                    logger.trace(
                        String.format(
                            "Trying to upload and compile file %s\n", uploadedFile.filename));
                    KCTScript script =
                        turtleCompiler.compileJavaTurtleCode(
                            uploadedFile.filename, uploadedFile.body);
                    logger.trace("Returned KCTScript (it's JSON is): " + script.toJSONString());
                    hook.addScript(script);
                    res.append(
                        String.format(
                            "Successfully uploaded file %s and compiled KnoxCraft Turtle program "
                                + "named %s in programming language %s\n\n",
                            uploadedFile.filename, script.getScriptName(), script.getLanguage()));
                    success++;
                  } catch (TurtleCompilerException e) {
                    logger.warn("Unable to compile Turtle code", e);
                    res.append(String.format("%s\n\n", e.getMessage()));
                    failure++;
                  } catch (TurtleException e) {
                    logger.error("Error in compiling (possibly a server side error)", e);
                    res.append(
                        String.format("Unable to process Turtle code %s\n\n", e.getMessage()));
                    failure++;
                  } catch (Exception e) {
                    logger.error("Unexpected error compiling Turtle code to KCTScript", e);
                    failure++;
                    res.append(String.format("Failed to load script %s\n", entry.getKey()));
                  }
                }
              } else {
                // UNKNOWN CLIENT UPLOAD
                // TODO Unknown client; make a best effort to handle upload
                res.append(
                    String.format(
                        "Unknown upload client: %s; making our best effort to handle the upload"));
              }

              res.append(
                  String.format(
                      "\nSuccessfully uploaded %d KnoxCraft Turtles programs\n", success));
              if (failure > 0) {
                res.append(
                    String.format("\nFailed to upload %d KnoxCraft Turtles programs\n", failure));
              }
              Canary.hooks().callHook(hook);
              writeResponse(ctx.channel(), fullRequest, res.toString(), client);
            } catch (TurtleException e) {
              // XXX can this still happen? Don't we catch all of these?
              writeResponse(ctx.channel(), fullRequest, e.getMessage(), "error");
            }
          }
        }
      }
    } catch (Exception e) {
      logger.error("Internal Server Error: Channel error", e);
      throw e;
    }
  }