/** * @param factory the factory used to create InterfaceHttpData * @param request the request to encode * @param multipart True if the FORM is a ENCTYPE="multipart/form-data" * @param charset the charset to use as default * @param encoderMode the mode for the encoder to use. See {@link EncoderMode} for the * details. * * @throws NullPointerException for request or charset or factory * @throws ErrorDataEncoderException if the request is not a POST */ HttpClientFormEncoder(HttpDataFactory factory, HttpRequest request, boolean multipart, Charset charset, EncoderMode encoderMode) throws ErrorDataEncoderException { super(factory, request, multipart, charset, encoderMode); this.newCharset = charset; this.request = request; this.isKey = true; this.cleanOnTerminate = true; this.progressFlux = DirectProcessor.create(); this.newMode = encoderMode; this.newFactory = factory; this.newMultipart = multipart; }
public void setHttpRequest(HttpRequest request) throws BadRequestException, IOException { if (request == null) { LOG.error("HttpRequest not initialized"); throw new BadRequestException("HttpRequest not initialized"); } if (!request.getMethod().equals(HttpMethod.POST)) { LOG.error("Got invalid HTTP method: expecting only POST"); throw new BadRequestException("Incorrect method " + request.getMethod().toString() + ", expected POST"); } HttpDataFactory factory = new DefaultHttpDataFactory(DefaultHttpDataFactory.MINSIZE); HttpPostRequestDecoder decoder = new HttpPostRequestDecoder(factory, request); InterfaceHttpData data = decoder.getBodyHttpData(HTTP_TEST_ATTRIBUTE); if (data == null) { LOG.error("HTTP Resolve request inccorect, {} attribute not found", HTTP_TEST_ATTRIBUTE); throw new BadRequestException("HTTP Resolve request inccorect, " + HTTP_TEST_ATTRIBUTE + " attribute not found"); } Attribute attribute = (Attribute) data; requestData = attribute.get(); LOG.trace("Name {}, type {} found, data size {}", data.getName(), data.getHttpDataType().name(), requestData.length); }
/** * Creates a {@link HttpPostRequestEncoder} that encodes the given {@code request} and {@code blobContent}. * @param request the {@link HttpRequest} containing headers and other metadata about the request. * @param blobContent the {@link ByteBuffer} that represents the content of the blob. * @param usermetadata the {@link ByteBuffer} that represents user metadata * @return a {@link HttpPostRequestEncoder} that can encode the {@code request} and {@code blobContent}. * @throws HttpPostRequestEncoder.ErrorDataEncoderException * @throws IOException */ private HttpPostRequestEncoder createEncoder(HttpRequest request, ByteBuffer blobContent, ByteBuffer usermetadata) throws HttpPostRequestEncoder.ErrorDataEncoderException, IOException { HttpDataFactory httpDataFactory = new DefaultHttpDataFactory(false); HttpPostRequestEncoder encoder = new HttpPostRequestEncoder(httpDataFactory, request, true); FileUpload fileUpload = new MemoryFileUpload(RestUtils.MultipartPost.BLOB_PART, RestUtils.MultipartPost.BLOB_PART, "application/octet-stream", "", Charset.forName("UTF-8"), blobContent.remaining()); fileUpload.setContent(Unpooled.wrappedBuffer(blobContent)); encoder.addBodyHttpData(fileUpload); fileUpload = new MemoryFileUpload(RestUtils.MultipartPost.USER_METADATA_PART, RestUtils.MultipartPost.USER_METADATA_PART, "application/octet-stream", "", Charset.forName("UTF-8"), usermetadata.remaining()); fileUpload.setContent(Unpooled.wrappedBuffer(usermetadata)); encoder.addBodyHttpData(fileUpload); return encoder; }
/** * Creates a {@link HttpPostRequestEncoder} that encodes the given {@code request} and {@code parts}. * @param request the {@link HttpRequest} containing headers and other metadata about the request. * @param parts the {@link InMemoryFile}s that will form the parts of the request. * @return a {@link HttpPostRequestEncoder} that can encode the {@code request} and {@code parts}. * @throws HttpPostRequestEncoder.ErrorDataEncoderException * @throws IOException */ private HttpPostRequestEncoder createEncoder(HttpRequest request, InMemoryFile[] parts) throws HttpPostRequestEncoder.ErrorDataEncoderException, IOException { HttpDataFactory httpDataFactory = new DefaultHttpDataFactory(false); HttpPostRequestEncoder encoder = new HttpPostRequestEncoder(httpDataFactory, request, true); if (parts != null) { for (InMemoryFile part : parts) { FileUpload fileUpload = new MemoryFileUpload(part.name, part.name, "application/octet-stream", "", Charset.forName("UTF-8"), part.content.remaining()); fileUpload.setContent(Unpooled.wrappedBuffer(part.content)); encoder.addBodyHttpData(fileUpload); } } return encoder; }
/** * * @param factory * the factory used to create InterfaceHttpData * @param request * the request to encode * @param multipart * True if the FORM is a ENCTYPE="multipart/form-data" * @param charset * the charset to use as default * @param encoderMode * the mode for the encoder to use. See {@link EncoderMode} for the details. * @throws NullPointerException * for request or charset or factory * @throws ErrorDataEncoderException * if the request is not a POST */ public MyHttpPostRequestEncoder( HttpDataFactory factory, HttpRequest request, boolean multipart, Charset charset, EncoderMode encoderMode) throws ErrorDataEncoderException { if (factory == null) { throw new NullPointerException("factory"); } if (request == null) { throw new NullPointerException("request"); } if (charset == null) { throw new NullPointerException("charset"); } if (request.getMethod() != HttpMethod.POST) { throw new ErrorDataEncoderException("Cannot create a Encoder if not a POST"); } this.request = request; this.charset = charset; this.factory = factory; // Fill default values bodyListDatas = new ArrayList<InterfaceHttpData>(); // default mode isLastChunk = false; isLastChunkSent = false; isMultipart = multipart; multipartHttpDatas = new ArrayList<InterfaceHttpData>(); this.encoderMode = encoderMode; if (isMultipart) { initDataMultipart(); } }
/** * {@inheritDoc} * <p/> * Prepares the request for reading by decoding all the content added via {@link #addContent(HttpContent)}. * @throws RestServiceException if request channel is closed or if the request could not be decoded/prepared. */ @Override public void prepare() throws RestServiceException { if (!isOpen()) { nettyMetrics.multipartRequestAlreadyClosedError.inc(); throw new RestServiceException("Request is closed", RestServiceErrorCode.RequestChannelClosed); } else if (!readyForRead) { // make sure data is held in memory. HttpDataFactory httpDataFactory = new DefaultHttpDataFactory(false); HttpPostMultipartRequestDecoder postRequestDecoder = new HttpPostMultipartRequestDecoder(httpDataFactory, request); try { HttpContent httpContent = rawRequestContents.poll(); while (httpContent != null) { try { // if the request is also an instance of HttpContent, the HttpPostMultipartRequestDecoder does the offer // automatically at the time of construction. We should not add it again. if (httpContent != request) { postRequestDecoder.offer(httpContent); } } finally { ReferenceCountUtil.release(httpContent); } httpContent = rawRequestContents.poll(); } for (InterfaceHttpData part : postRequestDecoder.getBodyHttpDatas()) { processPart(part); } requestContents.add(LastHttpContent.EMPTY_LAST_CONTENT); readyForRead = true; } catch (HttpPostRequestDecoder.ErrorDataDecoderException e) { nettyMetrics.multipartRequestDecodeError.inc(); throw new RestServiceException("There was an error decoding the request", e, RestServiceErrorCode.MalformedRequest); } finally { postRequestDecoder.destroy(); } } }
/** * Creates a {@link HttpPostRequestEncoder} that encodes the given {@code request} and {@code blobContent}. * @param request the {@link HttpRequest} containing headers and other metadata about the request. * @param blobContent the {@link ByteBuffer} that represents the content of the blob. * @return a {@link HttpPostRequestEncoder} that can encode the {@code request} and {@code blobContent}. * @throws HttpPostRequestEncoder.ErrorDataEncoderException * @throws IOException */ private HttpPostRequestEncoder createEncoder(HttpRequest request, ByteBuffer blobContent) throws HttpPostRequestEncoder.ErrorDataEncoderException, IOException { HttpDataFactory httpDataFactory = new DefaultHttpDataFactory(false); HttpPostRequestEncoder encoder = new HttpPostRequestEncoder(httpDataFactory, request, true); FileUpload fileUpload = new MemoryFileUpload(RestUtils.MultipartPost.BLOB_PART, RestUtils.MultipartPost.BLOB_PART, "application/octet-stream", "", Charset.forName("UTF-8"), blobContent.remaining()); fileUpload.setContent(Unpooled.wrappedBuffer(blobContent)); encoder.addBodyHttpData(fileUpload); return encoder; }
void _subscribe(CoreSubscriber<? super Long> s) { if (!parent.markSentHeaders()) { Operators.error(s, new IllegalStateException("headers have already " + "been sent")); return; } HttpDataFactory df = DEFAULT_FACTORY; try { HttpClientFormEncoder encoder = new HttpClientFormEncoder(df, parent.nettyRequest, false, HttpConstants.DEFAULT_CHARSET, HttpPostRequestEncoder.EncoderMode.RFC1738); formCallback.accept(encoder); encoder = encoder.applyChanges(parent.nettyRequest); df = encoder.newFactory; if (!encoder.isMultipart()) { parent.chunkedTransfer(false); } parent.addHandlerFirst(NettyPipeline.ChunkedWriter, new ChunkedWriteHandler()); boolean chunked = HttpUtil.isTransferEncodingChunked(parent.nettyRequest); HttpRequest r = encoder.finalizeRequest(); if (!chunked) { HttpUtil.setTransferEncodingChunked(r, false); HttpUtil.setContentLength(r, encoder.length()); } ChannelFuture f = parent.channel() .writeAndFlush(r); Flux<Long> tail = encoder.progressFlux.onBackpressureLatest(); if (encoder.cleanOnTerminate) { tail = tail.doOnCancel(encoder) .doAfterTerminate(encoder); } if (encoder.isChunked()) { tail.subscribe(s); parent.channel() .writeAndFlush(encoder); } else { FutureMono.from(f) .cast(Long.class) .switchIfEmpty(Mono.just(encoder.length())) .flux() .subscribe(s); } } catch (Throwable e) { Exceptions.throwIfFatal(e); df.cleanRequestHttpData(parent.nettyRequest); Operators.error(s, Exceptions.unwrap(e)); } }
/** * Standard post without multipart but already support on Factory (memory management) * * @return the list of HttpData object (attribute and file) to be reused on next post */ private static List<InterfaceHttpData> formpost( Bootstrap bootstrap, String host, int port, URI uriSimple, File file, HttpDataFactory factory, List<Entry<String, String>> headers) throws Exception { // XXX /formpost // Start the connection attempt. ChannelFuture future = bootstrap.connect(new InetSocketAddress(host, port)); // Wait until the connection attempt succeeds or fails. Channel channel = future.sync().channel(); // Prepare the HTTP request. HttpRequest request = new DefaultHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.POST, uriSimple.toASCIIString()); // Use the PostBody encoder HttpPostRequestEncoder bodyRequestEncoder = new HttpPostRequestEncoder(factory, request, false); // false => not multipart // it is legal to add directly header or cookie into the request until finalize for (Entry<String, String> entry : headers) { request.headers().set(entry.getKey(), entry.getValue()); } // add Form attribute bodyRequestEncoder.addBodyAttribute("getform", "POST"); bodyRequestEncoder.addBodyAttribute("info", "first value"); bodyRequestEncoder.addBodyAttribute("secondinfo", "secondvalue ���&"); bodyRequestEncoder.addBodyAttribute("thirdinfo", textArea); bodyRequestEncoder.addBodyAttribute("fourthinfo", textAreaLong); bodyRequestEncoder.addBodyFileUpload("myfile", file, "application/x-zip-compressed", false); // finalize request request = bodyRequestEncoder.finalizeRequest(); // Create the bodylist to be reused on the last version with Multipart support List<InterfaceHttpData> bodylist = bodyRequestEncoder.getBodyListAttributes(); // send request channel.write(request); // test if request was chunked and if so, finish the write if (bodyRequestEncoder.isChunked()) { // could do either request.isChunked() // either do it through ChunkedWriteHandler channel.write(bodyRequestEncoder); } channel.flush(); // Do not clear here since we will reuse the InterfaceHttpData on the next request // for the example (limit action on client side). Take this as a broadcast of the same // request on both Post actions. // // On standard program, it is clearly recommended to clean all files after each request // bodyRequestEncoder.cleanFiles(); // Wait for the server to close the connection. channel.closeFuture().sync(); return bodylist; }
/** * Multipart example */ private static void formpostmultipart( Bootstrap bootstrap, String host, int port, URI uriFile, HttpDataFactory factory, Iterable<Entry<String, String>> headers, List<InterfaceHttpData> bodylist) throws Exception { // XXX /formpostmultipart // Start the connection attempt. ChannelFuture future = bootstrap.connect(new InetSocketAddress(host, port)); // Wait until the connection attempt succeeds or fails. Channel channel = future.sync().channel(); // Prepare the HTTP request. HttpRequest request = new DefaultHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.POST, uriFile.toASCIIString()); // Use the PostBody encoder HttpPostRequestEncoder bodyRequestEncoder = new HttpPostRequestEncoder(factory, request, true); // true => multipart // it is legal to add directly header or cookie into the request until finalize for (Entry<String, String> entry : headers) { request.headers().set(entry.getKey(), entry.getValue()); } // add Form attribute from previous request in formpost() bodyRequestEncoder.setBodyHttpDatas(bodylist); // finalize request bodyRequestEncoder.finalizeRequest(); // send request channel.write(request); // test if request was chunked and if so, finish the write if (bodyRequestEncoder.isChunked()) { channel.write(bodyRequestEncoder); } channel.flush(); // Now no more use of file representation (and list of HttpData) bodyRequestEncoder.cleanFiles(); // Wait for the server to close the connection. channel.closeFuture().sync(); }
/** * Multipart example */ private static void formpostmultipart( Bootstrap bootstrap, String host, int port, URI uriFile, HttpDataFactory factory, List<Entry<String, String>> headers, List<InterfaceHttpData> bodylist) throws Exception { // XXX /formpostmultipart // Start the connection attempt. ChannelFuture future = bootstrap.connect(new InetSocketAddress(host, port)); // Wait until the connection attempt succeeds or fails. Channel channel = future.sync().channel(); // Prepare the HTTP request. HttpRequest request = new DefaultHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.POST, uriFile.toASCIIString()); // Use the PostBody encoder HttpPostRequestEncoder bodyRequestEncoder = new HttpPostRequestEncoder(factory, request, true); // true => multipart // it is legal to add directly header or cookie into the request until finalize for (Entry<String, String> entry : headers) { request.headers().set(entry.getKey(), entry.getValue()); } // add Form attribute from previous request in formpost() bodyRequestEncoder.setBodyHttpDatas(bodylist); // finalize request bodyRequestEncoder.finalizeRequest(); // send request channel.write(request); // test if request was chunked and if so, finish the write if (bodyRequestEncoder.isChunked()) { channel.write(bodyRequestEncoder); } channel.flush(); // Now no more use of file representation (and list of HttpData) bodyRequestEncoder.cleanFiles(); // Wait for the server to close the connection. channel.closeFuture().sync(); }
@Override public void parse() throws Exception { LOG.trace("CommandName: " + COMMAND_NAME + ": Parse.."); HttpDataFactory factory = new DefaultHttpDataFactory(DefaultHttpDataFactory.MINSIZE); HttpPostRequestDecoder decoder = new HttpPostRequestDecoder(factory, getRequest()); if (decoder.isMultipart()) { LOG.trace("Chunked: " + HttpHeaders.isTransferEncodingChunked(getRequest())); LOG.trace(": Multipart.."); List<InterfaceHttpData> datas = decoder.getBodyHttpDatas(); if (!datas.isEmpty()) { for (InterfaceHttpData data : datas) { LOG.trace("Multipart1 name " + data.getName() + " type " + data.getHttpDataType().name()); if (data.getHttpDataType() == HttpDataType.Attribute) { Attribute attribute = (Attribute) data; if (CommonEpConstans.REQUEST_SIGNATURE_ATTR_NAME.equals(data.getName())) { requestSignature = attribute.get(); if (LOG.isTraceEnabled()) { LOG.trace("Multipart name " + data.getName() + " type " + data.getHttpDataType().name() + " Signature set. size: " + requestSignature.length); LOG.trace(MessageEncoderDecoder.bytesToHex(requestSignature)); } } else if (CommonEpConstans.REQUEST_KEY_ATTR_NAME.equals(data.getName())) { requestKey = attribute.get(); if (LOG.isTraceEnabled()) { LOG.trace("Multipart name " + data.getName() + " type " + data.getHttpDataType().name() + " requestKey set. size: " + requestKey.length); LOG.trace(MessageEncoderDecoder.bytesToHex(requestKey)); } } else if (CommonEpConstans.REQUEST_DATA_ATTR_NAME.equals(data.getName())) { requestData = attribute.get(); if (LOG.isTraceEnabled()) { LOG.trace("Multipart name " + data.getName() + " type " + data.getHttpDataType().name() + " requestData set. size: " + requestData.length); LOG.trace(MessageEncoderDecoder.bytesToHex(requestData)); } } else if (CommonEpConstans.NEXT_PROTOCOL_ATTR_NAME.equals(data.getName())) { nextProtocol = ByteBuffer.wrap(attribute.get()).getInt(); LOG.trace("[{}] next protocol is {}", getSessionUuid(), nextProtocol); } } } } else { LOG.error("Multipart.. size 0"); throw new BadRequestException("HTTP Request inccorect, multiprat size is 0"); } } }
/** * * @param factory * the factory used to create InterfaceHttpData * @param request * the request to encode * @param multipart * True if the FORM is a ENCTYPE="multipart/form-data" * @throws NullPointerException * for request and factory * @throws ErrorDataEncoderException * if the request is not a POST */ public MyHttpPostRequestEncoder(HttpDataFactory factory, HttpRequest request, boolean multipart) throws ErrorDataEncoderException { this(factory, request, multipart, HttpConstants.DEFAULT_CHARSET, EncoderMode.RFC1738); }