@Override protected boolean isContentAlwaysEmpty(HttpMessage msg) { final int statusCode = ((HttpResponse) msg).getStatus().code(); if (statusCode == 100) { // 100-continue response should be excluded from paired comparison. return true; } // Get the getMethod of the HTTP request that corresponds to the // current response. HttpMethod method = queue.poll(); char firstChar = method.name().charAt(0); switch (firstChar) { case 'H': // According to 4.3, RFC2616: // All responses to the HEAD request getMethod MUST NOT include a // message-body, even though the presence of entity-header fields // might lead one to believe they do. if (HttpMethod.HEAD.equals(method)) { return true; // The following code was inserted to work around the servers // that behave incorrectly. It has been commented out // because it does not work with well behaving servers. // Please note, even if the 'Transfer-Encoding: chunked' // header exists in the HEAD response, the response should // have absolutely no content. // //// Interesting edge case: //// Some poorly implemented servers will send a zero-byte //// chunk if Transfer-Encoding of the response is 'chunked'. //// //// return !msg.isChunked(); } break; case 'C': // Successful CONNECT request results in a response with empty body. if (statusCode == 200) { if (HttpMethod.CONNECT.equals(method)) { // Proxy connection established - Not HTTP anymore. done = true; queue.clear(); return true; } } break; } return super.isContentAlwaysEmpty(msg); }
/* ------------------------------------------------------------ */ public Result generateRequest( MetaData.Request info, ByteBuffer header, ByteBuffer chunk, ByteBuffer content, boolean last) throws IOException { switch (_state) { case START: { if (info == null) return Result.NEED_INFO; // Do we need a request header if (header == null) return Result.NEED_HEADER; // If we have not been told our persistence, set the default if (_persistent == null) { _persistent = info.getVersion().ordinal() > HttpVersion.HTTP_1_0.ordinal(); if (!_persistent && HttpMethod.CONNECT.is(info.getMethod())) _persistent = true; } // prepare the header int pos = BufferUtil.flipToFill(header); try { // generate ResponseLine generateRequestLine(info, header); if (info.getVersion() == HttpVersion.HTTP_0_9) _noContent = true; else generateHeaders(info, header, content, last); boolean expect100 = info.getFields().contains(HttpHeader.EXPECT, HttpHeaderValue.CONTINUE.asString()); if (expect100) { _state = State.COMMITTED; } else { // handle the content. int len = BufferUtil.length(content); if (len > 0) { _contentPrepared += len; if (isChunking()) prepareChunk(header, len); } _state = last ? State.COMPLETING : State.COMMITTED; } return Result.FLUSH; } catch (Exception e) { String message = (e instanceof BufferOverflowException) ? "Request header too large" : e.getMessage(); throw new IOException(message, e); } finally { BufferUtil.flipToFlush(header, pos); } } case COMMITTED: { int len = BufferUtil.length(content); if (len > 0) { // Do we need a chunk buffer? if (isChunking()) { // Do we need a chunk buffer? if (chunk == null) return Result.NEED_CHUNK; BufferUtil.clearToFill(chunk); prepareChunk(chunk, len); BufferUtil.flipToFlush(chunk, 0); } _contentPrepared += len; } if (last) _state = State.COMPLETING; return len > 0 ? Result.FLUSH : Result.CONTINUE; } case COMPLETING: { if (BufferUtil.hasContent(content)) { if (LOG.isDebugEnabled()) LOG.debug("discarding content in COMPLETING"); BufferUtil.clear(content); } if (isChunking()) { // Do we need a chunk buffer? if (chunk == null) return Result.NEED_CHUNK; BufferUtil.clearToFill(chunk); prepareChunk(chunk, 0); BufferUtil.flipToFlush(chunk, 0); _endOfContent = EndOfContent.UNKNOWN_CONTENT; return Result.FLUSH; } _state = State.END; return Boolean.TRUE.equals(_persistent) ? Result.DONE : Result.SHUTDOWN_OUT; } case END: if (BufferUtil.hasContent(content)) { if (LOG.isDebugEnabled()) LOG.debug("discarding content in COMPLETING"); BufferUtil.clear(content); } return Result.DONE; default: throw new IllegalStateException(); } }