use of io.undertow.client.ClientRequest 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.client.ClientRequest in project undertow by undertow-io.
the class AjpClientConnection method initiateRequest.
private void initiateRequest(AjpClientExchange AjpClientExchange) {
currentRequest = AjpClientExchange;
ClientRequest request = AjpClientExchange.getRequest();
String connectionString = request.getRequestHeaders().getFirst(CONNECTION);
if (connectionString != null) {
if (CLOSE.equalToString(connectionString)) {
state |= CLOSE_REQ;
}
} else if (request.getProtocol() != Protocols.HTTP_1_1) {
state |= CLOSE_REQ;
}
if (request.getRequestHeaders().contains(UPGRADE)) {
state |= UPGRADE_REQUESTED;
}
long length = 0;
String fixedLengthString = request.getRequestHeaders().getFirst(CONTENT_LENGTH);
String transferEncodingString = request.getRequestHeaders().getLast(TRANSFER_ENCODING);
if (fixedLengthString != null) {
length = Long.parseLong(fixedLengthString);
} else if (transferEncodingString != null) {
length = -1;
}
AjpClientRequestClientStreamSinkChannel sinkChannel = connection.sendRequest(request.getMethod(), request.getPath(), request.getProtocol(), request.getRequestHeaders(), request, requestFinishListener);
currentRequest.setRequestChannel(sinkChannel);
AjpClientExchange.invokeReadReadyCallback(AjpClientExchange);
if (length == 0) {
//otherwise it is up to the user
try {
sinkChannel.shutdownWrites();
if (!sinkChannel.flush()) {
handleFailedFlush(sinkChannel);
}
} catch (IOException e) {
handleError(e);
}
}
}
use of io.undertow.client.ClientRequest in project spring-framework by spring-projects.
the class UndertowXhrTransport method executeReceiveRequest.
private void executeReceiveRequest(final TransportRequest transportRequest, final URI url, final HttpHeaders headers, final XhrClientSockJsSession session, final SettableListenableFuture<WebSocketSession> connectFuture) {
if (logger.isTraceEnabled()) {
logger.trace("Starting XHR receive request for " + url);
}
ClientCallback<ClientConnection> clientCallback = new ClientCallback<ClientConnection>() {
@Override
public void completed(ClientConnection connection) {
ClientRequest request = new ClientRequest().setMethod(Methods.POST).setPath(url.getPath());
HttpString headerName = HttpString.tryFromString(HttpHeaders.HOST);
request.getRequestHeaders().add(headerName, url.getHost());
addHttpHeaders(request, headers);
HttpHeaders httpHeaders = transportRequest.getHttpRequestHeaders();
connection.sendRequest(request, createReceiveCallback(transportRequest, url, httpHeaders, session, connectFuture));
}
@Override
public void failed(IOException ex) {
throw new SockJsTransportFailureException("Failed to execute request to " + url, ex);
}
};
this.httpClient.connect(clientCallback, url, this.worker, this.bufferPool, this.optionMap);
}
use of io.undertow.client.ClientRequest in project undertow by undertow-io.
the class HttpClientTestCase method testSimpleBasic.
@Test
public void testSimpleBasic() throws Exception {
//
DefaultServer.setRootHandler(SIMPLE_MESSAGE_HANDLER);
final UndertowClient client = createClient();
final List<ClientResponse> responses = new CopyOnWriteArrayList<>();
final CountDownLatch latch = new CountDownLatch(10);
final ClientConnection connection = client.connect(ADDRESS, worker, DefaultServer.getBufferPool(), OptionMap.EMPTY).get();
try {
connection.getIoThread().execute(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 10; i++) {
final ClientRequest request = new ClientRequest().setMethod(Methods.GET).setPath("/");
request.getRequestHeaders().put(Headers.HOST, DefaultServer.getHostAddress());
connection.sendRequest(request, createClientCallback(responses, latch));
}
}
});
latch.await(10, TimeUnit.SECONDS);
Assert.assertEquals(10, responses.size());
for (final ClientResponse response : responses) {
Assert.assertEquals(message, response.getAttachment(RESPONSE_BODY));
}
} finally {
IoUtils.safeClose(connection);
}
}
use of io.undertow.client.ClientRequest in project undertow by undertow-io.
the class LoadBalancingProxyHTTP2TestCase method testHttp2ClientMultipleStreamsThreadSafety.
@Test
public void testHttp2ClientMultipleStreamsThreadSafety() throws IOException, URISyntaxException, ExecutionException, InterruptedException, TimeoutException {
//not actually a proxy test
//but convent to put it here
UndertowXnioSsl ssl = new UndertowXnioSsl(DefaultServer.getWorker().getXnio(), OptionMap.EMPTY, DefaultServer.SSL_BUFFER_POOL, DefaultServer.createClientSslContext());
final UndertowClient client = UndertowClient.getInstance();
final ClientConnection connection = client.connect(new URI("https", null, DefaultServer.getHostAddress(), DefaultServer.getHostPort() + 1, "/", null, null), DefaultServer.getWorker(), ssl, DefaultServer.getBufferPool(), OptionMap.create(UndertowOptions.ENABLE_HTTP2, true)).get();
final ExecutorService service = Executors.newFixedThreadPool(10);
try {
Deque<FutureResult<String>> futures = new ArrayDeque<>();
for (int i = 0; i < 100; ++i) {
final FutureResult<String> future = new FutureResult<>();
futures.add(future);
service.submit(new Callable<String>() {
@Override
public String call() throws Exception {
ClientRequest cr = new ClientRequest().setMethod(Methods.GET).setPath("/path").setProtocol(Protocols.HTTP_1_1);
connection.sendRequest(cr, new ClientCallback<ClientExchange>() {
@Override
public void completed(ClientExchange result) {
result.setResponseListener(new ClientCallback<ClientExchange>() {
@Override
public void completed(ClientExchange result) {
new StringReadChannelListener(DefaultServer.getBufferPool()) {
@Override
protected void stringDone(String string) {
future.setResult(string);
}
@Override
protected void error(IOException e) {
future.setException(e);
}
}.setup(result.getResponseChannel());
}
@Override
public void failed(IOException e) {
future.setException(e);
}
});
}
@Override
public void failed(IOException e) {
future.setException(e);
}
});
return null;
}
});
}
while (!futures.isEmpty()) {
FutureResult<String> future = futures.poll();
Assert.assertNotEquals(IoFuture.Status.WAITING, future.getIoFuture().awaitInterruptibly(10, TimeUnit.SECONDS));
Assert.assertEquals("/path", future.getIoFuture().get());
}
} finally {
service.shutdownNow();
}
}
Aggregations