@Override public void reset() { resetForForward(); _status = 200; _reason = null; _contentLength = -1; _fields.clear(); String connection = _channel.getRequest().getHttpFields().getStringField(HttpHeader.CONNECTION); if (connection != null) { String[] values = connection.split(","); for (int i = 0; values != null && i < values.length; i++) { HttpHeaderValue cb = HttpHeaderValue.CACHE.get(values[0].trim()); if (cb != null) { switch (cb) { case CLOSE: _fields.put(HttpHeader.CONNECTION, HttpHeaderValue.CLOSE.toString()); break; case KEEP_ALIVE: if (HttpVersion.HTTP_1_0.is(_channel.getRequest().getProtocol())) _fields.put(HttpHeader.CONNECTION, HttpHeaderValue.KEEP_ALIVE.toString()); break; case TE: _fields.put(HttpHeader.CONNECTION, HttpHeaderValue.TE.toString()); break; } } } } }
/** * Asynchronous send of HTTP content. * * @param httpContent The HTTP content to send * @param callback The callback to use to notify success or failure */ public void sendContent(HttpContent httpContent, Callback callback) { if (LOG.isDebugEnabled()) LOG.debug("sendContent(http={},{})", httpContent, callback); if (BufferUtil.hasContent(_aggregate)) { callback.failed(new IOException("cannot sendContent() after write()")); return; } if (_channel.isCommitted()) { callback.failed(new IOException("committed")); return; } while (true) { switch (_state.get()) { case OPEN: if (!_state.compareAndSet(OutputState.OPEN, OutputState.PENDING)) continue; break; case ERROR: callback.failed(new EofException(_onError)); return; case CLOSED: callback.failed(new EofException("Closed")); return; default: throw new IllegalStateException(); } break; } ByteBuffer buffer = _channel.useDirectBuffers() ? httpContent.getDirectBuffer() : null; if (buffer == null) buffer = httpContent.getIndirectBuffer(); if (buffer != null) { sendContent(buffer, callback); return; } try { ReadableByteChannel rbc = httpContent.getReadableByteChannel(); if (rbc != null) { // Close of the rbc is done by the async sendContent sendContent(rbc, callback); return; } InputStream in = httpContent.getInputStream(); if (in != null) { sendContent(in, callback); return; } throw new IllegalArgumentException("unknown content for " + httpContent); } catch (Throwable th) { abort(th); callback.failed(th); } }
/** * Blocking send of content. * * @param content The content to send. * @throws IOException */ public void sendContent(ByteBuffer content) throws IOException { final BlockingCallback callback = _channel.getWriteBlockingCallback(); if (content.hasArray() && content.limit() < content.capacity()) content = content.asReadOnlyBuffer(); _channel.write(content, true, callback); callback.block(); }
@Override public void flush() throws IOException { while (true) { switch (_state.get()) { case OPEN: if (BufferUtil.hasContent(_aggregate)) _channel.write(_aggregate, false); else _channel.write(BufferUtil.EMPTY_BUFFER, false); return; case ASYNC: throw new IllegalStateException("isReady() not called"); case READY: if (!_state.compareAndSet(State.READY, State.PENDING)) continue; new AsyncFlush().process(); return; case PENDING: case UNREADY: throw new WritePendingException(); case CLOSED: return; } break; } }
@Override public void setWriteListener(WriteListener writeListener) { if (!_channel.getState().isAsync()) throw new IllegalStateException("!ASYNC"); if (_state.compareAndSet(OutputState.OPEN, OutputState.READY)) { _writeListener = writeListener; if (_channel.getState().onWritePossible()) _channel.execute(_channel); } else throw new IllegalStateException(); }
protected ResponseInfo newResponseInfo() { if (_status == HttpStatus.NOT_SET_000) _status = HttpStatus.OK_200; return new ResponseInfo( _channel.getRequest().getHttpVersion(), _fields, getLongContentLength(), getStatus(), getReason(), _channel.getRequest().isHead()); }
@Override public void write(int b) throws IOException { _written += 1; boolean complete = _channel.getResponse().isAllContentWritten(_written); // Async or Blocking ? while (true) { switch (_state.get()) { case OPEN: if (_aggregate == null) _aggregate = _channel.getByteBufferPool().acquire(getBufferSize(), false); BufferUtil.append(_aggregate, (byte) b); // Check if all written or full if (complete || BufferUtil.isFull(_aggregate)) { BlockingCallback callback = _channel.getWriteBlockingCallback(); _channel.write(_aggregate, complete, callback); callback.block(); if (complete) closed(); } break; case ASYNC: throw new IllegalStateException("isReady() not called"); case READY: if (!_state.compareAndSet(State.READY, State.PENDING)) continue; if (_aggregate == null) _aggregate = _channel.getByteBufferPool().acquire(getBufferSize(), false); BufferUtil.append(_aggregate, (byte) b); // Check if all written or full if (!complete && !BufferUtil.isFull(_aggregate)) { if (!_state.compareAndSet(State.PENDING, State.ASYNC)) throw new IllegalStateException(); return; } // Do the asynchronous writing from the callback new AsyncFlush().process(); return; case PENDING: case UNREADY: throw new WritePendingException(); case CLOSED: throw new EofException("Closed"); } break; } }
@Override public void sendRedirect(String location) throws IOException { if (isIncluding()) return; if (location == null) throw new IllegalArgumentException(); if (!URIUtil.hasScheme(location)) { StringBuilder buf = _channel.getRequest().getRootURL(); if (location.startsWith("/")) buf.append(location); else { String path = _channel.getRequest().getRequestURI(); String parent = (path.endsWith("/")) ? path : URIUtil.parentPath(path); location = URIUtil.addPaths(parent, location); if (location == null) throw new IllegalStateException("path cannot be above root"); if (!location.startsWith("/")) buf.append('/'); buf.append(location); } location = buf.toString(); HttpURI uri = new HttpURI(location); String path = uri.getDecodedPath(); String canonical = URIUtil.canonicalPath(path); if (canonical == null) throw new IllegalArgumentException(); if (!canonical.equals(path)) { buf = _channel.getRequest().getRootURL(); buf.append(URIUtil.encodePath(canonical)); String param = uri.getParam(); if (param != null) { buf.append(';'); buf.append(param); } String query = uri.getQuery(); if (query != null) { buf.append('?'); buf.append(query); } String fragment = uri.getFragment(); if (fragment != null) { buf.append('#'); buf.append(fragment); } location = buf.toString(); } } resetBuffer(); setHeader(HttpHeader.LOCATION, location); setStatus(HttpServletResponse.SC_MOVED_TEMPORARILY); complete(); }
/** * Called to indicate that the last write has been performed. It updates the state and performs * cleanup operations. */ void closed() { while (true) { OutputState state = _state.get(); switch (state) { case CLOSED: { return; } case UNREADY: { if (_state.compareAndSet(state, OutputState.ERROR)) _writeListener.onError( _onError == null ? new EofException("Async closed") : _onError); break; } default: { if (!_state.compareAndSet(state, OutputState.CLOSED)) break; try { _channel.getResponse().closeOutput(); } catch (Throwable x) { if (LOG.isDebugEnabled()) LOG.debug(x); abort(x); } finally { releaseBuffer(); } // Return even if an exception is thrown by closeOutput(). return; } } } }
/* Called to indicated that the output is already closed and the state needs to be updated to match */ void closed() { State state = _state.get(); while (state != State.CLOSED) { if (_state.compareAndSet(state, State.CLOSED)) { try { _channel.getResponse().closeOutput(); } catch (IOException e) { LOG.debug(e); _channel.failed(); } releaseBuffer(); return; } state = _state.get(); } }
@Override public void setLocale(Locale locale) { if (locale == null || isCommitted() || isIncluding()) return; _locale = locale; _fields.put(HttpHeader.CONTENT_LANGUAGE, locale.toString().replace('_', '-')); if (_outputType != OutputType.NONE) return; if (_channel.getRequest().getContext() == null) return; String charset = _channel.getRequest().getContext().getContextHandler().getLocaleEncoding(locale); if (charset != null && charset.length() > 0 && _characterEncoding == null) setCharacterEncoding(charset); }
@Override public void close() { State state = _state.get(); while (state != State.CLOSED) { if (_state.compareAndSet(state, State.CLOSED)) { try { if (BufferUtil.hasContent(_aggregate)) _channel.write(_aggregate, !_channel.getResponse().isIncluding()); else _channel.write(BufferUtil.EMPTY_BUFFER, !_channel.getResponse().isIncluding()); } catch (IOException e) { LOG.debug(e); _channel.failed(); } releaseBuffer(); return; } state = _state.get(); } }
public void write(ByteBuffer buffer) throws IOException { _written += buffer.remaining(); boolean complete = _channel.getResponse().isAllContentWritten(_written); // Async or Blocking ? while (true) { switch (_state.get()) { case OPEN: // process blocking below break; case ASYNC: throw new IllegalStateException("isReady() not called"); case READY: if (!_state.compareAndSet(State.READY, State.PENDING)) continue; // Do the asynchronous writing from the callback new AsyncWrite(buffer, complete).process(); return; case PENDING: case UNREADY: throw new WritePendingException(); case CLOSED: throw new EofException("Closed"); } break; } // handle blocking write int len = BufferUtil.length(buffer); // flush any content from the aggregate if (BufferUtil.hasContent(_aggregate)) _channel.write(_aggregate, complete && len == 0); // write any remaining content in the buffer directly if (len > 0) _channel.write(buffer, complete); else if (complete) _channel.write(BufferUtil.EMPTY_BUFFER, complete); if (complete) closed(); }
public GzipHttpOutputInterceptor( GzipFactory factory, HttpChannel channel, HttpOutput.Interceptor next, boolean syncFlush) { this( factory, VARY_ACCEPT_ENCODING_USER_AGENT, channel.getHttpConfiguration().getOutputBufferSize(), channel, next, syncFlush); }
@Override public void close() { while (true) { OutputState state = _state.get(); switch (state) { case CLOSED: { return; } case ASYNC: case UNREADY: case PENDING: { if (!_state.compareAndSet(state, OutputState.CLOSED)) break; IOException ex = new IOException("Closed while Pending/Unready"); LOG.warn(ex.toString()); LOG.debug(ex); _channel.abort(ex); } default: { if (!_state.compareAndSet(state, OutputState.CLOSED)) break; try { write( BufferUtil.hasContent(_aggregate) ? _aggregate : BufferUtil.EMPTY_BUFFER, !_channel.getResponse().isIncluding()); } catch (IOException x) { // Ignore it, it's been already logged in write(). } finally { releaseBuffer(); } // Return even if an exception is thrown by write(). return; } } } }
/** * Asynchronous send of content. * * @param httpContent The content to send * @param callback The callback to use to notify success or failure */ public void sendContent(HttpContent httpContent, Callback callback) throws IOException { if (BufferUtil.hasContent(_aggregate)) throw new IOException("written"); if (_channel.isCommitted()) throw new IOException("committed"); while (true) { switch (_state.get()) { case OPEN: if (!_state.compareAndSet(State.OPEN, State.PENDING)) continue; break; case CLOSED: throw new EofException("Closed"); default: throw new IllegalStateException(); } break; } ByteBuffer buffer = _channel.useDirectBuffers() ? httpContent.getDirectBuffer() : null; if (buffer == null) buffer = httpContent.getIndirectBuffer(); if (buffer != null) { sendContent(buffer, callback); return; } ReadableByteChannel rbc = httpContent.getReadableByteChannel(); if (rbc != null) { // Close of the rbc is done by the async sendContent sendContent(rbc, callback); return; } InputStream in = httpContent.getInputStream(); if (in != null) { sendContent(in, callback); return; } callback.failed(new IllegalArgumentException("unknown content for " + httpContent)); }
public GzipHttpOutputInterceptor( GzipFactory factory, HttpField vary, HttpChannel channel, HttpOutput.Interceptor next, boolean syncFlush) { this( factory, vary, channel.getHttpConfiguration().getOutputBufferSize(), channel, next, syncFlush); }
/** * Called to signal async read isReady() has returned false. This indicates that there is no * content available to be consumed and that once the channel enteres the ASYNC_WAIT state it will * register for read interest by calling {@link HttpChannel#asyncReadFillInterested()} either from * this method or from a subsequent call to {@link #unhandle()}. */ public void onReadUnready() { boolean interested = false; try (Locker.Lock lock = _locker.lock()) { if (DEBUG) LOG.debug("onReadUnready {}", toStringLocked()); // We were already unready, this is not a state change, so do nothing if (!_asyncReadUnready) { _asyncReadUnready = true; _asyncReadPossible = false; // Assumes this has been checked in isReady() with lock held if (_state == State.ASYNC_WAIT) interested = true; } } if (interested) _channel.asyncReadFillInterested(); }
/** * Asynchronous send of content. * * @param content The content to send * @param callback The callback to use to notify success or failure */ public void sendContent(ByteBuffer content, final Callback callback) { if (content.hasArray() && content.limit() < content.capacity()) content = content.asReadOnlyBuffer(); _channel.write( content, true, new Callback() { @Override public void succeeded() { closed(); callback.succeeded(); } @Override public void failed(Throwable x) { callback.failed(x); } }); }
public HttpOutput(HttpChannel channel) { _channel = channel; _interceptor = channel; _writeBlock = new SharedBlockingCallback() { @Override protected long getIdleTimeout() { long bto = getHttpChannel().getHttpConfiguration().getBlockingTimeout(); if (bto > 0) return bto; if (bto < 0) return -1; return _channel.getIdleTimeout(); } }; HttpConfiguration config = channel.getHttpConfiguration(); _bufferSize = config.getOutputBufferSize(); _commitSize = config.getOutputAggregationSize(); if (_commitSize > _bufferSize) { LOG.warn("OutputAggregationSize {} exceeds bufferSize {}", _commitSize, _bufferSize); _commitSize = _bufferSize; } }
@Override public boolean isCommitted() { return _channel.isCommitted(); }
@Override public void print(String s) throws IOException { if (isClosed()) throw new IOException("Closed"); write(s.getBytes(_channel.getResponse().getCharacterEncoding())); }
@Override public void write(byte[] b, int off, int len) throws IOException { _written += len; boolean complete = _channel.getResponse().isAllContentWritten(_written); // Async or Blocking ? while (true) { switch (_state.get()) { case OPEN: // process blocking below break; case ASYNC: throw new IllegalStateException("isReady() not called"); case READY: if (!_state.compareAndSet(OutputState.READY, OutputState.PENDING)) continue; // Should we aggregate? if (!complete && len <= _commitSize) { if (_aggregate == null) _aggregate = _channel .getByteBufferPool() .acquire(getBufferSize(), _interceptor.isOptimizedForDirectBuffers()); // YES - fill the aggregate with content from the buffer int filled = BufferUtil.fill(_aggregate, b, off, len); // return if we are not complete, not full and filled all the content if (filled == len && !BufferUtil.isFull(_aggregate)) { if (!_state.compareAndSet(OutputState.PENDING, OutputState.ASYNC)) throw new IllegalStateException(); return; } // adjust offset/length off += filled; len -= filled; } // Do the asynchronous writing from the callback new AsyncWrite(b, off, len, complete).iterate(); return; case PENDING: case UNREADY: throw new WritePendingException(); case ERROR: throw new EofException(_onError); case CLOSED: throw new EofException("Closed"); default: throw new IllegalStateException(); } break; } // handle blocking write // Should we aggregate? int capacity = getBufferSize(); if (!complete && len <= _commitSize) { if (_aggregate == null) _aggregate = _channel .getByteBufferPool() .acquire(capacity, _interceptor.isOptimizedForDirectBuffers()); // YES - fill the aggregate with content from the buffer int filled = BufferUtil.fill(_aggregate, b, off, len); // return if we are not complete, not full and filled all the content if (filled == len && !BufferUtil.isFull(_aggregate)) return; // adjust offset/length off += filled; len -= filled; } // flush any content from the aggregate if (BufferUtil.hasContent(_aggregate)) { write(_aggregate, complete && len == 0); // should we fill aggregate again from the buffer? if (len > 0 && !complete && len <= _commitSize && len <= BufferUtil.space(_aggregate)) { BufferUtil.append(_aggregate, b, off, len); return; } } // write any remaining content in the buffer directly if (len > 0) { ByteBuffer wrap = ByteBuffer.wrap(b, off, len); ByteBuffer view = wrap.duplicate(); // write a buffer capacity at a time to avoid JVM pooling large direct buffers // http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6210541 while (len > getBufferSize()) { int p = view.position(); int l = p + getBufferSize(); view.limit(p + getBufferSize()); write(view, false); len -= getBufferSize(); view.limit(l + Math.min(len, getBufferSize())); view.position(l); } write(view, complete); } else if (complete) { write(BufferUtil.EMPTY_BUFFER, true); } if (complete) closed(); }
private void releaseBuffer() { if (_aggregate != null) { _channel.getConnector().getByteBufferPool().release(_aggregate); _aggregate = null; } }
@Override public String toString() { return String.format( "%s %d %s%n%s", _channel.getRequest().getHttpVersion(), _status, _reason == null ? "" : _reason, _fields); }
private void abort(Throwable failure) { closed(); _channel.abort(failure); }
public boolean isAllContentWritten() { return _channel.getResponse().isAllContentWritten(_written); }
@Override public boolean hasOriginalRequestAndResponse() { HttpChannel channel = state().getHttpChannel(); return channel.getRequest() == getRequest() && channel.getResponse() == getResponse(); }
/* * @see javax.servlet.RequestDispatcher#forward(javax.servlet.ServletRequest, javax.servlet.ServletResponse) */ protected void forward(ServletRequest request, ServletResponse response, DispatcherType dispatch) throws ServletException, IOException { Request baseRequest = (request instanceof Request) ? ((Request) request) : HttpChannel.getCurrentHttpChannel().getRequest(); Response base_response = baseRequest.getResponse(); base_response.resetForForward(); if (!(request instanceof HttpServletRequest)) request = new ServletRequestHttpWrapper(request); if (!(response instanceof HttpServletResponse)) response = new ServletResponseHttpWrapper(response); final boolean old_handled = baseRequest.isHandled(); final String old_uri = baseRequest.getRequestURI(); final String old_context_path = baseRequest.getContextPath(); final String old_servlet_path = baseRequest.getServletPath(); final String old_path_info = baseRequest.getPathInfo(); final String old_query = baseRequest.getQueryString(); final Attributes old_attr = baseRequest.getAttributes(); final DispatcherType old_type = baseRequest.getDispatcherType(); MultiMap<String> old_params = baseRequest.getParameters(); try { baseRequest.setHandled(false); baseRequest.setDispatcherType(dispatch); if (_named != null) _contextHandler.handle( _named, baseRequest, (HttpServletRequest) request, (HttpServletResponse) response); else { // process any query string from the dispatch URL String query = _dQuery; if (query != null) { // force parameter extraction if (old_params == null) { baseRequest.extractParameters(); old_params = baseRequest.getParameters(); } baseRequest.mergeQueryString(query); } ForwardAttributes attr = new ForwardAttributes(old_attr); // If we have already been forwarded previously, then keep using the established // original value. Otherwise, this is the first forward and we need to establish the values. // Note: the established value on the original request for pathInfo and // for queryString is allowed to be null, but cannot be null for the other values. if (old_attr.getAttribute(FORWARD_REQUEST_URI) != null) { attr._pathInfo = (String) old_attr.getAttribute(FORWARD_PATH_INFO); attr._query = (String) old_attr.getAttribute(FORWARD_QUERY_STRING); attr._requestURI = (String) old_attr.getAttribute(FORWARD_REQUEST_URI); attr._contextPath = (String) old_attr.getAttribute(FORWARD_CONTEXT_PATH); attr._servletPath = (String) old_attr.getAttribute(FORWARD_SERVLET_PATH); } else { attr._pathInfo = old_path_info; attr._query = old_query; attr._requestURI = old_uri; attr._contextPath = old_context_path; attr._servletPath = old_servlet_path; } baseRequest.setRequestURI(_uri); baseRequest.setContextPath(_contextHandler.getContextPath()); baseRequest.setServletPath(null); baseRequest.setPathInfo(_uri); baseRequest.setAttributes(attr); _contextHandler.handle( _path, baseRequest, (HttpServletRequest) request, (HttpServletResponse) response); if (!baseRequest.getHttpChannelState().isAsync()) commitResponse(response, baseRequest); } } finally { baseRequest.setHandled(old_handled); baseRequest.setRequestURI(old_uri); baseRequest.setContextPath(old_context_path); baseRequest.setServletPath(old_servlet_path); baseRequest.setPathInfo(old_path_info); baseRequest.setAttributes(old_attr); baseRequest.setParameters(old_params); baseRequest.setQueryString(old_query); baseRequest.setDispatcherType(old_type); } }
/* * @see javax.servlet.RequestDispatcher#include(javax.servlet.ServletRequest, javax.servlet.ServletResponse) */ @Override public void include(ServletRequest request, ServletResponse response) throws ServletException, IOException { Request baseRequest = (request instanceof Request) ? ((Request) request) : HttpChannel.getCurrentHttpChannel().getRequest(); if (!(request instanceof HttpServletRequest)) request = new ServletRequestHttpWrapper(request); if (!(response instanceof HttpServletResponse)) response = new ServletResponseHttpWrapper(response); final DispatcherType old_type = baseRequest.getDispatcherType(); final Attributes old_attr = baseRequest.getAttributes(); MultiMap<String> old_params = baseRequest.getParameters(); try { baseRequest.setDispatcherType(DispatcherType.INCLUDE); baseRequest.getResponse().include(); if (_named != null) _contextHandler.handle( _named, baseRequest, (HttpServletRequest) request, (HttpServletResponse) response); else { String query = _dQuery; if (query != null) { // force parameter extraction if (old_params == null) { baseRequest.extractParameters(); old_params = baseRequest.getParameters(); } MultiMap<String> parameters = new MultiMap<>(); UrlEncoded.decodeTo(query, parameters, baseRequest.getCharacterEncoding(), -1); if (old_params != null) { // Merge parameters. parameters.addAllValues(old_params); } baseRequest.setParameters(parameters); } IncludeAttributes attr = new IncludeAttributes(old_attr); attr._requestURI = _uri; attr._contextPath = _contextHandler.getContextPath(); attr._servletPath = null; // set by ServletHandler attr._pathInfo = _path; attr._query = query; baseRequest.setAttributes(attr); _contextHandler.handle( _path, baseRequest, (HttpServletRequest) request, (HttpServletResponse) response); } } finally { baseRequest.setAttributes(old_attr); baseRequest.getResponse().included(); baseRequest.setParameters(old_params); baseRequest.setDispatcherType(old_type); } }