Example usage for io.netty.handler.codec.http.multipart HttpPostRequestDecoder destroy

List of usage examples for io.netty.handler.codec.http.multipart HttpPostRequestDecoder destroy

Introduction

In this page you can find the example usage for io.netty.handler.codec.http.multipart HttpPostRequestDecoder destroy.

Prototype

@Override
    public void destroy() 

Source Link

Usage

From source file:com.android.tools.idea.diagnostics.crash.GoogleCrashTest.java

License:Apache License

@Ignore
@Test//from w ww  .j a  va2  s .  co m
public void checkServerReceivesPostedData() throws Exception {
    String expectedReportId = "deadcafe";
    Map<String, String> attributes = new ConcurrentHashMap<>();

    myTestServer.setResponseSupplier(httpRequest -> {
        if (httpRequest.method() != HttpMethod.POST) {
            return new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.BAD_REQUEST);
        }

        HttpPostRequestDecoder requestDecoder = new HttpPostRequestDecoder(httpRequest);
        try {
            for (InterfaceHttpData httpData : requestDecoder.getBodyHttpDatas()) {
                if (httpData instanceof Attribute) {
                    Attribute attr = (Attribute) httpData;
                    attributes.put(attr.getName(), attr.getValue());
                }
            }
        } catch (IOException e) {
            return new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.BAD_REQUEST);
        } finally {
            requestDecoder.destroy();
        }

        return new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK,
                Unpooled.wrappedBuffer(expectedReportId.getBytes(UTF_8)));
    });

    CrashReport report = CrashReport.Builder.createForException(ourIndexNotReadyException)
            .setProduct("AndroidStudioTestProduct").setVersion("1.2.3.4").build();
    CompletableFuture<String> reportId = myReporter.submit(report);

    assertEquals(expectedReportId, reportId.get());

    // assert that the server get the expected data
    assertEquals("AndroidStudioTestProduct", attributes.get(GoogleCrash.KEY_PRODUCT_ID));
    assertEquals("1.2.3.4", attributes.get(GoogleCrash.KEY_VERSION));

    // Note: the exception message should have been elided
    assertEquals("com.intellij.openapi.project.IndexNotReadyException: <elided>\n" + STACK_TRACE,
            attributes.get(GoogleCrash.KEY_EXCEPTION_INFO));

    List<String> descriptions = Arrays.asList("2.3.0.0\n1.8.0_73-b02", "2.3.0.1\n1.8.0_73-b02");
    report = CrashReport.Builder.createForCrashes(descriptions).setProduct("AndroidStudioTestProduct")
            .setVersion("1.2.3.4").build();

    attributes.clear();

    reportId = myReporter.submit(report);
    assertEquals(expectedReportId, reportId.get());

    // check that the crash count and descriptions made through
    assertEquals(descriptions.size(), Integer.parseInt(attributes.get("numCrashes")));
    assertEquals("2.3.0.0\n1.8.0_73-b02\n\n2.3.0.1\n1.8.0_73-b02", attributes.get("crashDesc"));

    Path testData = Paths.get(AndroidTestBase.getTestDataPath());
    List<String> threadDump = Files.readAllLines(testData.resolve(Paths.get("threadDumps", "1.txt")), UTF_8);
    report = CrashReport.Builder.createForPerfReport("td.txt", Joiner.on('\n').join(threadDump)).build();

    attributes.clear();

    reportId = myReporter.submit(report);
    assertEquals(expectedReportId, reportId.get());
    assertEquals(threadDump.stream().collect(Collectors.joining("\n")), attributes.get("td.txt"));
}

From source file:com.stremebase.examples.todomvc.HttpRouter.java

License:Apache License

private static HttpResponse createResponse(HttpRequest req, Router<Integer> router) {
    RouteResult<Integer> routeResult = router.route(req.getMethod(), req.getUri());

    Integer request = routeResult.target();

    String data = "";
    String mimeType = "";

    if (request == CSS) {
        data = Todo.getCss();//from ww  w.j  a va 2 s.c  o m
        mimeType = "text/css";
    } else if (request == ICON) {
        mimeType = "image/x-icon";
    } else if (request == GET) {
        data = Todo.get();
        mimeType = "text/html";
    } else if (request == FILTER) {
        data = Todo.filter(routeResult.pathParams().get("filtertype"));
        mimeType = "text/html";
    } else if (req.getMethod().equals(HttpMethod.POST)) {
        HttpPostRequestDecoder decoder = new HttpPostRequestDecoder(new DefaultHttpDataFactory(false), req);

        Attribute attribute;

        String item_text = null;
        InterfaceHttpData httpData = decoder.getBodyHttpData("item-text");
        if (httpData != null) {
            attribute = (Attribute) httpData;
            try {
                item_text = attribute.getValue();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

        String item_id = null;
        httpData = decoder.getBodyHttpData("item-id");
        if (httpData != null) {
            attribute = (Attribute) httpData;
            try {
                item_id = attribute.getValue();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

        if (request == POST) {
            if (item_id == null)
                data = Todo.create(item_text);
            else
                data = Todo.save(Long.valueOf(item_id), item_text);
        } else if (request == DELETE) {
            data = Todo.delete(Long.valueOf(item_id));
        } else if (request == DELETECOMPLETED) {
            data = Todo.clearCompleted();
        } else if (request == TOGGLESTATUS)
            data = Todo.toggleStatus(Long.valueOf(item_id));

        mimeType = "text/html";
        decoder.destroy();
    }

    FullHttpResponse res;

    if (request == NOTFOUND) {
        res = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.TEMPORARY_REDIRECT);
        res.headers().add(HttpHeaders.Names.LOCATION, "/");
        return res;
    }

    if (request == ICON)
        res = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK,
                Unpooled.copiedBuffer(Todo.favicon));
    else
        res = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK,
                Unpooled.copiedBuffer(data, CharsetUtil.UTF_8));

    res.headers().set(HttpHeaders.Names.CONTENT_TYPE, mimeType);
    res.headers().set(HttpHeaders.Names.CONTENT_LENGTH, res.content().readableBytes());
    if (request == CSS || request == ICON)
        setDateAndCacheHeaders(res);

    return res;
}

From source file:firebats.http.server.exts.form.Form.java

License:Apache License

private static Form decodeWithContent(Context context, ByteBuf content) {
    //new DefaultHttpDataFactory(/*useDisk*/true)decoder?
    //Mix?16K???//from  ww w .j av a 2  s . co m
    //       final HttpPostRequestDecoder decoder = new HttpPostRequestDecoder(new DefaultHttpDataFactory(/*useDisk*/true),toNettyHttpRequest(context.request));
    HttpServerRequest<ByteBuf> rxRequest = context.getRequest();
    HttpRequest nettyRequest = new DefaultHttpRequest(HttpVersion.HTTP_1_1, rxRequest.getHttpMethod(),
            rxRequest.getUri());
    for (Map.Entry<String, String> header : rxRequest.getHeaders().entries()) {
        nettyRequest.headers().add(header.getKey(), header.getValue());
    }
    final HttpPostRequestDecoder decoder = new HttpPostRequestDecoder(nettyRequest);
    HttpContent httpContent = new DefaultHttpContent(content);
    decoder.offer(httpContent);
    decoder.offer(LastHttpContent.EMPTY_LAST_CONTENT);

    Map<String, String> formParams = new LinkedHashMap<>();
    Map<String, UploadedFile> files = new LinkedHashMap<>();
    try {
        while (decoder.hasNext()) {
            InterfaceHttpData data = decoder.next();
            if (data.getHttpDataType().equals(InterfaceHttpData.HttpDataType.Attribute)) {
                try {
                    Attribute attr = (Attribute) data;
                    if (!formParams.containsKey(data.getName())) {
                        formParams.put(attr.getName(), attr.getValue());
                    }
                } catch (IOException e) {
                    Throwables.propagate(e);
                } finally {
                    //?
                    data.release();
                }
            } else if (data.getHttpDataType().equals(InterfaceHttpData.HttpDataType.FileUpload)) {
                try {
                    if (!files.containsKey(data.getName())) {
                        final FileUpload nettyFileUpload = (FileUpload) data;
                        final ByteBuf byteBuf = nettyFileUpload.content();
                        byteBuf.retain();
                        context.onComplete(new Action0() {
                            @Override
                            public void call() {
                                if (log.isDebugEnabled()) {
                                    log.debug("form upload file release[" + data.getName() + ":"
                                            + nettyFileUpload.getFilename() + "]");
                                }
                                byteBuf.release();
                            }
                        });
                        UploadedFile fileUpload = new UploadedFile(nettyFileUpload.getFilename(),
                                nettyFileUpload.getContentType(), byteBuf);
                        files.put(data.getName(), fileUpload);
                    }
                } finally {
                    data.release();
                }
            }
        }
    } catch (HttpPostRequestDecoder.EndOfDataDecoderException ignore) {
        // ignore
    } finally {
        decoder.destroy();
    }
    Map<String, String> query = Form.toFlatQueryParams(context.getRequest().getQueryParameters());
    return fromAll(query, formParams, files);
}

From source file:nikoladasm.aspark.server.ServerHandler.java

License:Open Source License

private Map<String, List<String>> getPostAttributes(HttpMethod requestMethod, FullHttpRequest request) {
    final Map<String, List<String>> map = new HashMap<String, List<String>>();
    if (!requestMethod.equals(POST))
        return map;
    if (!isDecodeableContent(request.headers().get(CONTENT_TYPE)))
        return map;
    final HttpPostRequestDecoder decoder = new HttpPostRequestDecoder(request);
    try {//  w  w  w.  j a  va  2 s.  c o m
        for (InterfaceHttpData data : decoder.getBodyHttpDatas()) {
            if (data.getHttpDataType() == HttpDataType.Attribute) {
                Attribute attribute = (Attribute) data;
                List<String> list = map.get(attribute.getName());
                if (list == null) {
                    list = new LinkedList<String>();
                    map.put(attribute.getName(), list);
                }
                list.add(attribute.getValue());
            }
        }
    } catch (IOException e) {
        throw new IllegalStateException("Cannot parse http request data", e);
    } finally {
        decoder.destroy();
    }
    return Collections.unmodifiableMap(map);
}

From source file:org.apache.hyracks.http.server.FormUrlEncodedRequest.java

License:Apache License

public static IServletRequest create(FullHttpRequest request) throws IOException {
    List<String> names = new ArrayList<>();
    List<String> values = new ArrayList<>();
    HttpPostRequestDecoder decoder = new HttpPostRequestDecoder(request);
    try {//from   w w w .j ava 2s  .  c  o  m
        List<InterfaceHttpData> bodyHttpDatas = decoder.getBodyHttpDatas();
        for (InterfaceHttpData data : bodyHttpDatas) {
            if (data.getHttpDataType().equals(InterfaceHttpData.HttpDataType.Attribute)) {
                Attribute attr = (MixedAttribute) data;
                names.add(data.getName());
                values.add(attr.getValue());
            }
        }
    } finally {
        decoder.destroy();
    }
    return new FormUrlEncodedRequest(request, new QueryStringDecoder(request.uri()).parameters(), names,
            values);
}

From source file:org.apache.hyracks.http.server.PostRequest.java

License:Apache License

public static IServletRequest create(FullHttpRequest request) throws IOException {
    List<String> names = new ArrayList<>();
    List<String> values = new ArrayList<>();
    HttpPostRequestDecoder decoder = null;
    try {//from   www  .j a va 2s.com
        decoder = new HttpPostRequestDecoder(request);
    } catch (Exception e) {
        //ignore. this means that the body of the POST request does not have key value pairs
        LOGGER.log(Level.WARNING,
                "Failed to decode a post message. Fix the API not to have queries as POST body", e);
    }
    if (decoder != null) {
        try {
            List<InterfaceHttpData> bodyHttpDatas = decoder.getBodyHttpDatas();
            for (InterfaceHttpData data : bodyHttpDatas) {
                if (data.getHttpDataType().equals(InterfaceHttpData.HttpDataType.Attribute)) {
                    Attribute attr = (MixedAttribute) data;
                    names.add(data.getName());
                    values.add(attr.getValue());
                }
            }
        } finally {
            decoder.destroy();
        }
    }
    return new PostRequest(request, new QueryStringDecoder(request.uri()).parameters(), names, values);
}

From source file:org.apache.hyracks.http.server.util.ServletUtils.java

License:Apache License

public static IServletRequest post(FullHttpRequest request) throws IOException {
    List<String> names = new ArrayList<>();
    List<String> values = new ArrayList<>();
    HttpPostRequestDecoder decoder = null;
    try {//from w w w  .j  a  v a  2 s. com
        decoder = new HttpPostRequestDecoder(request);
    } catch (Exception e) {
        //ignore. this means that the body of the POST request does not have key value pairs
        LOGGER.log(Level.WARNING,
                "Failed to decode a post message. Fix the API not to have queries as POST body", e);
    }
    if (decoder != null) {
        try {
            List<InterfaceHttpData> bodyHttpDatas = decoder.getBodyHttpDatas();
            for (InterfaceHttpData data : bodyHttpDatas) {
                if (data.getHttpDataType().equals(HttpDataType.Attribute)) {
                    Attribute attr = (MixedAttribute) data;
                    names.add(data.getName());
                    values.add(attr.getValue());
                }
            }
        } finally {
            decoder.destroy();
        }
    }
    return new PostRequest(request, new QueryStringDecoder(request.uri()).parameters(), names, values);
}

From source file:org.jboss.arquillian.warp.impl.client.execution.HttpRequestWrapper.java

License:Apache License

@Override
public Map<String, List<String>> getHttpDataAttributes() {

    if (!HttpMethod.POST.equals(getMethod())) {
        throw new IllegalArgumentException("Cannot parse HttpData for requests other than POST");
    }// w w w. j  a  v  a  2  s  . c o m

    try {
        if (httpDataAttributes == null) {
            final HttpPostRequestDecoder decoder = new HttpPostRequestDecoder(new DefaultHttpDataFactory(false),
                    request);
            final Map<String, List<String>> map = new HashMap<String, List<String>>();

            try {
                for (InterfaceHttpData data : decoder.getBodyHttpDatas()) {
                    if (data.getHttpDataType() == HttpDataType.Attribute) {
                        Attribute attribute = (Attribute) data;

                        List<String> list = map.get(attribute.getName());
                        if (list == null) {
                            list = new LinkedList<String>();
                            map.put(attribute.getName(), list);
                        }

                        list.add(attribute.getValue());
                    }
                }
            } finally {
                decoder.destroy();
            }

            httpDataAttributes = map;
        }

        return Collections.unmodifiableMap(httpDataAttributes);

    } catch (IOException e) {
        throw new IllegalStateException("Cannot parse http request data", e);
    }
}

From source file:org.jooby.internal.netty.NettyRequest.java

License:Apache License

private Multimap<String, String> decodeParams() throws IOException {
    if (params == null) {
        params = ArrayListMultimap.create();
        files = ArrayListMultimap.create();

        query.parameters().forEach((name, values) -> values.forEach(value -> params.put(name, value)));

        HttpMethod method = req.method();
        boolean hasBody = method.equals(HttpMethod.POST) || method.equals(HttpMethod.PUT)
                || method.equals(HttpMethod.PATCH);
        boolean formLike = false;
        if (req.headers().contains("Content-Type")) {
            String contentType = req.headers().get("Content-Type").toLowerCase();
            formLike = (contentType.startsWith(MediaType.multipart.name())
                    || contentType.startsWith(MediaType.form.name()));
        }/*from   ww w .  ja  v  a2  s. c om*/
        if (hasBody && formLike) {
            HttpPostRequestDecoder decoder = new HttpPostRequestDecoder(new DefaultHttpDataFactory(), req);
            try {
                Function<HttpPostRequestDecoder, Boolean> hasNext = it -> {
                    try {
                        return it.hasNext();
                    } catch (HttpPostRequestDecoder.EndOfDataDecoderException ex) {
                        return false;
                    }
                };
                while (hasNext.apply(decoder)) {
                    HttpData field = (HttpData) decoder.next();
                    try {
                        String name = field.getName();
                        if (field.getHttpDataType() == HttpDataType.FileUpload) {
                            files.put(name, new NettyUpload((FileUpload) field, tmpdir));
                        } else {
                            params.put(name, field.getString());
                        }
                    } finally {
                        field.release();
                    }
                }
            } finally {
                decoder.destroy();
            }
        }
    }
    return params;
}

From source file:org.knoxcraft.netty.server.HttpUploadServerHandler.java

License:Apache License

@Override
public void channelRead0(ChannelHandlerContext ctx, HttpObject msg) throws Exception {
    try {// w w  w . ja v  a2  s  .  com
        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;
    }
}