ttomcat-1778514358873.zip-extract/apache-tomcat-11.0.18-src/java/org/apache/coyote/http11/Http11OutputBuffer.java

Path
ttomcat-1778514358873.zip-extract/apache-tomcat-11.0.18-src/java/org/apache/coyote/http11/Http11OutputBuffer.java
Status
scanned
Type
file
Name
Http11OutputBuffer.java
Extension
.java
Programming language
Java
Mime type
text/plain
File type
ASCII text, with CRLF line terminators
Tag

      
    
Rootfs path

      
    
Size
17341 (16.9 KB)
MD5
bc56a7b2ef5e1247d67273f57ffa6fce
SHA1
97763aa6e8f483416ec3b9769140fdcbbe6ac1af
SHA256
f26eaf62db7f4b979490effd830deebce33dabb1ad29d91526d12a062b47a713
SHA512

      
    
SHA1_git
833dad6540f40198682af30a12204db8bf5c93a1
Is binary

      
    
Is text
True
Is archive

      
    
Is media

      
    
Is legal

      
    
Is manifest

      
    
Is readme

      
    
Is top level

      
    
Is key file

      
    
Http11OutputBuffer.java | 16.9 KB |

/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.coyote.http11; import java.io.IOException; import java.nio.ByteBuffer; import java.util.Arrays; import org.apache.coyote.ActionCode; import org.apache.coyote.CloseNowException; import org.apache.coyote.Response; import org.apache.tomcat.util.buf.ByteChunk; import org.apache.tomcat.util.buf.MessageBytes; import org.apache.tomcat.util.net.SocketWrapperBase; import org.apache.tomcat.util.res.StringManager; /** * Provides buffering for the HTTP headers (allowing responses to be reset before they have been committed) and the link * to the Socket for writing the headers (once committed) and the response body. Note that buffering of the response * body happens at a higher level. */ public class Http11OutputBuffer implements HttpOutputBuffer { // -------------------------------------------------------------- Variables /** * The string manager for this package. */ protected static final StringManager sm = StringManager.getManager(Http11OutputBuffer.class); // ----------------------------------------------------- Instance Variables /** * Associated Coyote response. */ protected final Response response; private volatile boolean ackSent = false; /** * Finished flag. */ protected boolean responseFinished; /** * The buffer used for header composition. */ protected final ByteBuffer headerBuffer; /** * Filter library for processing the response body. */ protected OutputFilter[] filterLibrary; /** * Active filters for the current request. */ protected OutputFilter[] activeFilters; /** * Index of the last active filter. */ protected int lastActiveFilter; /** * Underlying output buffer. */ protected HttpOutputBuffer outputStreamOutputBuffer; /** * Wrapper for socket where data will be written to. */ protected SocketWrapperBase<?> socketWrapper; /** * Bytes written to client for the current request */ protected long byteCount = 0; protected Http11OutputBuffer(Response response, int headerBufferSize) { this.response = response; headerBuffer = ByteBuffer.allocate(headerBufferSize); filterLibrary = new OutputFilter[0]; activeFilters = new OutputFilter[0]; lastActiveFilter = -1; responseFinished = false; outputStreamOutputBuffer = new SocketOutputBuffer(); } // ------------------------------------------------------------- Properties /** * Add an output filter to the filter library. Note that calling this method resets the currently active filters to * none. * * @param filter The filter to add */ public void addFilter(OutputFilter filter) { OutputFilter[] newFilterLibrary = Arrays.copyOf(filterLibrary, filterLibrary.length + 1); newFilterLibrary[filterLibrary.length] = filter; filterLibrary = newFilterLibrary; activeFilters = new OutputFilter[filterLibrary.length]; } /** * Get filters. * * @return The current filter library containing all possible filters */ public OutputFilter[] getFilters() { return filterLibrary; } /** * Add an output filter to the active filters for the current response. * <p> * The filter does not have to be present in {@link #getFilters()}. * <p> * A filter can only be added to a response once. If the filter has already been added to this response then this * method will be a NO-OP. * * @param filter The filter to add */ public void addActiveFilter(OutputFilter filter) { if (lastActiveFilter == -1) { filter.setBuffer(outputStreamOutputBuffer); } else { for (int i = 0; i <= lastActiveFilter; i++) { if (activeFilters[i] == filter) { return; } } filter.setBuffer(activeFilters[lastActiveFilter]); } activeFilters[++lastActiveFilter] = filter; filter.setResponse(response); } // --------------------------------------------------- OutputBuffer Methods @Override public int doWrite(ByteBuffer chunk) throws IOException { if (!response.isCommitted()) { // Send the connector a request for commit. The connector should // then validate the headers, send them (using sendHeaders) and // set the filters accordingly. response.action(ActionCode.COMMIT, null); } if (lastActiveFilter == -1) { return outputStreamOutputBuffer.doWrite(chunk); } else { return activeFilters[lastActiveFilter].doWrite(chunk); } } @Override public long getBytesWritten() { if (lastActiveFilter == -1) { return outputStreamOutputBuffer.getBytesWritten(); } else { return activeFilters[lastActiveFilter].getBytesWritten(); } } // ----------------------------------------------- HttpOutputBuffer Methods @Override public void flush() throws IOException { if (lastActiveFilter == -1) { outputStreamOutputBuffer.flush(); } else { activeFilters[lastActiveFilter].flush(); } } @Override public void end() throws IOException { if (responseFinished) { return; } if (lastActiveFilter == -1) { outputStreamOutputBuffer.end(); } else { activeFilters[lastActiveFilter].end(); } responseFinished = true; } // --------------------------------------------------------- Public Methods /** * Reset the header buffer if an error occurs during the writing of the headers so the error response can be * written. */ void resetHeaderBuffer() { headerBuffer.position(0).limit(headerBuffer.capacity()); } /** * Recycle the output buffer. This should be called when closing the connection. */ public void recycle() { nextRequest(); socketWrapper = null; } /** * End processing of current HTTP request. Note: All bytes of the current request should have been already consumed. * This method only resets all the pointers so that we are ready to parse the next HTTP request. */ public void nextRequest() { // Recycle filters for (int i = 0; i <= lastActiveFilter; i++) { activeFilters[i].recycle(); } // Recycle response object response.recycle(); // Reset pointers headerBuffer.position(0).limit(headerBuffer.capacity()); lastActiveFilter = -1; ackSent = false; responseFinished = false; byteCount = 0; } public void init(SocketWrapperBase<?> socketWrapper) { this.socketWrapper = socketWrapper; } public void sendAck() throws IOException { // It possible that the protocol configuration is changed between the // request being received and the first read of the body. That could lead // to multiple calls to this method so ensure the ACK is only sent once. if (!response.isCommitted() && !ackSent) { ackSent = true; socketWrapper.write(isBlocking(), Constants.ACK_BYTES, 0, Constants.ACK_BYTES.length); if (flushBuffer(true)) { throw new IOException(sm.getString("iob.failedwrite.ack")); } } } /** * Commit the response. * * @throws IOException an underlying I/O error occurred */ protected void commit() throws IOException { response.setCommitted(true); writeHeaders(); } protected void writeHeaders() throws IOException { if (headerBuffer.position() > 0) { // Sending the response header buffer headerBuffer.flip(); try { SocketWrapperBase<?> socketWrapper = this.socketWrapper; if (socketWrapper != null) { socketWrapper.write(isBlocking(), headerBuffer); } else { throw new CloseNowException(sm.getString("iob.failedwrite")); } } finally { headerBuffer.position(0).limit(headerBuffer.capacity()); } } } /** * Send the response status line. * * @param status The HTTP status code to include in the status line */ public void sendStatus(int status) { // Write protocol name write(Constants.HTTP_11_BYTES); headerBuffer.put(Constants.SP); // Write status code switch (status) { case 200: write(Constants._200_BYTES); break; case 400: write(Constants._400_BYTES); break; case 404: write(Constants._404_BYTES); break; default: write(status); } headerBuffer.put(Constants.SP); // The reason phrase is optional but the space before it is not. Skip // sending the reason phrase. Clients should ignore it (RFC 7230) and it // just wastes bytes. headerBuffer.put(Constants.CR).put(Constants.LF); } /** * Send a header. * * @param name Header name * @param value Header value */ public void sendHeader(MessageBytes name, MessageBytes value) { write(name); headerBuffer.put(Constants.COLON).put(Constants.SP); write(value); headerBuffer.put(Constants.CR).put(Constants.LF); } /** * End the header block. */ public void endHeaders() { headerBuffer.put(Constants.CR).put(Constants.LF); } /** * This method will write the contents of the specified message bytes buffer to the output stream, without * filtering. This method is meant to be used to write the response header. * * @param mb data to be written */ private void write(MessageBytes mb) { if (mb.getType() != MessageBytes.T_BYTES) { mb.toBytes(); ByteChunk bc = mb.getByteChunk(); // Need to filter out CTLs excluding TAB. ISO-8859-1 and UTF-8 // values will be OK. Strings using other encodings may be // corrupted. byte[] buffer = bc.getBuffer(); for (int i = bc.getStart(); i < bc.getLength(); i++) { // byte values are signed i.e. -128 to 127 // The values are used unsigned. 0 to 31 are CTLs so they are // filtered (apart from TAB which is 9). 127 is a control (DEL). // The values 128 to 255 are all OK. Converting those to signed // gives -128 to -1. if ((buffer[i] > -1 && buffer[i] <= 31 && buffer[i] != 9) || buffer[i] == 127) { buffer[i] = ' '; } } } write(mb.getByteChunk()); } /** * This method will write the contents of the specified byte chunk to the output stream, without filtering. This * method is meant to be used to write the response header. * * @param bc data to be written */ private void write(ByteChunk bc) { // Writing the byte chunk to the output buffer int length = bc.getLength(); checkLengthBeforeWrite(length); headerBuffer.put(bc.getBytes(), bc.getStart(), length); } /** * This method will write the contents of the specified byte buffer to the output stream, without filtering. This * method is meant to be used to write the response header. * * @param b data to be written */ public void write(byte[] b) { checkLengthBeforeWrite(b.length); // Writing the byte chunk to the output buffer headerBuffer.put(b); } /** * This method will write the specified integer to the output stream. This method is meant to be used to write the * response header. * * @param value data to be written */ private void write(int value) { // From the Tomcat 3.3 HTTP/1.0 connector String s = Integer.toString(value); int len = s.length(); checkLengthBeforeWrite(len); for (int i = 0; i < len; i++) { char c = s.charAt(i); headerBuffer.put((byte) c); } } /** * Checks to see if there is enough space in the buffer to write the requested number of bytes. */ private void checkLengthBeforeWrite(int length) { // "+ 4": BZ 57509. Reserve space for CR/LF/COLON/SP characters that // are put directly into the buffer following this write operation. if (headerBuffer.position() + length + 4 > headerBuffer.capacity()) { throw new HeadersTooLargeException(sm.getString("iob.responseheadertoolarge.error")); } } // ------------------------------------------------------ Non-blocking writes /** * Writes any remaining buffered data. * * @param block Should this method block until the buffer is empty * * @return <code>true</code> if data remains in the buffer (which can only happen in non-blocking mode) else * <code>false</code>. * * @throws IOException Error writing data */ protected boolean flushBuffer(boolean block) throws IOException { return socketWrapper.flush(block); } /** * Is standard Servlet blocking IO being used for output? * * @return <code>true</code> if this is blocking IO */ protected final boolean isBlocking() { return response.getWriteListener() == null; } protected final boolean isReady() { boolean result = !hasDataToWrite(); if (!result) { socketWrapper.registerWriteInterest(); } return result; } public boolean hasDataToWrite() { return socketWrapper.hasDataToWrite(); } public void registerWriteInterest() { socketWrapper.registerWriteInterest(); } boolean isChunking() { for (int i = 0; i < lastActiveFilter; i++) { if (activeFilters[i] == filterLibrary[Constants.CHUNKED_FILTER]) { return true; } } return false; } // ------------------------------------------ SocketOutputBuffer Inner Class /** * This class is an output buffer which will write data to a socket. */ protected class SocketOutputBuffer implements HttpOutputBuffer { @Override public int doWrite(ByteBuffer chunk) throws IOException { try { int len = chunk.remaining(); SocketWrapperBase<?> socketWrapper = Http11OutputBuffer.this.socketWrapper; if (socketWrapper != null) { socketWrapper.write(isBlocking(), chunk); } else { throw new CloseNowException(sm.getString("iob.failedwrite")); } len -= chunk.remaining(); byteCount += len; return len; } catch (IOException ioe) { response.action(ActionCode.CLOSE_NOW, ioe); // Re-throw throw ioe; } } @Override public long getBytesWritten() { return byteCount; } @Override public void end() throws IOException { socketWrapper.flush(response.getWriteListener() == null); } @Override public void flush() throws IOException { socketWrapper.flush(isBlocking()); } } }
Detected license expression
apache-2.0
Detected license expression (SPDX)
Apache-2.0
Percentage of license text
7.22
Copyrights

      
    
Holders

      
    
Authors

      
    
License detections License expression License expression SPDX
apache_2_0-4bde3f57-78aa-4201-96bf-531cba09e7de apache-2.0 Apache-2.0
URL Start line End line
http://www.apache.org/licenses/LICENSE-2.0 9 9