@Override public void setBody(DataWrapper data) { if(data.getReadableSize() != 0) throw new IllegalArgumentException("Can't set body on HttpLastChunk according to http spec. It must be size=0"); super.setBody(data); }
private void copyData(DataWrapper body, byte[] data, int offset) { for(int i = 0; i < body.getReadableSize(); i++) { //TODO: Think about using System.arrayCopy here(what is faster?) data[offset + i] = body.readByteAt(i); } }
@Override public UnparsedState getUnParsedState() { if(inChunkParsingMode) { return new UnparsedState(ParsingState.CHUNK, leftOverData.getReadableSize()); } else if(halfParsedMessage != null) { return new UnparsedState(ParsingState.BODY, leftOverData.getReadableSize()); } return new UnparsedState(ParsingState.HEADERS, leftOverData.getReadableSize()); }
private HttpMessage parseHttpMessage(DataWrapper toBeParsed, List<Integer> markedPositions) { List<String> lines = new ArrayList<>(); //Add the last line.. markedPositions.add(toBeParsed.getReadableSize()); int offset = 0; for(Integer mark : markedPositions) { int len = mark - offset; String line = toBeParsed.createStringFrom(offset, len, iso8859_1); lines.add(line.trim()); offset = mark; } markedPositions.clear(); //buffer processed...release to be re-used now.. toBeParsed.releaseUnderlyingBuffers(pool); String firstLine = lines.get(0).trim(); if(firstLine.startsWith("HTTP/")) { return parseResponse(lines); } else { return parseRequest(lines); } }
private byte[] chunkedBytes(HttpChunk request) { DataWrapper dataWrapper = request.getBody(); int size = dataWrapper.getReadableSize(); String metaLine = request.createMetaLine(); String lastPart = request.createTrailer(); byte[] hex = metaLine.getBytes(iso8859_1); byte[] endData = lastPart.getBytes(iso8859_1); byte[] data = new byte[hex.length+size+endData.length]; //copy chunk header of <size>/r/n System.arraycopy(hex, 0, data, 0, hex.length); copyData(dataWrapper, data, hex.length); //copy closing /r/n (and headers if isLastChunk) System.arraycopy(endData, 0, data, data.length-endData.length, endData.length); return data; }
public static HttpFullRequest createJsonRequest(KnownHttpMethod method, String url) { HttpRequest request = createBaseRequest(method, url); String json = "{ `query`: `cats and dogs`, `meta`: { `numResults`: 4 } }".replace("`", "\""); DataWrapper body = gen.wrapByteArray(json.getBytes()); request.addHeader(new Header(KnownHeaderName.CONTENT_LENGTH, body.getReadableSize()+"")); return new HttpFullRequest(request, body); }
public String getBodyAsString() { Charset charset = extractCharset(); //get charset from headers? DataWrapper body = getBody(); if(body == null) return null; return body.createStringFrom(0, body.getReadableSize(), charset); }
public static HttpFullRequest createBadJsonRequest(KnownHttpMethod method, String url) { HttpRequest request = createBaseRequest(method, url); String json = "{ `query `cats and dogs`, `meta`: { `numResults`: 4 } }".replace("`", "\""); DataWrapper body = gen.wrapByteArray(json.getBytes()); request.addHeader(new Header(KnownHeaderName.CONTENT_LENGTH, body.getReadableSize()+"")); return new HttpFullRequest(request, body); }
private CompletableFuture<Void> writeDataFrame(DataFrame frame) { int streamId = frame.getStreamId(); log.info("actually writing data frame: " + frame); int dataSize = frame.getData().getReadableSize(); int paddingSize = frame.getPadding().getReadableSize(); if(paddingSize > 0) paddingSize += 1; int totalSize = dataSize + paddingSize; decrementOutgoingWindow(streamId, totalSize); return channel.write(ByteBuffer.wrap(marshal(frame).createByteArray())).thenAccept(c -> {}); }
private void readInBody(MementoImpl memento, boolean stripAndCompareLastTwo) { HttpPayload message = memento.getHalfParsedMessage(); DataWrapper dataToRead = memento.getLeftOverData(); int readableSize = dataToRead.getReadableSize(); int numBytesNeeded = memento.getNumBytesLeftToRead(); if(numBytesNeeded <= readableSize) { List<? extends DataWrapper> split = dataGen.split(dataToRead, numBytesNeeded); DataWrapper data = split.get(0); if(stripAndCompareLastTwo) { List<? extends DataWrapper> splitPieces = dataGen.split(data, data.getReadableSize()-2); data = splitPieces.get(0); DataWrapper trailer = splitPieces.get(1); String trailerStr = trailer.createStringFrom(0, trailer.getReadableSize(), iso8859_1); if(!TRAILER_STR.equals(trailerStr)) throw new IllegalStateException("The chunk did not end with \\r\\n . The format is invalid"); } message.setBody(data); memento.setLeftOverData(split.get(1)); memento.setNumBytesLeftToRead(0); memento.getParsedMessages().add(message); //clear any cached message we were waiting for more data for memento.setHalfParsedMessage(null); return; } }
public String createMetaLine() { String metaLine = Integer.toHexString(getBody().getReadableSize()); for(HttpChunkExtension extension : getExtensions()) { metaLine += ";"+extension.getName(); if(extension.getValue() != null) metaLine += "="+extension.getValue(); } return metaLine+TRAILER_STR; }
private CompletableFuture<ResponseId> actuallySendResponse(HttpResponse response, Stream stream, boolean isComplete) { return sendHeaderFrames(responseToHeaders(response), stream) .thenAccept(v -> { // Don't send an empty dataframe that is not completing. if (response.getBodyNonNull().getReadableSize() != 0 || isComplete) sendDataFrames(response.getBodyNonNull(), isComplete, stream, false); }) .thenApply(v -> stream.getResponseId()).exceptionally(e -> { log.error("can't send header frames", e); return stream.getResponseId(); }); }
@Override public CompletableFuture<HttpFullResponse> send(HttpFullRequest request) { Integer contentLength = request.getRequest().getContentLength(); if(request.getData() == null || request.getData().getReadableSize() == 0) { if(contentLength != null && contentLength != 0) throw new IllegalArgumentException("HttpRequest has 0 Content-Length but readable size="+request.getData().getReadableSize()); } else if(!request.getRequest().isHasNonZeroContentLength()) throw new IllegalArgumentException("HttpRequest must have Content-Length header"); else if(request.getRequest().getContentLength() != request.getData().getReadableSize()) throw new IllegalArgumentException("HttpRequest Content-Length header value=" +request.getRequest().getContentLength()+" does not match payload size="+request.getData().getReadableSize()); CompletableFuture<HttpFullResponse> future = new CompletableFuture<HttpFullResponse>(); HttpResponseListener l = new CompletableListener(future); HttpData data = new HttpData(request.getData(), true); send(request.getRequest(), l).thenCompose(w -> { return w.send(data); }); return future; }
@Override public CompletableFuture<Void> processPiece(StreamMsg data) { closeCheck(); if(!(data instanceof DataFrame)) throw new UnsupportedOperationException("not supported in http1.1="+data); DataFrame frame = (DataFrame) data; totalWritten += frame.getData().getReadableSize(); if(totalWritten > len) throw new IllegalArgumentException("You wrote more than the content length header="+len+" written size="+totalWritten); else if(frame.isEndOfStream() && totalWritten != len) throw new IllegalArgumentException("You did not write enough data. written="+totalWritten+" content length header="+len); if(frame.isEndOfStream()) { log.info(socket+" done sending response2"); remove(data); } HttpData httpData = new HttpData(frame.getData(), frame.isEndOfStream()); return write(httpData).thenApply(c -> { if(frame.isEndOfStream()) permitQueue.releasePermit(); return null; }); } }
public static HttpResponse createResponse(KnownStatusCode status, DataWrapper body) { HttpResponse resp = new HttpResponse(); HttpResponseStatusLine statusLine = new HttpResponseStatusLine(); HttpResponseStatus statusCode = new HttpResponseStatus(); statusCode.setKnownStatus(status); statusLine.setStatus(statusCode); resp.setStatusLine(statusLine); resp.setBody(body); resp.addHeader(new Header(KnownHeaderName.CONTENT_LENGTH, Integer.toString(body.getReadableSize()))); return resp; }
@Override public CompletableFuture<ResponseId> 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); } }
private void readChunk(MementoImpl memento, int i) { HttpChunk chunk = createHttpChunk(memento, i); memento.setHalfParsedMessage(chunk); readInBody(memento, true); if(chunk.getBody() != null && chunk.getBody().getReadableSize() == 0) { //this is the last chunk as it is 0 size memento.setInChunkParsingMode(false); } }
private void handleData(DataFrame frame, Stream stream) { // Only allowable if stream is open or half closed local switch(stream.getStatus()) { case OPEN: case HALF_CLOSED_LOCAL: int dataSize = frame.getData().getReadableSize(); int paddingSize = frame.getPadding().getReadableSize(); if(paddingSize > 0) { paddingSize += 1; //add the length byte in since there is padding } int payloadLength = dataSize + paddingSize; this.http2EngineImpl.decrementIncomingWindow(frame.getStreamId(), payloadLength); stream.checkAgainstContentLength(frame.getData().getReadableSize(), frame.isEndOfStream()); this.http2EngineImpl.sideSpecificHandleData(frame, payloadLength, stream); if(frame.isEndOfStream()) this.http2EngineImpl.receivedEndStream(stream); break; case HALF_CLOSED_REMOTE: throw new GoAwayError(this.http2EngineImpl.lastClosedRemoteOriginatedStream().orElse(0), stream.getStreamId(), Http2ErrorCode.STREAM_CLOSED, Http2EngineImpl.wrapperGen.emptyWrapper()); case CLOSED: throw new GoAwayError(this.http2EngineImpl.lastClosedRemoteOriginatedStream().orElse(0), stream.getStreamId(), Http2ErrorCode.STREAM_CLOSED, Http2EngineImpl.wrapperGen.emptyWrapper()); case IDLE: throw new GoAwayError(this.http2EngineImpl.lastClosedRemoteOriginatedStream().orElse(0), stream.getStreamId(), Http2ErrorCode.PROTOCOL_ERROR, Http2EngineImpl.wrapperGen.emptyWrapper()); default: throw new RstStreamError(Http2ErrorCode.PROTOCOL_ERROR, stream.getStreamId()); } }
private static HttpRequest createPostRequestImpl(String url, String ... argTuples) throws UnsupportedEncodingException { if(argTuples.length % 2 != 0) throw new IllegalArgumentException("argTuples.length must be of even size (key/value)"); HttpUri httpUri = new HttpUri(url); HttpRequestLine requestLine = new HttpRequestLine(); requestLine.setMethod(KnownHttpMethod.POST); requestLine.setUri(httpUri); HttpRequest req = new HttpRequest(); req.setRequestLine(requestLine); req.addHeader(new Header(KnownHeaderName.HOST, "myhost.com")); String encodedParams = ""; for(int i = 0; i < argTuples.length; i+=2) { String key = URLEncoder.encode(argTuples[i], StandardCharsets.UTF_8); String value = URLEncoder.encode(argTuples[i+1], StandardCharsets.UTF_8); if(!"".equals(encodedParams)) encodedParams += "&"; encodedParams += key+"="+value; } byte[] bytes = encodedParams.getBytes(StandardCharsets.UTF_8); DataWrapper body = gen.wrapByteArray(bytes); req.setBody(body); req.addHeader(new Header(KnownHeaderName.CONTENT_LENGTH, ""+body.getReadableSize())); req.addHeader(new Header(KnownHeaderName.CONTENT_TYPE, "application/x-www-form-urlencoded")); return req; }
private static HttpFullRequest createPostRequestImpl(String url, String ... argTuples) throws UnsupportedEncodingException { if(argTuples.length % 2 != 0) throw new IllegalArgumentException("argTuples.length must be of even size (key/value)"); HttpUri httpUri = new HttpUri(url); HttpRequestLine requestLine = new HttpRequestLine(); requestLine.setMethod(KnownHttpMethod.POST); requestLine.setUri(httpUri); HttpRequest req = new HttpRequest(); req.setRequestLine(requestLine); req.addHeader(new Header(KnownHeaderName.HOST, "myhost.com")); String encodedParams = ""; for(int i = 0; i < argTuples.length; i+=2) { String key = URLEncoder.encode(argTuples[i], StandardCharsets.UTF_8); String value = URLEncoder.encode(argTuples[i+1], StandardCharsets.UTF_8); if(!"".equals(encodedParams)) encodedParams += "&"; encodedParams += key+"="+value; } byte[] bytes = encodedParams.getBytes(StandardCharsets.UTF_8); DataWrapper body = gen.wrapByteArray(bytes); req.addHeader(new Header(KnownHeaderName.CONTENT_LENGTH, ""+body.getReadableSize())); req.addHeader(new Header(KnownHeaderName.CONTENT_TYPE, "application/x-www-form-urlencoded")); return new HttpFullRequest(req, body); }