void spewInternal() { if (pending.remaining() > 0) { com.koushikdutta.async.Util.emitAllData(BodySpewer.this, pending); if (pending.remaining() > 0) return; } // fill pending try { while (pending.remaining() == 0) { ByteBuffer buffer = ByteBuffer.allocate(8192); int read = cacheResponse.getBody().read(buffer.array()); if (read == -1) { allowEnd = true; report(null); return; } buffer.limit(read); pending.add(buffer); com.koushikdutta.async.Util.emitAllData(BodySpewer.this, pending); } } catch (IOException e) { allowEnd = true; report(e); } }
@Override public void write(AsyncHttpRequest request, AsyncHttpResponse sink) { Util.writeAll( sink, mBodyBytes, new CompletedCallback() { @Override public void onCompleted(Exception ex) {} }); }
void setSocket(AsyncSocket exchange) { mSocket = exchange; if (mSocket == null) return; mWriter = mRequest.getBody(); if (mWriter != null) { mRequest.getHeaders().setContentType(mWriter.getContentType()); if (mWriter.length() != -1) { mRequest.getHeaders().setContentLength(mWriter.length()); mSink = mSocket; } else { mRequest.getHeaders().getHeaders().set("Transfer-Encoding", "Chunked"); mSink = new ChunkedOutputFilter(mSocket); } } else { mSink = mSocket; } String rs = mRequest.getRequestString(); com.koushikdutta.async.Util.writeAll( exchange, rs.getBytes(), new CompletedCallback() { @Override public void onCompleted(Exception ex) { if (mWriter != null) mWriter.write(mRequest, AsyncHttpResponseImpl.this); } }); LineEmitter liner = new LineEmitter(); exchange.setDataCallback(liner); liner.setLineCallback(mHeaderCallback); mSocket.setEndCallback(mReporter); mSocket.setClosedCallback( new CompletedCallback() { @Override public void onCompleted(Exception ex) { // TODO: do we care? throw if socket is still writing or something? } }); }
@Override public void onDataAvailable(DataEmitter emitter, ByteBufferList bb) { try { ByteBufferList transformed = new ByteBufferList(); ByteBuffer output = ByteBuffer.allocate(bb.remaining() * 2); int totalInflated = 0; int totalRead = 0; while (bb.size() > 0) { ByteBuffer b = bb.remove(); if (b.hasRemaining()) { totalRead = +b.remaining(); mInflater.setInput(b.array(), b.arrayOffset() + b.position(), b.remaining()); do { int inflated = mInflater.inflate( output.array(), output.arrayOffset() + output.position(), output.remaining()); totalInflated += inflated; output.position(output.position() + inflated); if (!output.hasRemaining()) { output.limit(output.position()); output.position(0); transformed.add(output); Assert.assertNotSame(totalRead, 0); int newSize = output.capacity() * 2; output = ByteBuffer.allocate(newSize); } } while (!mInflater.needsInput() && !mInflater.finished()); } } output.limit(output.position()); output.position(0); transformed.add(output); Util.emitAllData(this, transformed); } catch (Exception ex) { report(ex); } }
@Override public void onDataAvailable(DataEmitter emitter, ByteBufferList bb) { if (cached != null) { com.koushikdutta.async.Util.emitAllData(this, cached); // couldn't emit it all, so just wait for another day... if (cached.remaining() > 0) return; cached = null; } // write to cache... any data not consumed needs to be retained for the next callback try { if (cacheRequest != null) { OutputStream outputStream = cacheRequest.getBody(); if (outputStream != null) { int count = bb.size(); for (int i = 0; i < count; i++) { ByteBuffer b = bb.remove(); outputStream.write(b.array(), b.arrayOffset() + b.position(), b.remaining()); bb.add(b); } } else { abort(); } } } catch (Exception e) { abort(); } super.onDataAvailable(emitter, bb); if (cacheRequest != null && bb.remaining() > 0) { cached = new ByteBufferList(); cached.add(bb); bb.clear(); } }
@Override public boolean exchangeHeaders(final OnExchangeHeaderData data) { Protocol p = Protocol.get(data.protocol); if (p != null && p != Protocol.HTTP_1_0 && p != Protocol.HTTP_1_1) return super.exchangeHeaders(data); AsyncHttpRequest request = data.request; AsyncHttpRequestBody requestBody = data.request.getBody(); if (requestBody != null) { if (requestBody.length() >= 0) { request.getHeaders().set("Content-Length", String.valueOf(requestBody.length())); data.response.sink(data.socket); } else if ("close".equals(request.getHeaders().get("Connection"))) { data.response.sink(data.socket); } else { request.getHeaders().set("Transfer-Encoding", "Chunked"); data.response.sink(new ChunkedOutputFilter(data.socket)); } } String rl = request.getRequestLine().toString(); String rs = request.getHeaders().toPrefixString(rl); request.logv("\n" + rs); Util.writeAll(data.socket, rs.getBytes(), data.sendHeadersCallback); LineEmitter.StringCallback headerCallback = new LineEmitter.StringCallback() { Headers mRawHeaders = new Headers(); String statusLine; @Override public void onStringAvailable(String s) { try { s = s.trim(); if (statusLine == null) { statusLine = s; } else if (!TextUtils.isEmpty(s)) { mRawHeaders.addLine(s); } else { String[] parts = statusLine.split(" ", 3); if (parts.length < 2) throw new Exception(new IOException("Not HTTP")); data.response.headers(mRawHeaders); String protocol = parts[0]; data.response.protocol(protocol); data.response.code(Integer.parseInt(parts[1])); data.response.message(parts.length == 3 ? parts[2] : ""); data.receiveHeadersCallback.onCompleted(null); // socket may get detached after headers (websocket) AsyncSocket socket = data.response.socket(); if (socket == null) return; DataEmitter emitter; // HEAD requests must not return any data. They still may // return content length, etc, which will confuse the body decoder if (AsyncHttpHead.METHOD.equalsIgnoreCase(data.request.getMethod())) { emitter = HttpUtil.EndEmitter.create(socket.getServer(), null); } else { emitter = HttpUtil.getBodyDecoder(socket, Protocol.get(protocol), mRawHeaders, false); } data.response.emitter(emitter); } } catch (Exception ex) { data.receiveHeadersCallback.onCompleted(ex); } } }; LineEmitter liner = new LineEmitter(); data.socket.setDataCallback(liner); liner.setLineCallback(headerCallback); return true; }
// step 3) if this is a conditional cache request, serve it from the cache if necessary // otherwise, see if it is cacheable @Override public void onBodyDecoder(OnBodyData data) { CachedSocket cached = (CachedSocket) com.koushikdutta.async.Util.getWrappedSocket(data.socket, CachedSocket.class); if (cached != null) return; CacheData cacheData = data.state.getParcelable("cache-data"); if (cacheData != null) { if (cacheData.cachedResponseHeaders.validate(data.headers)) { data.headers = cacheData.cachedResponseHeaders.combine(data.headers); data.headers .getHeaders() .setStatusLine(cacheData.cachedResponseHeaders.getHeaders().getStatusLine()); conditionalCacheHitCount++; BodySpewer bodySpewer = new BodySpewer(); bodySpewer.cacheResponse = cacheData.candidate; bodySpewer.setDataEmitter(data.bodyEmitter); data.bodyEmitter = bodySpewer; bodySpewer.spew(); return; } // did not validate, so fall through and cache the response data.state.remove("cache-data"); } if (!caching) return; if (!data.headers.isCacheable(data.request.getHeaders()) || !data.request.getMethod().equals(AsyncHttpGet.METHOD)) { /* * Don't cache non-GET responses. We're technically allowed to cache * HEAD requests and some POST requests, but the complexity of doing * so is high and the benefit is low. */ networkCount++; return; } String key = uriToKey(data.request.getUri()); RawHeaders varyHeaders = data.request.getHeaders().getHeaders().getAll(data.headers.getVaryFields()); Entry entry = new Entry(data.request.getUri(), varyHeaders, data.request, data.headers); DiskLruCache.Editor editor = null; BodyCacher cacher = new BodyCacher(); try { editor = cache.edit(key); if (editor == null) { // Log.i(LOGTAG, "can't cache"); return; } entry.writeTo(editor); cacher.cacheRequest = new CacheRequestImpl(editor); if (cacher.cacheRequest.getBody() == null) return; // cacher.cacheData = cacher.setDataEmitter(data.bodyEmitter); data.bodyEmitter = cacher; data.state.putParcelable("body-cacher", cacher); cacheStoreCount++; } catch (Exception e) { // Log.e(LOGTAG, "error", e); if (cacher.cacheRequest != null) cacher.cacheRequest.abort(); cacher.cacheRequest = null; networkCount++; } }
@Override public void write( final AsyncHttpRequest request, DataSink sink, final CompletedCallback completed) { Util.pump(emitter, sink, completed); if (emitter.isPaused()) emitter.resume(); }