Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@
import org.apache.hc.core5.http.Header;
import org.apache.hc.core5.http.HttpConnection;
import org.apache.hc.core5.http.HttpException;
import org.apache.hc.core5.http.HttpHeaders;
import org.apache.hc.core5.http.HttpStreamResetException;
import org.apache.hc.core5.http.HttpVersion;
import org.apache.hc.core5.http.ProtocolException;
Expand All @@ -71,7 +72,6 @@
import org.apache.hc.core5.http.nio.command.StaleCheckCommand;
import org.apache.hc.core5.http.protocol.HttpContext;
import org.apache.hc.core5.http.protocol.HttpProcessor;
import org.apache.hc.core5.http.HttpHeaders;
import org.apache.hc.core5.http2.H2ConnectionException;
import org.apache.hc.core5.http2.H2Error;
import org.apache.hc.core5.http2.H2StreamResetException;
Expand All @@ -91,7 +91,6 @@
import org.apache.hc.core5.http2.nio.command.PushResponseCommand;
import org.apache.hc.core5.http2.priority.PriorityParamsParser;
import org.apache.hc.core5.http2.priority.PriorityValue;
import org.apache.hc.core5.http2.priority.PriorityFormatter;
import org.apache.hc.core5.io.CloseMode;
import org.apache.hc.core5.reactor.Command;
import org.apache.hc.core5.reactor.ProtocolIOSession;
Expand Down Expand Up @@ -1369,25 +1368,6 @@ H2Stream createStream(final H2StreamChannel channel, final H2StreamHandler strea
return streams.createActive(channel, streamHandler);
}

public final void sendPriorityUpdate(final int prioritizedStreamId, final PriorityValue value) throws IOException {
if (value == null) {
return;
}
final String field = PriorityFormatter.format(value);
if (field == null) {
return;
}
final byte[] ascii = field.getBytes(StandardCharsets.US_ASCII);
final ByteArrayBuffer buf = new ByteArrayBuffer(4 + ascii.length);
buf.append((byte) (prioritizedStreamId >> 24));
buf.append((byte) (prioritizedStreamId >> 16));
buf.append((byte) (prioritizedStreamId >> 8));
buf.append((byte) prioritizedStreamId);
buf.append(ascii, 0, ascii.length);
final RawFrame frame = frameFactory.createPriorityUpdate(ByteBuffer.wrap(buf.array(), 0, buf.length()));
commitFrame(frame);
}

private void recordPriorityFromHeaders(final int streamId, final List<? extends Header> headers) {
if (headers == null || headers.isEmpty()) {
return;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,12 @@
package org.apache.hc.core5.http2.priority;


import java.util.ArrayList;
import java.util.List;

import org.apache.hc.core5.annotation.Internal;
import org.apache.hc.core5.http.Header;
import org.apache.hc.core5.http.HttpHeaders;
import org.apache.hc.core5.http.message.BasicHeader;
import org.apache.hc.core5.http.message.BufferedHeader;
import org.apache.hc.core5.util.CharArrayBuffer;

/**
* Formats PriorityValue as RFC 9218 Structured Fields Dictionary.
Expand All @@ -43,21 +45,44 @@ public final class PriorityFormatter {
private PriorityFormatter() {
}

public static String format(final PriorityValue value) {
public static void format(final CharArrayBuffer dst, final PriorityValue value) {
if (value == null) {
return null;
return;
}
final List<String> parts = new ArrayList<>(2);
boolean urgencyPresent = false;
if (value.getUrgency() != PriorityValue.DEFAULT_URGENCY) {
parts.add("u=" + value.getUrgency());
dst.append("u=");
dst.append(value.getUrgency());
urgencyPresent = true;
}
if (value.isIncremental()) {
// In SF Dictionary, boolean true can be represented by key without value (per RFC 8941).
parts.add("i");
if (urgencyPresent) {
dst.append(", ");
}
dst.append("i");
}

}

public static String format(final PriorityValue value) {
if (value == null) {
return null;
}
if (parts.isEmpty()) {
return null; // omit header when all defaults
final CharArrayBuffer buf = new CharArrayBuffer(16);
format(buf, value);
return buf.toString();
}

public static Header formatHeader(final PriorityValue value) {
if (value == null) {
return new BasicHeader(HttpHeaders.PRIORITY, null);
}
return String.join(", ", parts);
final CharArrayBuffer buf = new CharArrayBuffer(16);
buf.append(HttpHeaders.PRIORITY);
buf.append(": ");
format(buf, value);
return BufferedHeader.create(buf);
}

}

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -26,34 +26,31 @@
*/
package org.apache.hc.core5.http2.examples;

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Future;

import org.apache.hc.core5.annotation.Experimental;
import org.apache.hc.core5.http.ClassicHttpRequest;
import org.apache.hc.core5.http.ClassicHttpResponse;
import org.apache.hc.core5.http.ContentType;
import org.apache.hc.core5.concurrent.FutureCallback;
import org.apache.hc.core5.http.Header;
import org.apache.hc.core5.http.HttpConnection;
import org.apache.hc.core5.http.HttpEntity;
import org.apache.hc.core5.http.HttpHost;
import org.apache.hc.core5.http.HttpResponse;
import org.apache.hc.core5.http.Message;
import org.apache.hc.core5.http.impl.bootstrap.HttpAsyncRequester;
import org.apache.hc.core5.http.io.support.ClassicRequestBuilder;
import org.apache.hc.core5.http.message.BasicHttpRequest;
import org.apache.hc.core5.http.nio.AsyncClientEndpoint;
import org.apache.hc.core5.http.nio.support.classic.ClassicToAsyncRequestProducer;
import org.apache.hc.core5.http.nio.support.classic.ClassicToAsyncResponseConsumer;
import org.apache.hc.core5.http.protocol.HttpCoreContext;
import org.apache.hc.core5.http.nio.entity.StringAsyncEntityConsumer;
import org.apache.hc.core5.http.nio.support.BasicRequestProducer;
import org.apache.hc.core5.http.nio.support.BasicResponseConsumer;
import org.apache.hc.core5.http.support.BasicRequestBuilder;
import org.apache.hc.core5.http2.HttpVersionPolicy;
import org.apache.hc.core5.http2.config.H2Config;
import org.apache.hc.core5.http2.frame.RawFrame;
import org.apache.hc.core5.http2.impl.H2Processors;
import org.apache.hc.core5.http2.impl.nio.H2StreamListener;
import org.apache.hc.core5.http2.priority.PriorityFormatter;
import org.apache.hc.core5.http2.priority.PriorityValue;
import org.apache.hc.core5.http2.protocol.H2RequestPriority;
import org.apache.hc.core5.io.CloseMode;
import org.apache.hc.core5.util.Timeout;

Expand All @@ -63,7 +60,7 @@
* Requires H2Processors to include H2RequestPriority (client chain) and an HTTP/2 connection.
*/
@Experimental
public class ClassicH2PriorityExample {
public class H2RequestExecutionWithPriorityExample {

public static void main(final String[] args) throws Exception {

Expand Down Expand Up @@ -120,50 +117,62 @@ public void onOutputFlowControl(final HttpConnection c, final int id, final int
final Future<AsyncClientEndpoint> future = requester.connect(target, Timeout.ofSeconds(30));
final AsyncClientEndpoint clientEndpoint = future.get();

final CountDownLatch latch = new CountDownLatch(2);

// ---- Request 1: Explicit non-default priority -> header MUST be emitted
executeWithPriority(clientEndpoint, target, "/httpbin/headers", PriorityValue.of(0, true));
executeWithPriority(clientEndpoint, target, "/httpbin/headers", PriorityValue.of(0, true), latch);

// ---- Request 2: RFC defaults -> header MUST be omitted by the interceptor
executeWithPriority(clientEndpoint, target, "/httpbin/user-agent", PriorityValue.defaults());
executeWithPriority(clientEndpoint, target, "/httpbin/user-agent", PriorityValue.defaults(), latch);

System.out.println("Shutting down I/O reactor");
requester.initiateShutdown();
}

private static void executeWithPriority(
final AsyncClientEndpoint endpoint,
final AsyncClientEndpoint clientEndpoint,
final HttpHost target,
final String path,
final PriorityValue priorityValue) throws Exception {
final String requestUri,
final PriorityValue priorityValue,
final CountDownLatch latch) throws Exception {

final ClassicHttpRequest request = ClassicRequestBuilder.get()
final BasicHttpRequest request = BasicRequestBuilder.get()
.setHttpHost(target)
.setPath(path)
.setPath(requestUri)
.build();
if (!PriorityValue.defaults().equals(priorityValue)) {
request.addHeader(PriorityFormatter.formatHeader(priorityValue));
}

clientEndpoint.execute(
new BasicRequestProducer(request, null),
new BasicResponseConsumer<>(new StringAsyncEntityConsumer()),
new FutureCallback<Message<HttpResponse, String>>() {

// Place the PriorityValue into the context so H2RequestPriority can format the header
final HttpCoreContext ctx = HttpCoreContext.create();
ctx.setAttribute(H2RequestPriority.ATTR_HTTP2_PRIORITY_VALUE, priorityValue);

final ClassicToAsyncRequestProducer requestProducer = new ClassicToAsyncRequestProducer(request, Timeout.ofMinutes(1));
final ClassicToAsyncResponseConsumer responseConsumer = new ClassicToAsyncResponseConsumer(Timeout.ofMinutes(1));

endpoint.execute(requestProducer, responseConsumer, ctx, null);

requestProducer.blockWaiting().execute();
try (ClassicHttpResponse response = responseConsumer.blockWaiting()) {
System.out.println(path + " -> " + response.getCode());
final HttpEntity entity = response.getEntity();
if (entity != null) {
final ContentType ct = ContentType.parse(entity.getContentType());
final Charset cs = ContentType.getCharset(ct, StandardCharsets.UTF_8);
try (BufferedReader reader = new BufferedReader(new InputStreamReader(entity.getContent(), cs))) {
String line;
while ((line = reader.readLine()) != null) {
System.out.println(line);
@Override
public void completed(final Message<HttpResponse, String> message) {
clientEndpoint.releaseAndReuse();
final HttpResponse response = message.getHead();
final String body = message.getBody();
System.out.println(requestUri + "->" + response.getCode());
System.out.println(body);
latch.countDown();
}
}
}
}

@Override
public void failed(final Exception ex) {
clientEndpoint.releaseAndDiscard();
System.out.println(requestUri + "->" + ex);
latch.countDown();
}

@Override
public void cancelled() {
clientEndpoint.releaseAndDiscard();
System.out.println(requestUri + " cancelled");
latch.countDown();
}

});
}
}
Loading