public PortForwardOpenListener(
     ClientConnection masterPortForwardConnection,
     final String urlPath,
     final int targetPort,
     final AtomicInteger requestId,
     final Pool<ByteBuffer> pool,
     final OptionMap undertowOptions) {
   this.masterPortForwardConnection = masterPortForwardConnection;
   this.urlPath = urlPath;
   this.targetPort = targetPort;
   this.requestId = requestId;
   this.undertowOptions = undertowOptions;
   this.bufferPool = new XnioByteBufferPool(pool);
   Pooled<ByteBuffer> buf = pool.allocate();
   this.bufferSize = buf.getResource().remaining();
   buf.free();
 }
 /**
  * Handles writing out the header data. It can also take a byte buffer of user data, to enable
  * both user data and headers to be written out in a single operation, which has a noticeable
  * performance impact.
  *
  * <p>It is up to the caller to note the current position of this buffer before and after they
  * call this method, and use this to figure out how many bytes (if any) have been written.
  *
  * @param state
  * @param userData
  * @return
  * @throws java.io.IOException
  */
 private int processWrite(int state, final ByteBuffer userData) throws IOException {
   if (state == STATE_START) {
     pooledBuffer = pool.allocate();
   }
   ClientRequest request = this.request;
   ByteBuffer buffer = pooledBuffer.getResource();
   Iterator<HttpString> nameIterator = this.nameIterator;
   Iterator<String> valueIterator = this.valueIterator;
   int charIndex = this.charIndex;
   int length;
   String string = this.string;
   HttpString headerName = this.headerName;
   int res;
   // BUFFER IS FLIPPED COMING IN
   if (state != STATE_START && buffer.hasRemaining()) {
     log.trace("Flushing remaining buffer");
     do {
       res = next.write(buffer);
       if (res == 0) {
         return state;
       }
     } while (buffer.hasRemaining());
   }
   buffer.clear();
   // BUFFER IS NOW EMPTY FOR FILLING
   for (; ; ) {
     switch (state) {
       case STATE_BODY:
         {
           // shouldn't be possible, but might as well do the right thing anyway
           return state;
         }
       case STATE_START:
         {
           log.trace("Starting request");
           // we assume that our buffer has enough space for the initial request line plus one more
           // CR+LF
           assert buffer.remaining() >= 50;
           request.getMethod().appendTo(buffer);
           buffer.put((byte) ' ');
           string = request.getPath();
           length = string.length();
           for (charIndex = 0; charIndex < length; charIndex++) {
             buffer.put((byte) string.charAt(charIndex));
           }
           buffer.put((byte) ' ');
           request.getProtocol().appendTo(buffer);
           buffer.put((byte) '\r').put((byte) '\n');
           HeaderMap headers = request.getRequestHeaders();
           nameIterator = headers.getHeaderNames().iterator();
           if (!nameIterator.hasNext()) {
             log.trace("No request headers");
             buffer.put((byte) '\r').put((byte) '\n');
             buffer.flip();
             while (buffer.hasRemaining()) {
               res = next.write(buffer);
               if (res == 0) {
                 log.trace("Continuation");
                 return STATE_BUF_FLUSH;
               }
             }
             pooledBuffer.free();
             pooledBuffer = null;
             log.trace("Body");
             return STATE_BODY;
           }
           headerName = nameIterator.next();
           charIndex = 0;
           // fall thru
         }
       case STATE_HDR_NAME:
         {
           log.tracef("Processing header '%s'", headerName);
           length = headerName.length();
           while (charIndex < length) {
             if (buffer.hasRemaining()) {
               buffer.put(headerName.byteAt(charIndex++));
             } else {
               log.trace("Buffer flush");
               buffer.flip();
               do {
                 res = next.write(buffer);
                 if (res == 0) {
                   this.string = string;
                   this.headerName = headerName;
                   this.charIndex = charIndex;
                   this.valueIterator = valueIterator;
                   this.nameIterator = nameIterator;
                   log.trace("Continuation");
                   return STATE_HDR_NAME;
                 }
               } while (buffer.hasRemaining());
               buffer.clear();
             }
           }
           // fall thru
         }
       case STATE_HDR_D:
         {
           if (!buffer.hasRemaining()) {
             buffer.flip();
             do {
               res = next.write(buffer);
               if (res == 0) {
                 log.trace("Continuation");
                 this.string = string;
                 this.headerName = headerName;
                 this.charIndex = charIndex;
                 this.valueIterator = valueIterator;
                 this.nameIterator = nameIterator;
                 return STATE_HDR_D;
               }
             } while (buffer.hasRemaining());
             buffer.clear();
           }
           buffer.put((byte) ':');
           // fall thru
         }
       case STATE_HDR_DS:
         {
           if (!buffer.hasRemaining()) {
             buffer.flip();
             do {
               res = next.write(buffer);
               if (res == 0) {
                 log.trace("Continuation");
                 this.string = string;
                 this.headerName = headerName;
                 this.charIndex = charIndex;
                 this.valueIterator = valueIterator;
                 this.nameIterator = nameIterator;
                 return STATE_HDR_DS;
               }
             } while (buffer.hasRemaining());
             buffer.clear();
           }
           buffer.put((byte) ' ');
           if (valueIterator == null) {
             valueIterator = request.getRequestHeaders().get(headerName).iterator();
           }
           assert valueIterator.hasNext();
           string = valueIterator.next();
           charIndex = 0;
           // fall thru
         }
       case STATE_HDR_VAL:
         {
           log.tracef("Processing header value '%s'", string);
           length = string.length();
           while (charIndex < length) {
             if (buffer.hasRemaining()) {
               buffer.put((byte) string.charAt(charIndex++));
             } else {
               buffer.flip();
               do {
                 res = next.write(buffer);
                 if (res == 0) {
                   this.string = string;
                   this.headerName = headerName;
                   this.charIndex = charIndex;
                   this.valueIterator = valueIterator;
                   this.nameIterator = nameIterator;
                   log.trace("Continuation");
                   return STATE_HDR_VAL;
                 }
               } while (buffer.hasRemaining());
               buffer.clear();
             }
           }
           charIndex = 0;
           if (!valueIterator.hasNext()) {
             if (!buffer.hasRemaining()) {
               buffer.flip();
               do {
                 res = next.write(buffer);
                 if (res == 0) {
                   log.trace("Continuation");
                   return STATE_HDR_EOL_CR;
                 }
               } while (buffer.hasRemaining());
               buffer.clear();
             }
             buffer.put((byte) 13); // CR
             if (!buffer.hasRemaining()) {
               buffer.flip();
               do {
                 res = next.write(buffer);
                 if (res == 0) {
                   log.trace("Continuation");
                   return STATE_HDR_EOL_LF;
                 }
               } while (buffer.hasRemaining());
               buffer.clear();
             }
             buffer.put((byte) 10); // LF
             if (nameIterator.hasNext()) {
               headerName = nameIterator.next();
               valueIterator = null;
               state = STATE_HDR_NAME;
               break;
             } else {
               if (!buffer.hasRemaining()) {
                 buffer.flip();
                 do {
                   res = next.write(buffer);
                   if (res == 0) {
                     log.trace("Continuation");
                     return STATE_HDR_FINAL_CR;
                   }
                 } while (buffer.hasRemaining());
                 buffer.clear();
               }
               buffer.put((byte) 13); // CR
               if (!buffer.hasRemaining()) {
                 buffer.flip();
                 do {
                   res = next.write(buffer);
                   if (res == 0) {
                     log.trace("Continuation");
                     return STATE_HDR_FINAL_LF;
                   }
                 } while (buffer.hasRemaining());
                 buffer.clear();
               }
               buffer.put((byte) 10); // LF
               this.nameIterator = null;
               this.valueIterator = null;
               this.string = null;
               buffer.flip();
               // for performance reasons we use a gather write if there is user data
               if (userData == null) {
                 do {
                   res = next.write(buffer);
                   if (res == 0) {
                     log.trace("Continuation");
                     return STATE_BUF_FLUSH;
                   }
                 } while (buffer.hasRemaining());
               } else {
                 ByteBuffer[] b = {buffer, userData};
                 do {
                   long r = next.write(b, 0, b.length);
                   if (r == 0 && buffer.hasRemaining()) {
                     log.trace("Continuation");
                     return STATE_BUF_FLUSH;
                   }
                 } while (buffer.hasRemaining());
               }
               pooledBuffer.free();
               pooledBuffer = null;
               log.trace("Body");
               return STATE_BODY;
             }
             // not reached
           }
           // fall thru
         }
         // Clean-up states
       case STATE_HDR_EOL_CR:
         {
           if (!buffer.hasRemaining()) {
             buffer.flip();
             do {
               res = next.write(buffer);
               if (res == 0) {
                 log.trace("Continuation");
                 return STATE_HDR_EOL_CR;
               }
             } while (buffer.hasRemaining());
             buffer.clear();
           }
           buffer.put((byte) 13); // CR
         }
       case STATE_HDR_EOL_LF:
         {
           if (!buffer.hasRemaining()) {
             buffer.flip();
             do {
               res = next.write(buffer);
               if (res == 0) {
                 log.trace("Continuation");
                 return STATE_HDR_EOL_LF;
               }
             } while (buffer.hasRemaining());
             buffer.clear();
           }
           buffer.put((byte) 10); // LF
           if (valueIterator.hasNext()) {
             state = STATE_HDR_NAME;
             break;
           } else if (nameIterator.hasNext()) {
             headerName = nameIterator.next();
             valueIterator = null;
             state = STATE_HDR_NAME;
             break;
           }
           // fall thru
         }
       case STATE_HDR_FINAL_CR:
         {
           if (!buffer.hasRemaining()) {
             buffer.flip();
             do {
               res = next.write(buffer);
               if (res == 0) {
                 log.trace("Continuation");
                 return STATE_HDR_FINAL_CR;
               }
             } while (buffer.hasRemaining());
             buffer.clear();
           }
           buffer.put((byte) 13); // CR
           // fall thru
         }
       case STATE_HDR_FINAL_LF:
         {
           if (!buffer.hasRemaining()) {
             buffer.flip();
             do {
               res = next.write(buffer);
               if (res == 0) {
                 log.trace("Continuation");
                 return STATE_HDR_FINAL_LF;
               }
             } while (buffer.hasRemaining());
             buffer.clear();
           }
           buffer.put((byte) 10); // LF
           this.nameIterator = null;
           this.valueIterator = null;
           this.string = null;
           buffer.flip();
           // for performance reasons we use a gather write if there is user data
           if (userData == null) {
             do {
               res = next.write(buffer);
               if (res == 0) {
                 log.trace("Continuation");
                 return STATE_BUF_FLUSH;
               }
             } while (buffer.hasRemaining());
           } else {
             ByteBuffer[] b = {buffer, userData};
             do {
               long r = next.write(b, 0, b.length);
               if (r == 0 && buffer.hasRemaining()) {
                 log.trace("Continuation");
                 return STATE_BUF_FLUSH;
               }
             } while (buffer.hasRemaining());
           }
           // fall thru
         }
       case STATE_BUF_FLUSH:
         {
           // buffer was successfully flushed above
           pooledBuffer.free();
           pooledBuffer = null;
           return STATE_BODY;
         }
       default:
         {
           throw new IllegalStateException();
         }
     }
   }
 }
Exemple #3
0
  /**
   * Initiate a low-copy transfer between two stream channels. The pool should be a direct buffer
   * pool for best performance.
   *
   * @param source the source channel
   * @param sink the target channel
   * @param sourceListener the source listener to set and call when the transfer is complete, or
   *     {@code null} to clear the listener at that time
   * @param sinkListener the target listener to set and call when the transfer is complete, or
   *     {@code null} to clear the listener at that time
   * @param readExceptionHandler the read exception handler to call if an error occurs during a read
   *     operation
   * @param writeExceptionHandler the write exception handler to call if an error occurs during a
   *     write operation
   * @param pool the pool from which the transfer buffer should be allocated
   */
  public static <I extends StreamSourceChannel, O extends StreamSinkChannel> void initiateTransfer(
      final I source,
      final O sink,
      final ChannelListener<? super I> sourceListener,
      final ChannelListener<? super O> sinkListener,
      final ChannelExceptionHandler<? super I> readExceptionHandler,
      final ChannelExceptionHandler<? super O> writeExceptionHandler,
      Pool<ByteBuffer> pool) {
    if (pool == null) {
      throw UndertowMessages.MESSAGES.argumentCannotBeNull("pool");
    }
    final Pooled<ByteBuffer> allocated = pool.allocate();
    boolean free = true;
    try {
      final ByteBuffer buffer = allocated.getResource();
      long read;
      for (; ; ) {
        try {
          read = source.read(buffer);
          buffer.flip();
        } catch (IOException e) {
          ChannelListeners.invokeChannelExceptionHandler(source, readExceptionHandler, e);
          return;
        }
        if (read == 0 && !buffer.hasRemaining()) {
          break;
        }
        if (read == -1 && !buffer.hasRemaining()) {
          done(source, sink, sourceListener, sinkListener);
          return;
        }
        while (buffer.hasRemaining()) {
          final int res;
          try {
            res = sink.write(buffer);
          } catch (IOException e) {
            ChannelListeners.invokeChannelExceptionHandler(sink, writeExceptionHandler, e);
            return;
          }
          if (res == 0) {
            break;
          }
        }
        if (buffer.hasRemaining()) {
          break;
        }
        buffer.clear();
      }
      Pooled<ByteBuffer> current = null;
      if (buffer.hasRemaining()) {
        current = allocated;
        free = false;
      }

      final TransferListener<I, O> listener =
          new TransferListener<I, O>(
              pool,
              current,
              source,
              sink,
              sourceListener,
              sinkListener,
              writeExceptionHandler,
              readExceptionHandler,
              read == -1);
      sink.getWriteSetter().set(listener);
      source.getReadSetter().set(listener);
      // we resume both reads and writes, as we want to keep trying to fill the buffer
      if (current == null || buffer.capacity() != buffer.remaining()) {
        // we don't resume if the buffer is 100% full
        source.resumeReads();
      }
      if (current != null) {
        // we don't resume writes if we have nothing to write
        sink.resumeWrites();
      }
    } finally {
      if (free) {
        allocated.free();
      }
    }
  }
Exemple #4
0
    public void handleEvent(final Channel channel) {
      if (done) {
        if (channel instanceof StreamSinkChannel) {
          ((StreamSinkChannel) channel).suspendWrites();
        } else if (channel instanceof StreamSourceChannel) {
          ((StreamSourceChannel) channel).suspendReads();
        }
        return;
      }
      boolean noWrite = false;
      if (pooledBuffer == null) {
        pooledBuffer = pool.allocate();
        noWrite = true;
      } else if (channel instanceof StreamSourceChannel) {
        noWrite = true; // attempt a read first, as this is a read notification
        pooledBuffer.getResource().compact();
      }

      final ByteBuffer buffer = pooledBuffer.getResource();
      try {
        long read;

        for (; ; ) {
          boolean writeFailed = false;
          // always attempt to write first if we have the buffer
          if (!noWrite) {
            while (buffer.hasRemaining()) {
              final int res;
              try {
                res = sink.write(buffer);
              } catch (IOException e) {
                pooledBuffer.free();
                pooledBuffer = null;
                done = true;
                ChannelListeners.invokeChannelExceptionHandler(sink, writeExceptionHandler, e);
                return;
              }
              if (res == 0) {
                writeFailed = true;
                break;
              }
            }
            if (sourceDone && !buffer.hasRemaining()) {
              done = true;
              done(source, sink, sourceListener, sinkListener);
              return;
            }
            buffer.compact();
          }
          noWrite = false;

          if (buffer.hasRemaining() && !sourceDone) {
            try {
              read = source.read(buffer);
              buffer.flip();
            } catch (IOException e) {
              pooledBuffer.free();
              pooledBuffer = null;
              done = true;
              ChannelListeners.invokeChannelExceptionHandler(source, readExceptionHandler, e);
              return;
            }
            if (read == 0) {
              break;
            } else if (read == -1) {
              sourceDone = true;
              if (!buffer.hasRemaining()) {
                done = true;
                done(source, sink, sourceListener, sinkListener);
                return;
              }
            }
          } else {
            buffer.flip();
            if (writeFailed) {
              break;
            }
          }
        }
        // suspend writes if there is nothing to write
        if (!buffer.hasRemaining()) {
          sink.suspendWrites();
        } else if (!sink.isWriteResumed()) {
          sink.resumeWrites();
        }
        // suspend reads if there is nothing to read
        if (buffer.remaining() == buffer.capacity()) {
          source.suspendReads();
        } else if (!source.isReadResumed()) {
          source.resumeReads();
        }
      } finally {
        if (pooledBuffer != null && !buffer.hasRemaining()) {
          pooledBuffer.free();
          pooledBuffer = null;
        }
      }
    }