public SpdyClientConnection(SpdyChannel spdyChannel, ClientStatistics clientStatistics) { this.spdyChannel = spdyChannel; this.clientStatistics = clientStatistics; spdyChannel.getReceiveSetter().set(new SpdyReceiveListener()); spdyChannel.resumeReceives(); spdyChannel.addCloseTask( new ChannelListener<SpdyChannel>() { @Override public void handleEvent(SpdyChannel channel) { ChannelListeners.invokeChannelListener(SpdyClientConnection.this, closeSetter.get()); } }); }
@Override public void handleEvent(SpdyChannel channel) { try { SpdyStreamSourceChannel result = channel.receive(); if (result instanceof SpdySynReplyStreamSourceChannel) { final int streamId = ((SpdySynReplyStreamSourceChannel) result).getStreamId(); SpdyClientExchange request = currentExchanges.get(streamId); result.addCloseTask( new ChannelListener<SpdyStreamSourceChannel>() { @Override public void handleEvent(SpdyStreamSourceChannel channel) { currentExchanges.remove(streamId); } }); if (request == null) { // server side initiated stream, we can't deal with that at the moment // just fail // TODO: either handle this properly or at the very least send RST_STREAM channel.sendGoAway(SpdyChannel.CLOSE_PROTOCOL_ERROR); IoUtils.safeClose(SpdyClientConnection.this); return; } request.responseReady((SpdySynReplyStreamSourceChannel) result); } else if (result instanceof SpdyPingStreamSourceChannel) { handlePing((SpdyPingStreamSourceChannel) result); } else if (result instanceof SpdyRstStreamStreamSourceChannel) { int stream = ((SpdyRstStreamStreamSourceChannel) result).getStreamId(); UndertowLogger.REQUEST_LOGGER.debugf("Client received RST_STREAM for stream %s", stream); SpdyClientExchange exchange = currentExchanges.get(stream); if (exchange != null) { exchange.failed(UndertowMessages.MESSAGES.spdyStreamWasReset()); } } else if (!channel.isOpen()) { throw UndertowMessages.MESSAGES.channelIsClosed(); } } catch (IOException e) { UndertowLogger.REQUEST_IO_LOGGER.ioException(e); IoUtils.safeClose(SpdyClientConnection.this); for (Map.Entry<Integer, SpdyClientExchange> entry : currentExchanges.entrySet()) { try { entry.getValue().failed(e); } catch (Exception ex) { UndertowLogger.REQUEST_IO_LOGGER.ioException(new IOException(ex)); } } } }
@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); } } }
@Override public boolean isOpen() { return spdyChannel.isOpen(); }
@Override public void close() throws IOException { spdyChannel.sendGoAway(SpdyChannel.CLOSE_OK); }
@Override public XnioIoThread getIoThread() { return spdyChannel.getIoThread(); }
@Override public XnioWorker getWorker() { return spdyChannel.getWorker(); }
@Override public <A extends SocketAddress> A getLocalAddress(Class<A> type) { return spdyChannel.getLocalAddress(type); }
@Override public SocketAddress getLocalAddress() { return spdyChannel.getLocalAddress(); }
@Override public Pool<ByteBuffer> getBufferPool() { return spdyChannel.getBufferPool(); }