java.lang.OutOfMemoryError: Direct buffer memory
by Steve Hu
Hi,
I am working on the benchmark tests for TechEmpower and the following
handler got OutOfMemoryError. The tricky thing is 1.2.5.Final works fine
but 1.4.4.Final/1.4.10.Final fails with the error.
I am wondering what has been changed between 1.2 and 1.4 might cause the
problem. The application is running within a vagrant environment and memory
might be constraint.
Thanks,
Steve
public class PlaintextGetHandler implements HttpHandler {
private static final String MESSAGE = "Hello, World!";
@Override
public void handleRequest(HttpServerExchange exchange) throws Exception {
exchange.getResponseHeaders().put(Headers.CONTENT_TYPE, "text/plain");
exchange.getResponseSender().send(MESSAGE);
}
}
and the stacktrace
Server light-java: java.lang.OutOfMemoryError: Direct buffer memory
Server light-java: at java.nio.Bits.reserveMemory(Bits.java:693)
Server light-java: at
java.nio.DirectByteBuffer.<init>(DirectByteBuffer.java:123)
Server light-java: at java.nio.ByteBuffer.allocateDirect(ByteBuffer.java:311)
Server light-java: at
io.undertow.server.DefaultByteBufferPool.allocate(DefaultByteBufferPool.java:146)
Server light-java: at
io.undertow.server.protocol.http.PipeliningBufferingStreamSinkConduit.write(PipeliningBufferingStreamSinkConduit.java:98)
Server light-java: at
io.undertow.server.protocol.http.HttpResponseConduit.processWrite(HttpResponseConduit.java:250)
Server light-java: at
io.undertow.server.protocol.http.HttpResponseConduit.write(HttpResponseConduit.java:596)
Server light-java: at
io.undertow.conduits.AbstractFixedLengthStreamSinkConduit.write(AbstractFixedLengthStreamSinkConduit.java:106)
Server light-java: at
io.undertow.conduits.AbstractFixedLengthStreamSinkConduit.write(AbstractFixedLengthStreamSinkConduit.java:120)
Server light-java: at
org.xnio.conduits.ConduitStreamSinkChannel.write(ConduitStreamSinkChannel.java:154)
Server light-java: at
io.undertow.channels.DetachableStreamSinkChannel.write(DetachableStreamSinkChannel.java:187)
Server light-java: at
io.undertow.server.HttpServerExchange$WriteDispatchChannel.write(HttpServerExchange.java:2021)
Server light-java: at
io.undertow.io.AsyncSenderImpl.send(AsyncSenderImpl.java:219)
Server light-java: at
io.undertow.io.AsyncSenderImpl.send(AsyncSenderImpl.java:310)
Server light-java: at
io.undertow.io.AsyncSenderImpl.send(AsyncSenderImpl.java:282)
Server light-java: at
io.undertow.io.AsyncSenderImpl.send(AsyncSenderImpl.java:316)
Server light-java: at
com.networknt.techempower.handler.PlaintextGetHandler.handleRequest(PlaintextGetHandler.java:20)
Server light-java: at
io.undertow.server.RoutingHandler.handleRequest(RoutingHandler.java:93)
Server light-java: at
io.undertow.server.handlers.SetHeaderHandler.handleRequest(SetHeaderHandler.java:90)
Server light-java: at
io.undertow.server.Connectors.executeRootHandler(Connectors.java:211)
Server light-java: at
io.undertow.server.protocol.http.HttpReadListener.handleEventWithNoRunningRequest(HttpReadListener.java:243)
Server light-java: at
io.undertow.server.protocol.http.HttpReadListener.handleEvent(HttpReadListener.java:134)
Server light-java: at
io.undertow.server.protocol.http.HttpReadListener.handleEvent(HttpReadListener.java:58)
Server light-java: at
org.xnio.ChannelListeners.invokeChannelListener(ChannelListeners.java:92)
Server light-java: at
org.xnio.conduits.ReadReadyHandler$ChannelListenerHandler.readReady(ReadReadyHandler.java:66)
Server light-java: at
org.xnio.nio.NioSocketConduit.handleReady(NioSocketConduit.java:88)
Server light-java: at org.xnio.nio.WorkerThread.run(WorkerThread.java:559)
7 years, 11 months
WebSocketChannel getPeerConnections() unnecesary cost?
by Mario Carbajal
Undertow websockets mantain a peer connections set
per WebSocketProtocolHandshakeHandler instance. From the looks of it, it's
there only for undertow users convinience.
The application I'm building doesn't make use of the peer connections set
at all and it feels kinda bad to have to pay for the feature despite not
using it.
It would be great if there was a way to disable this functionality. Perhaps
with an overload of the WebSocketProtocolHandshakeHandler constructor that
initializes that set to null. (and adding null checks whenever the set is
used.)
Or maybe some global undertow setting?
This kind of functionality could be provided optionally as a convinience
abstract class that implements WebSocketConnectionCallback and maintains
the set, exposing it to the users by an overrideable overload of the
onConnect method.
Users that want the functionality could inherit from it.
7 years, 11 months
How to access the http headers in Endpoint.onOpen ?
by Angelo Salvade
Hi
If I understand the Java API for WebSocket Spec (JSR 356) correctly,
the only way to access the http headers and the http session is by
the parameter 'HandshakeRequest request' of
ServerEndpointConfig.Configurator.modifyHandshake
with the methods HandshakeRequest.getHeaders and
HandshakeRequest.getHttpSession.
>From there, the only way to pass this data to Endpoint.onOpen seems to
be by EndpointConfig.getUserProperties
because 'EndpointConfig config' is a parameter of Endpoint.onOpen.
The problem is, that according to the documentation, EndpointConfig is
shared with all sessions and therefore this won't work.
See also https://java.net/jira/browse/WEBSOCKET_SPEC-218 and
https://java.net/jira/browse/WEBSOCKET_SPEC-235.
However, I did some testing with Undertow and Jetty.
Please have a look at the following test code:
static ServerEndpointConfig.Configurator SERVER_CONFIGURATOR = new
ServerEndpointConfig.Configurator() {
@Override public <T> T getEndpointInstance(Class<T> endpointClass) {
return endpointClass.cast(new Endpoint() {
@Override public void onOpen(Session session,
EndpointConfig config) {
System.out.printf(
"onOpen - config: %s, session: %s%n",
config.getUserProperties().keySet().stream().filter(k ->
k.startsWith("Header")).collect(Collectors.toSet()),
session.getUserProperties().keySet().stream().filter(k ->
k.startsWith("Header")).collect(Collectors.toSet())
);
}
@Override public void onClose(Session session, CloseReason
closeReason) {
}
@Override public void onError(Session session, Throwable
throwable) {
}
});
}
@Override public void modifyHandshake(ServerEndpointConfig sec,
HandshakeRequest request, HandshakeResponse response) {
sec.getUserProperties().putAll(request.getHeaders());
}
@Override public String getNegotiatedSubprotocol(List<String>
supported, List<String> requested) {
return "";
}
@Override public List<Extension>
getNegotiatedExtensions(List<Extension> installed, List<Extension>
requested) {
return new ArrayList<>();
}
@Override public boolean checkOrigin(String originHeaderValue) {
return true;
}
};
static void clientConnect(WebSocketContainer container) throws Exception {
for (String header : Arrays.asList("Header1", "Header2")) {
container.connectToServer(
new Endpoint() {
@Override public void onOpen(Session session,
EndpointConfig config) {
}
},
ClientEndpointConfig.Builder.create().configurator(new
ClientEndpointConfig.Configurator() {
@Override public void beforeRequest(Map<String,
List<String>> headers) {
headers.put(header, Collections.singletonList("foo"));
}
}).build(),
URI.create("ws://localhost:" + PORT + PATH)
);
TimeUnit.SECONDS.sleep(1L);
}
}
You'll find the source code in the attachments and under:
https://github.com/softappeal/yass/blob/master/java/test/ch/softappeal/ya...
https://github.com/softappeal/yass/blob/master/java/test/ch/softappeal/ya...
https://github.com/softappeal/yass/blob/master/java/test/ch/softappeal/ya...
These are the outputs of the tests:
UndertowUserPropertiesTest (io.undertow:undertow-websockets-jsr:1.4.8.Final)
onOpen - config: [Header1], session: [Header1]
onOpen - config: [Header1, Header2], session: [Header1, Header2]
JettyUserPropertiesTest
(org.eclipse.jetty.websocket:javax-websocket-server-impl:9.4.1.v20170120)
onOpen - config: [Header1], session: [Header1]
onOpen - config: [Header2], session: [Header2]
So the questions are:
Jetty and Undertow seem to copy EndpointConfig.getUserProperties to
Session.getUserProperties.
Is this behaviour guaranteed? Where does it say so in the spec?
Jetty seems to make a new EndpointConfig.getUserProperties for each session.
This seems NOT to be according to the spec.
This behaviour would be THE SOLUTION to my problem.
Undertow seems to share EndpointConfig.getUserProperties over all sessions.
This seems to be according to the spec.
But so it's not possible to pass the headers to Endpoint.onOpen.
So can we say the WebSocket API should behave like Jetty?
Regards,
Angelo
7 years, 11 months
exchange.getResponseSender().send issues
by Hicks, Matt
I'm having a very strange problem sending a generated HTML file back to the
browser using `exchange.getResponseSender().send(s)`. It seems to work
fine if the content is a short bit of text like "Hello World", but the
content is 144,772 bytes and if I use Curl to test it I get a 200 OK, but
it says the Content-Length is 0 and no content comes back. If I set the
content to a short String like I said, then the Content-Length is properly
set and the content comes back like expected.
Should I not be using `exchange.getResponseSender().send` for large amounts
of text?
I just updated to 1.4.10.Final hoping that it was a bug that was fixed
since 1.4.8.Final, but it's still happening.
Any insights would be greatly appreciated.
7 years, 12 months
ChangeSessionIdOnLogin unexpected behavior during SPA initialization
by Matthieu BROUILLARD
Hi all,
first of all sorry if some of you already answered on my IRC request on
freenode but due to network policy in my company I could only connect via
web and have been disconnected without access to channel history.
I apologize if you already took time to answer on the channel but I could
not see your answer.
The problem I have is around the 'ChangeSessionIdOnLogin' feature
implemented in undertow. I understand that the feature is there in order to
avoid attacks via session reuse (exposing a link with an already provided
JSESSIONID for example).
In one of our application, a SPA application with some server side
HttpSession caching, during initialization a bunch of asynchronous calls
are performed. Due to the 'ChangeSessionIdOnLogin' feature several of them
are failing.
I have implemented a simple reproducer application exposing the problem and
possible workarounds (examples are self runnable using maven :
https://github.com/McFoggy/jsessionid-concurrency).
In the provided reproducer app we workaround the JSESSIONID regeneration by
either:
- making a fake call before the asynchronous calls are performed
(changing the JSESSIONID before parallel calls occure)
- disabling the 'ChangeSessionIdOnLogin' feature using an undertow
ServletExtension
The real question I have is why the created session is not trusted (see
https://github.com/undertow-io/undertow/blob/master/servlet/src/main/java...)
when the HttpSession is created by an authenticated call without incoming
JSESSIONID?
Is this a bug? I'll fill an issue if so.
Is this a feature? In this case what is the recommendation to avoid this
when several calls are issued before JSESSIONID stabilization like in the
demo project?
Thanks for any hint & answer.
Matthieu
- - - - - - - - - - - - - - - -
http://blog.matthieu.brouillard.fr/
http://oss.brouillard.fr/
https://github.com/McFoggy
https://twitter.com/alightinthefog
7 years, 12 months
FormDataProcessor ending exchange prematurely
by Oliver Dain
I have some code for handling file uploads. It uses FormDataProcessor and
tries to do everything asynchronously. I call "FormDataParser.parse"
passing in another handler. I'll call that handler the OnFormDataAvailable.
The OnFormDataAvailable handler checks to see if it's on the IO thread. If
it is, it calls dispatch. Either way, once we're sure we're dispatched that
handler calls yet another handler.
What I've seen is that my OnFormDataAvailable handler (the one called by
parse()) is not on the IO thread so it doesn't need to call dispatch. And
yet, the exchange gets ended before the handler it calls is even close to
complete.
I've found a fix, but I don't understand why it's necessary. Specifically,
if OnFormDataAvailable is not on the IO thread when its invoked it calls
the 0-argument version of "HttpServerExchange.dispatch()" (which you've
told me in another conversation shouldn't ever be necessary). If I do that,
everything is fine.
For completeness, here's the complete code:
public class FormDataParsingHandler {
private static final Logger log =
LoggerFactory.getLogger(FormDataParsingHandler.class);
private static final FormParserFactory formParserFactory =
FormParserFactory.builder().build();
public static final AttachmentKey<FormData> FORM_DATA_ATTACHMENT_KEY
= AttachmentKey.create(FormData.class);
/**
* The only public method - this is what gets exposed as the HttpHandler.
*/
public CompletableFuture<FormData> parseForm(HttpServerExchange exchange) {
log.info("audio file upload request received.");
FormDataParser parser = formParserFactory.createParser(exchange);
if (parser == null) {
log.warn("No parser found that can handle this content type.
Headers were: {}", exchange.getRequestHeaders());
throw new UserVisibleException("No parser for the given content
type.", ResponseCodes.BAD_REQUEST);
}
CompletableFuture<FormData> toComplete = new CompletableFuture<>();
try {
parser.parse(new OnFormDataAvailable(toComplete));
} catch (Exception e) {
log.error("Error parsing form data:", e);
throw wrapAsUnchecked(e);
}
exchange.addExchangeCompleteListener((ex, nextListener) -> {
// Must close the parser so it can free any temporary files that
were created.
try {
parser.close();
} catch (IOException e) {
log.error("Error closing the FormDataParser. Request was
handled successfully but temporary files may not "
+ "have been cleaned up.", e);
}
nextListener.proceed();
});
return toComplete;
}
// The FormDataParser calls an HttpHandler when it's complete so we
add a silly handler here that does nothing but
// complete this method's future when the form data is available.
private static class OnFormDataAvailable implements HttpHandler {
private final CompletableFuture<FormData> toComplete;
private OnFormDataAvailable(CompletableFuture<FormData> toComplete) {
this.toComplete = toComplete;
}
@Override
public void handleRequest(HttpServerExchange exchange) throws Exception {
// Before we complete the future we have to re-dispatch or we'll
fall off the end of this method and Undertow
// will complete the exchange on our behalf.
FormData data = exchange.getAttachment(FormDataParser.FORM_DATA);
if (exchange.isInIoThread()) {
log.debug("Was on the IO thread. Re-dispatching.");
exchange.dispatch(SameThreadExecutor.INSTANCE, () ->
afterDistpach(data));
} else {
// THIS THE MYSTERY LINE. WHY IS THIS NEEDED?
exchange.dispatch();
afterDistpach(data);
}
}
private void afterDistpach(FormData data) {
if (data == null) {
toComplete.completeExceptionally(
new UserVisibleException("Parsing of data failed.",
ResponseCodes.BAD_REQUEST));
} else {
toComplete.complete(data);
}
}
}
}
--
CTO, Analytic Spot
44 West Broadway #222
Eugene, OR 97401
analyticspot.com • 425-296-6556
www.linkedin.com/in/oliverdain
7 years, 12 months
Testing custom HttpHandlers
by Bill O'Neil
Hello,
How would you recommend testing custom HttpHanders? The DefaultServer class
in core part of the test code so it is not included in the jar for reuse.
Would it be reasonable to pull testing utils into their own project so it
can be reused? Or would you recommend just mocking a HttpServerExchange and
passing that to the HttpHandler?
Thanks,
Bill
8 years
WebSockets static sendBinary methods.and PooledByteBuffers
by Mario Carbajal
>From looking at the source it seems that the WebSockets.sendBinary methods
that take ByteBuffer will take ownership of the buffer, meaning I shouldn't
modify (or return it to a pool) after I pass it to these methods. Is this
correct?
Looking at sendInternal, the buffer passed is then wrapped in a dummy
ImmediatePooledByteBuffer. I could make alternative versions of these
sendBinary methods that take PooledByteBuffer. Allowing me to use pooled
buffers.
Since this functionality is missing it makes me think that there may be a
reason why it shouldn't be done.
8 years