Search in sources :

Example 1 with HttpDownCallback

use of lee.study.down.dispatch.HttpDownCallback in project proxyee-down by monkeyWie.

the class HttpDownInitializer method initChannel.

@Override
protected void initChannel(Channel ch) throws Exception {
    if (bootstrap.getHttpDownInfo().getProxyConfig() != null) {
        ch.pipeline().addLast(ProxyHandleFactory.build(bootstrap.getHttpDownInfo().getProxyConfig()));
    }
    if (isSsl) {
        ch.pipeline().addLast(bootstrap.getClientSslContext().newHandler(ch.alloc()));
    }
    ch.pipeline().addLast("httpCodec", new HttpClientCodec());
    ch.pipeline().addLast(new ChannelInboundHandlerAdapter() {

        private Closeable closeable;

        private TaskInfo taskInfo = bootstrap.getHttpDownInfo().getTaskInfo();

        private HttpDownCallback callback = bootstrap.getCallback();

        @Override
        public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
            try {
                if (msg instanceof HttpContent) {
                    if (!isSucc) {
                        return;
                    }
                    HttpContent httpContent = (HttpContent) msg;
                    ByteBuf byteBuf = httpContent.content();
                    synchronized (chunkInfo) {
                        Channel nowChannel = bootstrap.getChannel(chunkInfo);
                        if (chunkInfo.getStatus() == HttpDownStatus.RUNNING && nowChannel == ctx.channel()) {
                            int readableBytes = bootstrap.doFileWriter(chunkInfo, byteBuf.nioBuffer());
                            if (readableBytes > 0) {
                                // 最后一次下载时间
                                chunkInfo.setLastDownTime(System.currentTimeMillis());
                                // 文件已下载大小
                                chunkInfo.setDownSize(chunkInfo.getDownSize() + readableBytes);
                                taskInfo.setDownSize(taskInfo.getDownSize() + readableBytes);
                                if (callback != null) {
                                    callback.onProgress(bootstrap.getHttpDownInfo(), chunkInfo);
                                }
                            }
                        } else {
                            safeClose(ctx.channel());
                            return;
                        }
                    }
                    if (isDone(chunkInfo.getDownSize(), httpContent)) {
                        LOGGER.debug("分段下载完成:channelId[" + ctx.channel().id() + "]\t" + chunkInfo);
                        bootstrap.close(chunkInfo, true);
                        // 分段下载完成回调
                        chunkInfo.setStatus(HttpDownStatus.DONE);
                        taskInfo.refresh(chunkInfo);
                        if (callback != null) {
                            callback.onChunkDone(bootstrap.getHttpDownInfo(), chunkInfo);
                        }
                        synchronized (taskInfo) {
                            if (taskInfo.getStatus() == HttpDownStatus.RUNNING && taskInfo.getChunkInfoList().stream().allMatch((chunk) -> chunk.getStatus() == HttpDownStatus.DONE)) {
                                if (!taskInfo.isSupportRange()) {
                                    // chunked编码最后更新文件大小
                                    taskInfo.setTotalSize(taskInfo.getDownSize());
                                    taskInfo.getChunkInfoList().get(0).setTotalSize(taskInfo.getDownSize());
                                }
                                // 文件下载完成回调
                                taskInfo.setStatus(HttpDownStatus.DONE);
                                LOGGER.debug("下载完成:channelId[" + ctx.channel().id() + "]\t" + chunkInfo);
                                bootstrap.close(true);
                                if (callback != null) {
                                    callback.onDone(bootstrap.getHttpDownInfo());
                                }
                            }
                        }
                    } else if (isContinue(chunkInfo.getDownSize())) {
                        // 百度响应做了手脚,会少一个字节
                        // 真实响应字节小于要下载的字节,在下载完成后要继续下载
                        LOGGER.debug("继续下载:channelId[" + ctx.channel().id() + "]\t" + chunkInfo);
                        bootstrap.retryChunkDown(chunkInfo, HttpDownStatus.CONNECTING_CONTINUE);
                    }
                } else {
                    HttpResponse httpResponse = (HttpResponse) msg;
                    Integer responseCode = httpResponse.status().code();
                    if (responseCode.toString().indexOf("20") != 0) {
                        // 应对百度近期同一时段多个连接返回400的问题
                        LOGGER.debug("响应状态码异常:" + responseCode + "\t" + chunkInfo);
                        if (responseCode == 401 || responseCode == 403 || responseCode == 404) {
                            chunkInfo.setStatus(HttpDownStatus.ERROR_WAIT_CONNECT);
                        }
                        return;
                    }
                    realContentSize = HttpDownUtil.getDownContentSize(httpResponse.headers());
                    synchronized (chunkInfo) {
                        // 判断状态是否为连接中
                        if (chunkInfo.getStatus() == HttpDownStatus.CONNECTING_NORMAL || chunkInfo.getStatus() == HttpDownStatus.CONNECTING_FAIL || chunkInfo.getStatus() == HttpDownStatus.CONNECTING_CONTINUE) {
                            LOGGER.debug("下载响应:channelId[" + ctx.channel().id() + "]\t contentSize[" + realContentSize + "]" + chunkInfo);
                            chunkInfo.setDownSize(chunkInfo.getNowStartPosition() - chunkInfo.getOriStartPosition());
                            closeable = bootstrap.initFileWriter(chunkInfo);
                            chunkInfo.setStatus(HttpDownStatus.RUNNING);
                            if (callback != null) {
                                callback.onChunkConnected(bootstrap.getHttpDownInfo(), chunkInfo);
                            }
                            isSucc = true;
                        } else {
                            safeClose(ctx.channel());
                        }
                    }
                }
            } catch (Exception e) {
                throw e;
            } finally {
                ReferenceCountUtil.release(msg);
            }
        }

        @Override
        public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
            LOGGER.error("down onChunkError:", cause);
            Channel nowChannel = bootstrap.getChannel(chunkInfo);
            safeClose(ctx.channel());
            if (nowChannel == ctx.channel()) {
                if (callback != null) {
                    callback.onChunkError(bootstrap.getHttpDownInfo(), chunkInfo, cause);
                }
                bootstrap.retryChunkDown(chunkInfo);
            }
        }

        @Override
        public void channelUnregistered(ChannelHandlerContext ctx) throws Exception {
            super.channelUnregistered(ctx);
            safeClose(ctx.channel());
        }

        private void safeClose(Channel channel) {
            try {
                HttpDownUtil.safeClose(channel, closeable);
            } catch (IOException e) {
                LOGGER.error("safeClose fail:", e);
            }
        }

        private boolean isDone(long downSize, HttpContent content) {
            return downSize == chunkInfo.getTotalSize() || (!taskInfo.isSupportRange() && content instanceof LastHttpContent);
        }

        private boolean isContinue(long downSize) {
            long downChunkSize = downSize + chunkInfo.getOriStartPosition() - chunkInfo.getNowStartPosition();
            return realContentSize == downChunkSize || (realContentSize - 1) == downChunkSize;
        }
    });
}
Also used : Closeable(java.io.Closeable) Channel(io.netty.channel.Channel) HttpResponse(io.netty.handler.codec.http.HttpResponse) ChannelHandlerContext(io.netty.channel.ChannelHandlerContext) IOException(java.io.IOException) HttpClientCodec(io.netty.handler.codec.http.HttpClientCodec) ByteBuf(io.netty.buffer.ByteBuf) LastHttpContent(io.netty.handler.codec.http.LastHttpContent) IOException(java.io.IOException) TaskInfo(lee.study.down.model.TaskInfo) HttpDownCallback(lee.study.down.dispatch.HttpDownCallback) HttpContent(io.netty.handler.codec.http.HttpContent) LastHttpContent(io.netty.handler.codec.http.LastHttpContent) ChannelInboundHandlerAdapter(io.netty.channel.ChannelInboundHandlerAdapter)

Example 2 with HttpDownCallback

use of lee.study.down.dispatch.HttpDownCallback in project proxyee-down by monkeyWie.

the class HttpDownController method doUpdate.

@RequestMapping("/doUpdate")
public ResultInfo doUpdate() throws Exception {
    ResultInfo resultInfo = new ResultInfo();
    if (updateInfo == null) {
        resultInfo.setStatus(ResultStatus.BAD.getCode()).setMsg("没有可用版本进行更新");
        return resultInfo;
    }
    if (updateBootstrap != null) {
        updateBootstrap.close();
        updateBootstrap = null;
    }
    try {
        updateBootstrap = updateService.update(updateInfo, new HttpDownCallback() {

            @Override
            public void onDone(HttpDownInfo httpDownInfo) throws Exception {
                String zipPath = httpDownInfo.getTaskInfo().buildTaskFilePath();
                String unzipDir = "proxyee-down-" + updateInfo.getVersionStr();
                String unzipPath = unzipDir + "/main/proxyee-down-core.jar";
                // 下载完解压
                FileUtil.unzip(zipPath, null, unzipPath);
                // 复制出来
                Files.copy(Paths.get(httpDownInfo.getTaskInfo().getFilePath() + File.separator + unzipPath), Paths.get(httpDownInfo.getTaskInfo().getFilePath() + File.separator + "proxyee-down-core.jar.bak"));
                // 删除临时的文件
                FileUtil.deleteIfExists(zipPath);
                FileUtil.deleteIfExists(httpDownInfo.getTaskInfo().getFilePath() + File.separator + unzipDir);
                // 通知客户端
                ContentManager.WS.sendMsg(new WsForm(WsDataType.UPDATE_PROGRESS, httpDownInfo.getTaskInfo()));
                // 清空更新下载信息
                updateBootstrap = null;
            }
        });
    } catch (TimeoutException e) {
        resultInfo.setStatus(ResultStatus.BAD.getCode()).setMsg("检测更新超时,请重试");
        return resultInfo;
    }
    return resultInfo;
}
Also used : HttpDownCallback(lee.study.down.dispatch.HttpDownCallback) WsForm(lee.study.down.mvc.form.WsForm) ResultInfo(lee.study.down.model.ResultInfo) HttpDownInfo(lee.study.down.model.HttpDownInfo) TimeoutException(java.util.concurrent.TimeoutException) RequestMapping(org.springframework.web.bind.annotation.RequestMapping)

Aggregations

HttpDownCallback (lee.study.down.dispatch.HttpDownCallback)2 ByteBuf (io.netty.buffer.ByteBuf)1 Channel (io.netty.channel.Channel)1 ChannelHandlerContext (io.netty.channel.ChannelHandlerContext)1 ChannelInboundHandlerAdapter (io.netty.channel.ChannelInboundHandlerAdapter)1 HttpClientCodec (io.netty.handler.codec.http.HttpClientCodec)1 HttpContent (io.netty.handler.codec.http.HttpContent)1 HttpResponse (io.netty.handler.codec.http.HttpResponse)1 LastHttpContent (io.netty.handler.codec.http.LastHttpContent)1 Closeable (java.io.Closeable)1 IOException (java.io.IOException)1 TimeoutException (java.util.concurrent.TimeoutException)1 HttpDownInfo (lee.study.down.model.HttpDownInfo)1 ResultInfo (lee.study.down.model.ResultInfo)1 TaskInfo (lee.study.down.model.TaskInfo)1 WsForm (lee.study.down.mvc.form.WsForm)1 RequestMapping (org.springframework.web.bind.annotation.RequestMapping)1