예제 #1
0
 /**
  * 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();
         }
     }
   }
 }
예제 #2
0
  @Override
  public void sendRequest(ClientRequest request, ClientCallback<ClientExchange> clientCallback) {
    request.getRequestHeaders().put(PATH, request.getPath());
    request.getRequestHeaders().put(SCHEME, "https");
    request.getRequestHeaders().put(VERSION, request.getProtocol().toString());
    request.getRequestHeaders().put(METHOD, request.getMethod().toString());
    request.getRequestHeaders().put(HOST, request.getRequestHeaders().getFirst(Headers.HOST));
    request.getRequestHeaders().remove(Headers.HOST);

    SpdySynStreamStreamSinkChannel sinkChannel;
    try {
      sinkChannel = spdyChannel.createStream(request.getRequestHeaders());
    } catch (IOException e) {
      clientCallback.failed(e);
      return;
    }
    SpdyClientExchange exchange = new SpdyClientExchange(this, sinkChannel, request);
    currentExchanges.put(sinkChannel.getStreamId(), exchange);

    boolean hasContent = true;

    String fixedLengthString = request.getRequestHeaders().getFirst(CONTENT_LENGTH);
    String transferEncodingString = request.getRequestHeaders().getLast(TRANSFER_ENCODING);
    if (fixedLengthString != null) {
      try {
        long length = Long.parseLong(fixedLengthString);
        hasContent = length != 0;
      } catch (NumberFormatException e) {
        handleError(new IOException(e));
        return;
      }
    } else if (transferEncodingString == null) {
      hasContent = false;
    }
    if (clientCallback != null) {
      clientCallback.completed(exchange);
    }
    if (!hasContent) {
      // if there is no content we flush the response channel.
      // otherwise it is up to the user
      try {
        sinkChannel.shutdownWrites();
        if (!sinkChannel.flush()) {
          sinkChannel
              .getWriteSetter()
              .set(
                  ChannelListeners.flushingChannelListener(
                      null,
                      new ChannelExceptionHandler<StreamSinkChannel>() {
                        @Override
                        public void handleException(
                            StreamSinkChannel channel, IOException exception) {
                          handleError(exception);
                        }
                      }));
          sinkChannel.resumeWrites();
        }
      } catch (IOException e) {
        handleError(e);
      }
    } else if (!sinkChannel.isWriteResumed()) {
      try {
        // TODO: this needs some more thought
        if (!sinkChannel.flush()) {
          sinkChannel
              .getWriteSetter()
              .set(
                  new ChannelListener<StreamSinkChannel>() {
                    @Override
                    public void handleEvent(StreamSinkChannel channel) {
                      try {
                        if (channel.flush()) {
                          channel.suspendWrites();
                        }
                      } catch (IOException e) {
                        handleError(e);
                      }
                    }
                  });
          sinkChannel.resumeWrites();
        }
      } catch (IOException e) {
        handleError(e);
      }
    }
  }