List of usage examples for io.netty.buffer ByteBuf retain
@Override public abstract ByteBuf retain();
From source file:divconq.api.LocalSession.java
License:Open Source License
@Override public void sendStream(ScatteringByteChannel in, long size, long offset, final String channelid, final OperationCallback callback) { final DataStreamChannel chan = this.session.getChannel(channelid); if (chan == null) { callback.error(1, "Missing channel"); callback.complete();/*from w ww . jav a 2 s . c om*/ return; } chan.setDriver(new IStreamDriver() { @Override public void cancel() { callback.error(1, "Transfer canceled"); chan.complete(); callback.complete(); } @Override public void message(StreamMessage msg) { if (msg.isFinal()) { System.out.println("Final on channel: " + channelid); chan.complete(); callback.complete(); } } @Override public void nextChunk() { // won't chunk so won't happen here } }); long sent = offset; int seq = 0; if (size > 0) { callback.getContext().setAmountCompleted((int) (sent * 100 / size)); chan.getContext().setAmountCompleted((int) (sent * 100 / size)); // keep the channel active so it does not timeout } try { ByteBuf bb = Hub.instance.getBufferAllocator().directBuffer(64 * 1024); long toskip = offset; if (in instanceof SeekableByteChannel) { ((SeekableByteChannel) in).position(toskip); } else { while (toskip > 0) { int skip = (int) Math.min(bb.capacity(), toskip); toskip -= bb.writeBytes(in, skip); bb.clear(); } } chan.touch(); // now start writing the upload int amt = bb.writeBytes(in, bb.capacity()); while (amt != -1) { bb.retain(); // this ups ref cnt to 2 - we plan to reuse the buffer StreamMessage b = new StreamMessage("Block", bb); b.setField("Sequence", seq); OperationResult sr = chan.send(b); if (sr.hasErrors()) { chan.close(); break; } seq++; sent += amt; if (size > 0) { callback.getContext().setAmountCompleted((int) (sent * 100 / size)); chan.getContext().setAmountCompleted((int) (sent * 100 / size)); // keep the channel active so it does not timeout } callback.touch(); chan.touch(); // by the time we get here, that buffer has been used up and we can use it for the next buffer if (bb.refCnt() != 1) throw new IOException("Buffer reference count is not correct"); // stop writing if canceled if (chan.isClosed()) break; bb.clear(); amt = bb.writeBytes(in, bb.capacity()); } // we are now done with it bb.release(); // final only if not canceled if (!chan.isClosed()) chan.send(MessageUtil.streamFinal()); } catch (IOException x) { callback.error(1, "Local read error: " + x); chan.send(MessageUtil.streamError(1, "Source read error: " + x)); chan.close(); callback.complete(); } finally { try { in.close(); } catch (IOException x) { } } }
From source file:divconq.bus.net.StreamDecoder.java
License:Open Source License
@Override protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception { if ((in instanceof EmptyByteBuf) || (in.readableBytes() == 0)) return;/* w w w . j a va 2 s . com*/ OperationContext.useHubContext(); Logger.trace("Decoding Stream Data: " + in.readableBytes()); switch (this.state) { case HEADER: { if (this.headerparser == null) { this.builder = new ObjectBuilder(); this.headerparser = new BufferToCompositeParser(this.builder); } this.headerparser.parseStruct(in); // if not done wait for more bytes if (!this.headerparser.isDone()) return; this.state = State.PAYLOAD_SIZE; // deliberate fall through } case PAYLOAD_SIZE: { if (in.readableBytes() < 4) return; this.size = in.readInt(); this.state = State.PAYLOAD; // deliberate fall through } case PAYLOAD: { // return here, without any state reset, means we need more before we can decide what to do if (in.readableBytes() < this.size) return; // we have enough data to send the message... StreamMessage msg = new StreamMessage(); // add Data only if there are some bytes, otherwise skip buffer allocation if (this.size > 0) { ByteBuf bb = in.readSlice(this.size); bb.retain(); msg.setData(bb); } msg.copyFields((RecordStruct) this.builder.getRoot()); out.add(msg); // set state to start over - ready to process next message this.headerparser = null; this.size = 0; this.state = State.HEADER; } } }
From source file:divconq.ctp.f.BlockCommand.java
License:Open Source License
@Override public boolean decode(ByteBuf in) { while (this.state != State.DONE) { switch (this.state) { case BLOCK_TYPE: { if (in.readableBytes() < 1) return false; this.blocktype = in.readUnsignedByte(); this.eof = ((this.blocktype & CtpConstants.CTP_F_BLOCK_TYPE_EOF) != 0); this.skipHeaders = ((this.blocktype & CtpConstants.CTP_F_BLOCK_TYPE_HEADER) == 0); this.skipPayload = ((this.blocktype & CtpConstants.CTP_F_BLOCK_TYPE_CONTENT) == 0); // completely done, exit the loop and decode if (this.skipHeaders && this.skipPayload) { this.state = State.DONE; break; }/*from w w w . j av a 2 s.c o m*/ // to skip headers, go back to loop if (this.skipHeaders) { this.state = State.STREAM_OFFSET; break; } this.state = State.HEADER_ATTR; // deliberate fall through } case HEADER_ATTR: { if (in.readableBytes() < 2) return false; this.currattr = in.readShort(); // done with headers, go back to loop to skip down to payload if (this.currattr == CtpConstants.CTP_F_ATTR_END) { if (this.skipPayload) this.state = State.DONE; else this.state = State.STREAM_OFFSET; break; } this.state = State.HEADER_SIZE; // deliberate fall through } case HEADER_SIZE: { if (in.readableBytes() < 2) return false; this.currasize = in.readShort(); // an empty attribute is like a flag - present but no data // go on to next header if (this.currasize == 0) { this.headers.put(this.currattr, new byte[0]); this.currattr = 0; this.state = State.HEADER_ATTR; break; } this.state = State.HEADER_VALUE; // deliberate fall through } case HEADER_VALUE: { if (in.readableBytes() < this.currasize) return false; byte[] val = new byte[this.currasize]; in.readBytes(val); this.headers.put(this.currattr, val); this.currattr = 0; this.currasize = 0; this.state = State.HEADER_ATTR; break; } case STREAM_OFFSET: { if (in.readableBytes() < 8) return false; this.streamOffset = in.readLong(); this.state = State.PAYLOAD_SIZE; // deliberate fall through } case PAYLOAD_SIZE: { if (in.readableBytes() < 3) return false; this.paysize = in.readMedium(); this.state = State.PAYLOAD; // deliberate fall through } case PAYLOAD: { // return here, without any state reset, means we need more before we can decide what to do if (in.readableBytes() < this.paysize) return false; // add Data only if there are some bytes, otherwise skip buffer allocation if (this.paysize > 0) { ByteBuf bb = in.readSlice(this.paysize); bb.retain(); this.data = bb; } this.state = State.DONE; // deliberate fall through } case DONE: { break; } } } return true; }
From source file:divconq.http.multipart.MixedFileUpload.java
License:Apache License
@Override public void addContent(ByteBuf buffer, boolean last) throws IOException { if (fileUpload instanceof MemoryFileUpload) { checkSize(fileUpload.length() + buffer.readableBytes()); if (fileUpload.length() + buffer.readableBytes() > limitSize) { DiskFileUpload diskFileUpload = new DiskFileUpload(fileUpload.getName(), fileUpload.getFilename(), fileUpload.getContentType(), fileUpload.getContentTransferEncoding(), fileUpload.getCharset(), definedSize); diskFileUpload.setMaxSize(maxSize); ByteBuf data = fileUpload.getByteBuf(); if (data != null && data.isReadable()) { diskFileUpload.addContent(data.retain(), false); }/*from w w w.java 2s . c om*/ // release old upload fileUpload.release(); fileUpload = diskFileUpload; } } fileUpload.addContent(buffer, last); }
From source file:divconq.web.http.ServerHandler.java
License:Open Source License
public void handleHttpRequest(ChannelHandlerContext ctx, HttpObject httpobj) throws Exception { if (httpobj instanceof HttpContent) { this.context.offerContent((HttpContent) httpobj); return;//from w ww . j a va 2 s . co m } if (!(httpobj instanceof HttpRequest)) { this.context.sendRequestBad(); return; } HttpRequest httpreq = (HttpRequest) httpobj; this.context.load(ctx, httpreq); // Handle a bad request. if (!httpreq.getDecoderResult().isSuccess()) { this.context.sendRequestBad(); return; } Request req = this.context.getRequest(); Response resp = this.context.getResponse(); // to avoid lots of unused sessions if (req.pathEquals("/favicon.ico") || req.pathEquals("/robots.txt")) { this.context.sendNotFound(); return; } // make sure we don't have a leftover task context OperationContext.clear(); String origin = "http:" + NetUtil.formatIpAddress((InetSocketAddress) ctx.channel().remoteAddress()); // TODO use X-Forwarded-For if available, maybe a plug in approach to getting client's IP? DomainInfo dinfo = this.context.getSiteman().resolveDomainInfo(req.getHeader("Host")); if (dinfo == null) { this.context.sendForbidden(); return; } WebDomain wdomain = this.context.getSiteman().getDomain(dinfo.getId()); // check into url re-routing String reroute = wdomain.route(req, (SslHandler) ctx.channel().pipeline().get("ssl")); if (StringUtil.isNotEmpty(reroute)) { this.context.getResponse().setStatus(HttpResponseStatus.FOUND); this.context.getResponse().setHeader("Location", reroute); this.context.send(); return; } Cookie sesscookie = req.getCookie("SessionId"); Session sess = null; if (sesscookie != null) { String v = sesscookie.getValue(); String sessionid = v.substring(0, v.lastIndexOf('_')); String accesscode = v.substring(v.lastIndexOf('_') + 1); sess = Hub.instance.getSessions().lookupAuth(sessionid, accesscode); } if (sess == null) { sess = Hub.instance.getSessions().create(origin, dinfo.getId()); Logger.info("Started new session: " + sess.getId() + " on " + req.getPath() + " for " + origin); // TODO if ssl set client key on user context //req.getSecuritySession().getPeerCertificates(); sess.setAdatper(new ISessionAdapter() { protected volatile ListStruct msgs = new ListStruct(); @Override public void stop() { ServerHandler.this.context.close(); } @Override public ListStruct popMessages() { ListStruct ret = this.msgs; this.msgs = new ListStruct(); return ret; } @Override public void deliver(Message msg) { // keep no more than 100 messages - this is not a "reliable" approach, just basic comm help while (this.msgs.getSize() > 99) this.msgs.removeItem(0); this.msgs.addItem(msg); } }); Cookie sk = new DefaultCookie("SessionId", sess.getId() + "_" + sess.getKey()); sk.setPath("/"); sk.setHttpOnly(true); resp.setCookie(sk); } this.context.setSession(sess); sess.touch(); OperationContext tc = sess.setContext(origin); tc.info("Web request for host: " + req.getHeader("Host") + " url: " + req.getPath() + " by: " + origin + " session: " + sess.getId()); /* System.out.println("sess proto: " + ((SslHandler)ctx.channel().pipeline().get("ssl")).engine().getSession().getProtocol()); System.out.println("sess suite: " + ((SslHandler)ctx.channel().pipeline().get("ssl")).engine().getSession().getCipherSuite()); */ try { if (req.pathEquals(ServerHandler.BUS_PATH)) { // Allow only GET methods. if (req.getMethod() != HttpMethod.GET) { this.context.sendForbidden(); return; } // Handshake WebSocketServerHandshakerFactory wsFactory = new WebSocketServerHandshakerFactory( ServerHandler.getWebSocketLocation( "True".equals(this.context.getConfig().getAttribute("Secure")), httpreq), null, false); this.handshaker = wsFactory.newHandshaker(httpreq); if (this.handshaker == null) WebSocketServerHandshakerFactory.sendUnsupportedVersionResponse(ctx.channel()); else { DefaultFullHttpRequest freq = new DefaultFullHttpRequest(httpreq.getProtocolVersion(), httpreq.getMethod(), httpreq.getUri()); freq.headers().add(httpreq.headers()); this.handshaker.handshake(ctx.channel(), freq); return; } this.context.sendForbidden(); return; } // "upload" is it's own built-in extension. if ((req.getPath().getNameCount() == 3) && req.getPath().getName(0).equals(ServerHandler.UPLOAD_PATH)) { if (!Hub.instance.isRunning()) { // only allow uploads when running this.context.sendRequestBad(); return; } // currently only supporting POST/PUT of pure binary - though support for form uploads can be restored, see below // we cannot rely on content type being meaningful //if (!"application/octet-stream".equals(req.getContentType().getPrimary())) { // this.context.sendRequestBad(); // return; //} // TODO add CORS support if needed if ((req.getMethod() != HttpMethod.PUT) && (req.getMethod() != HttpMethod.POST)) { this.context.sendRequestBad(); return; } final String cid = req.getPath().getName(1); final String op = req.getPath().getName(2); final DataStreamChannel dsc = sess.getChannel(cid); if (dsc == null) { this.context.sendRequestBad(); return; } dsc.setDriver(new IStreamDriver() { @Override public void cancel() { Logger.error("Transfer canceled on channel: " + cid); dsc.complete(); ServerHandler.this.context.sendRequestBad(); // TODO headers? } @Override public void nextChunk() { Logger.debug("Continue on channel: " + cid); ServerHandler.this.context.sendRequestOk(); } @Override public void message(StreamMessage msg) { if (msg.isFinal()) { Logger.debug("Final on channel: " + cid); dsc.complete(); ServerHandler.this.context.sendRequestOk(); } } }); //if (req.getMethod() == HttpMethod.PUT) { this.context.setDecoder(new IContentDecoder() { protected boolean completed = false; protected int seq = 0; @Override public void release() { // trust that http connection is closing or what ever needs to happen, we just need to deal with datastream Logger.debug("Releasing data stream"); // if not done with request then something went wrong, kill data channel if (!this.completed) dsc.abort(); } @Override public void offer(HttpContent chunk) { boolean finalchunk = (chunk instanceof LastHttpContent); //System.out.println("Chunk: " + finalchunk); ByteBuf buffer = chunk.content(); if (!dsc.isClosed()) { int size = buffer.readableBytes(); //System.out.println("Chunk size: " + size); dsc.touch(); // TODO try to set progress on dsc // TODO set hint in netty as to where this buffer was handled and sent if (size > 0) { buffer.retain(); // we will be using a reference up during send StreamMessage b = new StreamMessage("Block", buffer); b.setField("Sequence", this.seq); //System.out.println("Buffer ref cnt a: " + buffer.refCnt()); OperationResult or = dsc.send(b); //System.out.println("Buffer ref cnt b: " + buffer.refCnt()); // indicate we have read the buffer? buffer.readerIndex(buffer.writerIndex()); if (or.hasErrors()) { dsc.close(); return; } this.seq++; } // if last buffer of last block then mark the upload as completed if (finalchunk) { if ("Final".equals(op)) dsc.send(MessageUtil.streamFinal()); else dsc.getDriver().nextChunk(); } } // means this block is completed, not necessarily entire file uploaded if (finalchunk) this.completed = true; } }); //return; //} /* old approach that supported multipart posts TODO review/remove if (req.getMethod() == HttpMethod.POST) { StreamingDataFactory sdf = new StreamingDataFactory(dsc, op); // TODO consider supporting non-multipart? final HttpPostMultipartRequestDecoder prd = new HttpPostMultipartRequestDecoder(sdf, httpreq); this.context.setDecoder(new IContentDecoder() { @Override public void release() { // trust that http connection is closing or what ever needs to happen, we just need to deal with datastream // if not done with request then something went wrong, kill data channel if ((prd.getStatus() != MultiPartStatus.EPILOGUE) && (prd.getStatus() != MultiPartStatus.PREEPILOGUE)) dsc.kill(); } @Override public void offer(HttpContent chunk) { //the only thing we care about is the file, the file will stream to dsc - the rest can disappear prd.offer(chunk); } }); return; } */ //this.context.sendRequestBad(); return; } // "download" is it's own built-in extension. if ((req.getPath().getNameCount() == 2) && req.getPath().getName(0).equals(ServerHandler.DOWNLOAD_PATH)) { if (!Hub.instance.isRunning()) { // only allow downloads when running this.context.sendRequestBad(); return; } if (req.getMethod() != HttpMethod.GET) { this.context.sendRequestBad(); return; } String cid = req.getPath().getName(1); final DataStreamChannel dsc = sess.getChannel(cid); if (dsc == null) { this.context.sendRequestBad(); return; } dsc.setDriver(new IStreamDriver() { //protected long amt = 0; protected long seq = 0; @Override public void cancel() { dsc.complete(); ServerHandler.this.context.close(); } @Override public void nextChunk() { // meaningless in download } @Override public void message(StreamMessage msg) { int seqnum = (int) msg.getFieldAsInteger("Sequence", 0); if (seqnum != this.seq) { this.error(1, "Bad sequence number: " + seqnum); return; } if (msg.hasData()) { //this.amt += msg.getData().readableBytes(); HttpContent b = new DefaultHttpContent(Unpooled.copiedBuffer(msg.getData())); // TODO not copied ServerHandler.this.context.sendDownload(b); } this.seq++; // TODO update progress if (msg.isFinal()) { ServerHandler.this.context.sendDownload(new DefaultLastHttpContent()); ServerHandler.this.context.close(); dsc.complete(); } } public void error(int code, String msg) { dsc.send(MessageUtil.streamError(code, msg)); ServerHandler.this.context.close(); } }); // for some reason HyperSession is sending content. this.context.setDecoder(new IContentDecoder() { @Override public void release() { } @Override public void offer(HttpContent chunk) { if (!(chunk instanceof LastHttpContent)) Logger.error("Unexplained and unwanted content during download: " + chunk); } }); // tell the client that chunked content is coming this.context.sendDownloadHeaders(dsc.getPath() != null ? dsc.getPath().getFileName() : null, dsc.getMime()); // get the data flowing dsc.send(new StreamMessage("Start")); return; } if ((req.getPath().getNameCount() == 1) && req.getPath().getName(0).equals(ServerHandler.STATUS_PATH)) { if (Hub.instance.getState() == HubState.Running) this.context.sendRequestOk(); else this.context.sendRequestBad(); return; } // "rpc" is it's own built-in extension. all requests to rpc are routed through // DivConq bus, if the request is valid if (req.pathEquals(ServerHandler.RPC_PATH)) { if (req.getMethod() != HttpMethod.POST) { this.context.sendRequestBad(); return; } //System.out.println("looks like we have a rpc message"); // max 4MB of json? -- TODO is that max chunk size or max total? we don't need 4MB chunk... this.context.setDecoder(new HttpBodyRequestDecoder(4096 * 1024, new RpcHandler(this.context))); return; } // otherwise we need to figure out which extension is being called // "local" is also used to mean default extension String ext = req.pathEquals("/") ? "local" : req.getPath().getName(0); IWebExtension ex = "local".equals(ext) ? this.context.getSiteman().getDefaultExtension() : this.context.getSiteman().getExtension(ext); // still cannot figure it out, use default if (ex == null) ex = this.context.getSiteman().getDefaultExtension(); // then have extension handle it if (ex != null) { //OperationResult res = new OperationResult(); OperationResult res = ex.handle(sess, this.context); //resp.addBody("Hello"); //this.context.send(); // no errors starting page processing, return if (!res.hasErrors()) return; resp.setHeader("X-dcResultCode", res.getCode() + ""); resp.setHeader("X-dcResultMesage", res.getMessage()); this.context.sendNotFound(); return; } } catch (Exception x) { this.context.sendInternalError(); return; } this.context.sendNotFound(); }
From source file:divconq.web.HttpUploadDecoder.java
License:Open Source License
@Override public void offer(HttpContent chunk) { if (this.channel.isClosed()) return; // TODO somehow connect the cancel back to netsession if (chunk.content().readableBytes() > this.max) { this.channel.abort(); // TODO somehow connect the cancel back to netsession return;//w w w. ja v a 2s. c o m } ByteBuf bb = chunk.content(); bb.retain(); // we will use it in upcoming send System.out.println("ref count a: " + bb.refCnt()); StreamMessage b = new StreamMessage("Block", bb); b.setField("Sequence", this.seq); OperationResult or = this.channel.send(b); // bb should now be back to 1 System.out.println("ref count b: " + bb.refCnt()); if (or.hasErrors()) { this.channel.close(); return; } this.seq++; // TODO track progress if possible // final only if not canceled if (chunk instanceof LastHttpContent) this.channel.send(MessageUtil.streamFinal()); }
From source file:eu.stratosphere.runtime.io.network.netty.InboundEnvelopeDecoderTest.java
License:Apache License
@Test public void testEncodeDecode() throws Exception { final EmbeddedChannel ch = new EmbeddedChannel(new OutboundEnvelopeEncoder(), new InboundEnvelopeDecoder(this.bufferProviderBroker)); when(this.bufferProviderBroker.getBufferProvider(anyJobId(), anyChannelId())) .thenReturn(this.bufferProvider); when(this.bufferProvider.requestBuffer(anyInt())).thenAnswer(new Answer<Object>() { @Override/*from www .j a va 2s . c om*/ public Object answer(InvocationOnMock invocation) throws Throwable { // fulfill the buffer request return allocBuffer((Integer) invocation.getArguments()[0]); } }); // -------------------------------------------------------------------- Envelope[] envelopes = new Envelope[] { nextEnvelope(0), nextEnvelope(2), nextEnvelope(32768), nextEnvelope(3782, new TestEvent1(34872527)), nextEnvelope(88, new TestEvent1(8749653), new TestEvent1(365345)), nextEnvelope(0, new TestEvent2(34563456), new TestEvent1(598432), new TestEvent2(976293845)), nextEnvelope(23) }; ByteBuf buf = encode(ch, envelopes); // 1. complete ByteBuf as input int refCount = buf.retain().refCnt(); decodeAndVerify(ch, buf, envelopes); Assert.assertEquals(refCount - 1, buf.refCnt()); // 2. random slices buf.readerIndex(0); ByteBuf[] slices = randomSlices(buf); ch.writeInbound(slices); for (ByteBuf slice : slices) { Assert.assertEquals(1, slice.refCnt()); } decodeAndVerify(ch, envelopes); buf.release(); }
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 w w w . jav a 2s .c om*/ // 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:gribbit.server.siteresources.CacheExtension.java
License:Open Source License
/** * Create a hash URI (which allows the browser to cache this resource indefinitely) if the last modified * timestamp has increased, or if there is no hash URI yet for this resource. For a new hash URI to be created, * the passed object is scheduled to be hashed by a background thread. * * This method can be called by any route handler that stores or returns database objects. It should be called * both when storing objects and when returning them, since the hash URI cache is held in RAM and is empty when * the server starts, so it needs to be built as requests start to come in. IMPORTANT NOTE: If the database can * be written to by other database clients, then this method must also be called when those changes are * detected, otherwise web clients connected to this web server will continue to serve old linked resources. * // www. j a v a2 s .co m * This method should only be used when the total keyspace of URIs that map to database objects easily fits in * RAM, and when the objects that need to be hashed are not large (i.e. tens of MB is OK, hundreds of MB is * probably not, since there are several background worker threads and they all can be hashing objects in * parallel). */ public static void updateHashURI(String origURI, ByteBuf content, long lastModifiedEpochSeconds) { // Can only hash absolute but local (i.e. domain-less) URIs that have not already been hashed if (origURI.startsWith("/") && !origURI.startsWith("//") && !origURI.startsWith("/_/")) { // Check to see if there is already a mapping to hash URI for this original URI HashInfo hashInfo = origURIToHashInfo.get(origURI); if (hashInfo == null || hashInfo.lastModifiedEpochSeconds < lastModifiedEpochSeconds) { // There is no hash URI yet for origURI, or there is already a hash URI corresponding to origURI, // but the modification time has increased since the cached version, so need to re-hash. // Check if another thread has already enqueued the URI for hashing. Object alreadyInQueue = scheduledURIsToHash.put(origURI, new Object()); if (alreadyInQueue == null) { content.retain(); // This URI is not currently queued for hashing by background workers, add it to the queue scheduleHasher(origURI, lastModifiedEpochSeconds, new Hasher() { @Override public String computeHashKey() { // Compute MD5 hash of the ByteBuf contents, then base64-encode the results try { String hash = Base64Safe .base64Encode(DigestUtils.md5(new ByteBufInputStream(content))); content.release(); // TODO: does ByteBufInputStream call release()? return hash; } catch (IOException e) { return null; } } }); } } } }
From source file:http2.server.HelloWorldHttp2Handler.java
License:Apache License
@Override public int onDataRead(ChannelHandlerContext ctx, int streamId, ByteBuf data, int padding, boolean endOfStream) { logger.info("onDataRead, " + streamId + ", data : " + data.toString()); int processed = data.readableBytes() + padding; if (endOfStream) { sendResponse(ctx, streamId, data.retain()); }/*from w ww.j a va 2 s.c o m*/ return processed; }