use of io.undertow.util.HttpString in project undertow by undertow-io.
the class HttpRequestConduit method processWrite.
/**
* 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.
*
* 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.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();
}
// CR
buffer.put((byte) 13);
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();
}
// LF
buffer.put((byte) 10);
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();
}
// CR
buffer.put((byte) 13);
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();
}
// LF
buffer.put((byte) 10);
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();
}
// CR
buffer.put((byte) 13);
}
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();
}
// LF
buffer.put((byte) 10);
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();
}
// CR
buffer.put((byte) 13);
// 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();
}
// LF
buffer.put((byte) 10);
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();
}
}
}
}
use of io.undertow.util.HttpString in project undertow by undertow-io.
the class HttpResponseParser method handleHeaderValue.
/**
* Parses a header value. This is called from the generated bytecode.
*
* @param buffer The buffer
* @param state The current state
* @param builder The exchange builder
* @return The number of bytes remaining
*/
@SuppressWarnings("unused")
final void handleHeaderValue(ByteBuffer buffer, ResponseParseState state, HttpResponseBuilder builder) {
StringBuilder stringBuilder = state.stringBuilder;
if (stringBuilder == null) {
stringBuilder = new StringBuilder();
state.parseState = 0;
}
int parseState = state.parseState;
while (buffer.hasRemaining()) {
final byte next = buffer.get();
switch(parseState) {
case NORMAL:
{
if (next == '\r') {
parseState = BEGIN_LINE_END;
} else if (next == '\n') {
parseState = LINE_END;
} else if (next == ' ' || next == '\t') {
parseState = WHITESPACE;
} else {
stringBuilder.append((char) next);
}
break;
}
case WHITESPACE:
{
if (next == '\r') {
parseState = BEGIN_LINE_END;
} else if (next == '\n') {
parseState = LINE_END;
} else if (next == ' ' || next == '\t') {
} else {
if (stringBuilder.length() > 0) {
stringBuilder.append(' ');
}
stringBuilder.append((char) next);
parseState = NORMAL;
}
break;
}
case LINE_END:
case BEGIN_LINE_END:
{
if (next == '\n' && parseState == BEGIN_LINE_END) {
parseState = LINE_END;
} else if (next == '\t' || next == ' ') {
//this is a continuation
parseState = WHITESPACE;
} else {
//we have a header
HttpString nextStandardHeader = state.nextHeader;
String headerValue = stringBuilder.toString();
//TODO: we need to decode this according to RFC-2047 if we have seen a =? symbol
builder.getResponseHeaders().add(nextStandardHeader, headerValue);
state.nextHeader = null;
state.leftOver = next;
state.stringBuilder.setLength(0);
if (next == '\r') {
parseState = AWAIT_DATA_END;
} else {
state.state = ResponseParseState.HEADER;
state.parseState = 0;
return;
}
}
break;
}
case AWAIT_DATA_END:
{
state.state = ResponseParseState.PARSE_COMPLETE;
return;
}
}
}
//we only write to the state if we did not finish parsing
state.parseState = parseState;
}
use of io.undertow.util.HttpString in project undertow by undertow-io.
the class HttpResponseParser method httpStrings.
/**
* This is a bit of hack to enable the parser to get access to the HttpString's that are sorted
* in the static fields of the relevant classes. This means that in most cases a HttpString comparison
* will take the fast path == route, as they will be the same object
*
* @return
*/
protected static Map<String, HttpString> httpStrings() {
final Map<String, HttpString> results = new HashMap<>();
final Class[] classs = { Headers.class, Methods.class, Protocols.class };
for (Class<?> c : classs) {
for (Field field : c.getDeclaredFields()) {
if (field.getType().equals(HttpString.class)) {
field.setAccessible(true);
HttpString result = null;
try {
result = (HttpString) field.get(null);
results.put(result.toString(), result);
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
}
}
}
}
return results;
}
use of io.undertow.util.HttpString in project undertow by undertow-io.
the class HpackDecoder method decode.
/**
* Decodes the provided frame data. If this method leaves data in the buffer then
* this buffer should be compacted so this data is preserved, unless there is no
* more data in which case this should be considered a protocol error.
*
* @param buffer The buffer
*/
public void decode(ByteBuffer buffer, boolean moreData) throws HpackException {
while (buffer.hasRemaining()) {
int originalPos = buffer.position();
byte b = buffer.get();
if ((b & 0b10000000) != 0) {
first = false;
//if the first bit is set it is an indexed header field
//unget the byte
buffer.position(buffer.position() - 1);
//prefix is 7
int index = Hpack.decodeInteger(buffer, 7);
if (index == -1) {
if (!moreData) {
throw UndertowMessages.MESSAGES.hpackFailed();
}
buffer.position(originalPos);
return;
} else if (index == 0) {
throw UndertowMessages.MESSAGES.zeroNotValidHeaderTableIndex();
}
handleIndex(index);
} else if ((b & 0b01000000) != 0) {
first = false;
//Literal Header Field with Incremental Indexing
HttpString headerName = readHeaderName(buffer, 6);
if (headerName == null) {
if (!moreData) {
throw UndertowMessages.MESSAGES.hpackFailed();
}
buffer.position(originalPos);
return;
}
String headerValue = readHpackString(buffer);
if (headerValue == null) {
if (!moreData) {
throw UndertowMessages.MESSAGES.hpackFailed();
}
buffer.position(originalPos);
return;
}
headerEmitter.emitHeader(headerName, headerValue, false);
addEntryToHeaderTable(new HeaderField(headerName, headerValue));
} else if ((b & 0b11110000) == 0) {
first = false;
//Literal Header Field without Indexing
HttpString headerName = readHeaderName(buffer, 4);
if (headerName == null) {
if (!moreData) {
throw UndertowMessages.MESSAGES.hpackFailed();
}
buffer.position(originalPos);
return;
}
String headerValue = readHpackString(buffer);
if (headerValue == null) {
if (!moreData) {
throw UndertowMessages.MESSAGES.hpackFailed();
}
buffer.position(originalPos);
return;
}
headerEmitter.emitHeader(headerName, headerValue, false);
} else if ((b & 0b11110000) == 0b00010000) {
first = false;
//Literal Header Field never indexed
HttpString headerName = readHeaderName(buffer, 4);
if (headerName == null) {
buffer.position(originalPos);
return;
}
String headerValue = readHpackString(buffer);
if (headerValue == null) {
if (!moreData) {
throw UndertowMessages.MESSAGES.hpackFailed();
}
buffer.position(originalPos);
return;
}
headerEmitter.emitHeader(headerName, headerValue, true);
} else if ((b & 0b11100000) == 0b00100000) {
if (!first) {
throw new HpackException();
}
//context update max table size change
if (!handleMaxMemorySizeChange(buffer, originalPos)) {
return;
}
} else {
throw UndertowMessages.MESSAGES.invalidHpackEncoding(b);
}
}
if (!moreData) {
first = true;
}
}
use of io.undertow.util.HttpString in project undertow by undertow-io.
the class HttpClientConnection method sendRequest.
@Override
public void sendRequest(final ClientRequest request, final ClientCallback<ClientExchange> clientCallback) {
if (http2Delegate != null) {
http2Delegate.sendRequest(request, clientCallback);
return;
}
count++;
if (anyAreSet(state, UPGRADE_REQUESTED | UPGRADED | CLOSE_REQ | CLOSED)) {
clientCallback.failed(UndertowClientMessages.MESSAGES.invalidConnectionState());
return;
}
final HttpClientExchange httpClientExchange = new HttpClientExchange(clientCallback, request, this);
boolean ssl = this.connection instanceof SslConnection;
if (!ssl && !http2Tried && options.get(UndertowOptions.ENABLE_HTTP2, false) && !request.getRequestHeaders().contains(Headers.UPGRADE) && request.getMethod().equals(Methods.GET)) {
//this is the first request, as we want to try a HTTP2 upgrade
request.getRequestHeaders().put(new HttpString("HTTP2-Settings"), Http2ClearClientProvider.createSettingsFrame(options, bufferPool));
request.getRequestHeaders().put(Headers.UPGRADE, Http2Channel.CLEARTEXT_UPGRADE_STRING);
request.getRequestHeaders().put(Headers.CONNECTION, "Upgrade, HTTP2-Settings");
http2Tried = true;
}
if (currentRequest == null) {
initiateRequest(httpClientExchange);
} else {
pendingQueue.add(httpClientExchange);
}
}
Aggregations