docs: private int processWrite(int state, final ByteBuffer userData) throws IOException {
          if (state == STATE_START) {
              pooledBuffer = pool.allocate();
          }
          ClientRequest request = this.request;
          ByteBuffer buffer = pooledBuffer.getBuffer();
          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");
                      int len = request.getMethod().length() + request.getPath().length() + request.getProtocol().length() + 4;
  
                      // test that our buffer has enough space for the initial request line plus one more CR+LF
                      if(len <= buffer.remaining()) {
                          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');
                      } else {
                          StringBuilder sb = new StringBuilder(len);
                          sb.append(request.getMethod().toString());
                          sb.append(" ");
                          sb.append(request.getPath());
                          sb.append(" ");
                          sb.append(request.getProtocol());
                          sb.append("\r\n");
                          string = sb.toString();
                          charIndex = 0;
                          state = STATE_URL;
                          break;
                      }
                      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.close();
                          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.close();
                              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.close();
                      pooledBuffer = null;
                      return STATE_BODY;
                  }
                  case STATE_URL: {
                      for(int i = charIndex; i < string.length(); ++i) {
                          if(!buffer.hasRemaining()) {
                              buffer.flip();
                              do {
                                  res = next.write(buffer);
                                  if (res == 0) {
                                      log.trace("Continuation");
                                      this.charIndex = i;
                                      this.string = string;
                                      this.state = STATE_URL;
                                      return STATE_URL;
                                  }
                              } while (buffer.hasRemaining());
                              buffer.clear();
                          }
                          buffer.put((byte) string.charAt(i));
                      }
  
                      HeaderMap headers = request.getRequestHeaders();
                      nameIterator = headers.getHeaderNames().iterator();
                      state = STATE_HDR_NAME;
                      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.close();
                          pooledBuffer = null;
                          log.trace("Body");
                          return STATE_BODY;
                      }
                      headerName = nameIterator.next();
                      charIndex = 0;
                      break;
                  }
                  default: {
                      throw new IllegalStateException();
                  }
              }
          }
      }