org.webpieces.frontend.impl.Http11ResponseSender Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of http-frontend Show documentation
Show all versions of http-frontend Show documentation
Create a webserver with this library in just 3 lines of code. just register your HttpRequestListener and it feeds you a FrontendSocket that you write HttpResponses to
package org.webpieces.frontend.impl;
import java.nio.ByteBuffer;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import org.webpieces.data.api.DataWrapper;
import org.webpieces.httpcommon.Responses;
import org.webpieces.httpcommon.api.Protocol;
import org.webpieces.httpcommon.api.RequestId;
import org.webpieces.httpcommon.api.ResponseId;
import org.webpieces.httpcommon.api.ResponseSender;
import org.webpieces.httpcommon.api.exceptions.HttpException;
import org.webpieces.httpparser.api.HttpParser;
import org.webpieces.httpparser.api.common.Header;
import org.webpieces.httpparser.api.common.KnownHeaderName;
import org.webpieces.httpparser.api.dto.HttpChunk;
import org.webpieces.httpparser.api.dto.HttpLastChunk;
import org.webpieces.httpparser.api.dto.HttpRequest;
import org.webpieces.httpparser.api.dto.HttpResponse;
import org.webpieces.httpparser.api.dto.HttpResponseStatus;
import org.webpieces.httpparser.api.dto.HttpResponseStatusLine;
import org.webpieces.nio.api.channels.Channel;
import com.webpieces.http2parser.api.dto.lib.Http2Header;
class Http11ResponseSender implements ResponseSender {
private Channel channel;
private HttpParser parser;
public Http11ResponseSender(Channel channel, HttpParser parser) {
this.channel = channel;
this.parser = parser;
}
@Override
public Protocol getProtocol() {
return Protocol.HTTP11;
}
@Override
public CompletableFuture close() {
return channel.close().thenAccept(c -> {});
}
private CompletableFuture actuallySendResponse(HttpResponse response) {
ByteBuffer data = parser.marshalToByteBuffer(response);
return channel.write(data).thenAccept(c -> {});
}
@Override
public CompletableFuture sendResponse(HttpResponse response, HttpRequest request, RequestId requestId, boolean isComplete) {
// HTTP/1.1 doesn't need responseids
ResponseId id = new ResponseId(0);
if(isComplete) {
return actuallySendResponse(response)
.thenApply(v -> id);
} else {
HttpResponse responseNoBody = Responses.copyResponseExceptBody(response);
DataWrapper body = response.getBodyNonNull();
// If we're not already chunked, make it chunked.
Header transferEncodingHeader = responseNoBody.getHeaderLookupStruct().getHeader(KnownHeaderName.TRANSFER_ENCODING);
if(transferEncodingHeader == null || !transferEncodingHeader.getValue().equalsIgnoreCase("chunked")) {
responseNoBody.addHeader(new Header(KnownHeaderName.TRANSFER_ENCODING, "chunked"));
}
return actuallySendResponse(responseNoBody)
.thenAccept(v -> {
if(body.getReadableSize() != 0)
sendData(body, id, false);
})
.thenApply(v -> id);
}
}
@Override
public CompletableFuture sendData(DataWrapper data, ResponseId id, boolean isComplete) {
// Create a chunk from the data, then send.
HttpChunk chunk;
if(isComplete) {
chunk = new HttpLastChunk();
} else
{
chunk = new HttpChunk();
}
chunk.setBody(data);
return channel.write(parser.marshalToByteBuffer(chunk)).thenAccept(c -> {});
}
@Override
public void sendTrailer(List headerList, ResponseId id, boolean isComplete) {
if(!isComplete) {
throw new RuntimeException("can't sendTrailer that isnot completeing the response");
}
HttpLastChunk lastChunk = new HttpLastChunk();
for(Http2Header header: headerList) {
lastChunk.addHeader(new Header(header.getName(), header.getValue()));
}
channel.write(parser.marshalToByteBuffer(lastChunk));
}
@Override
public CompletableFuture sendException(HttpException e) {
HttpResponseStatus respStatus = new HttpResponseStatus();
respStatus.setKnownStatus(e.getStatusCode());
HttpResponseStatusLine statusLine = new HttpResponseStatusLine();
statusLine.setStatus(respStatus);
HttpResponse response = new HttpResponse();
response.setStatusLine(statusLine );
response.addHeader(new Header("Failure", e.getMessage()));
ByteBuffer data = parser.marshalToByteBuffer(response);
return channel.write(data).thenAccept(c -> {});
}
@Override
public Channel getUnderlyingChannel() {
return channel;
}
@Override
public String toString() {
return "ResponseSender[" + channel + "]";
}
}