1 // 2 // ======================================================================== 3 // Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd. 4 // ------------------------------------------------------------------------ 5 // All rights reserved. This program and the accompanying materials 6 // are made available under the terms of the Eclipse Public License v1.0 7 // and Apache License v2.0 which accompanies this distribution. 8 // 9 // The Eclipse Public License is available at 10 // http://www.eclipse.org/legal/epl-v10.html 11 // 12 // The Apache License v2.0 is available at 13 // http://www.opensource.org/licenses/apache2.0.php 14 // 15 // You may elect to redistribute this code under either of these licenses. 16 // ======================================================================== 17 // 18 19 package org.eclipse.jetty.client; 20 21 import java.io.Closeable; 22 import java.nio.ByteBuffer; 23 import java.util.Collections; 24 import java.util.Iterator; 25 26 import org.eclipse.jetty.client.api.ContentProvider; 27 import org.eclipse.jetty.util.BufferUtil; 28 import org.eclipse.jetty.util.Callback; 29 import org.eclipse.jetty.util.log.Log; 30 import org.eclipse.jetty.util.log.Logger; 31 32 /** 33 * {@link HttpContent} is a stateful, linear representation of the request content provided 34 * by a {@link ContentProvider} that can be traversed one-way to obtain content buffers to 35 * send to a HTTP server. 36 * <p /> 37 * {@link HttpContent} offers the notion of a one-way cursor to traverse the content. 38 * The cursor starts in a virtual "before" position and can be advanced using {@link #advance()} 39 * until it reaches a virtual "after" position where the content is fully consumed. 40 * <pre> 41 * +---+ +---+ +---+ +---+ +---+ 42 * | | | | | | | | | | 43 * +---+ +---+ +---+ +---+ +---+ 44 * ^ ^ ^ ^ 45 * | | --> advance() | | 46 * | | last | 47 * | | | 48 * before | after 49 * | 50 * current 51 * </pre> 52 * At each valid (non-before and non-after) cursor position, {@link HttpContent} provides the following state: 53 * <ul> 54 * <li>the buffer containing the content to send, via {@link #getByteBuffer()}</li> 55 * <li>a copy of the content buffer that can be used for notifications, via {@link #getContent()}</li> 56 * <li>whether the buffer to write is the last one, via {@link #isLast()}</li> 57 * </ul> 58 * {@link HttpContent} may not have content, if the related {@link ContentProvider} is {@code null}, and this 59 * is reflected by {@link #hasContent()}. 60 * <p /> 61 * {@link HttpContent} may have {@link DeferredContentProvider deferred content}, in which case {@link #advance()} 62 * moves the cursor to a position that provides {@code null} {@link #getByteBuffer() buffer} and 63 * {@link #getContent() content}. When the deferred content is available, a further call to {@link #advance()} 64 * will move the cursor to a position that provides non {@code null} buffer and content. 65 */ 66 public class HttpContent implements Callback, Closeable 67 { 68 private static final Logger LOG = Log.getLogger(HttpContent.class); 69 private static final ByteBuffer AFTER = ByteBuffer.allocate(0); 70 71 private final ContentProvider provider; 72 private final Iterator<ByteBuffer> iterator; 73 private ByteBuffer buffer; 74 private volatile ByteBuffer content; 75 76 public HttpContent(ContentProvider provider) 77 { 78 this.provider = provider; 79 this.iterator = provider == null ? Collections.<ByteBuffer>emptyIterator() : provider.iterator(); 80 } 81 82 /** 83 * @return whether there is any content at all 84 */ 85 public boolean hasContent() 86 { 87 return provider != null; 88 } 89 90 /** 91 * @return whether the cursor points to the last content 92 */ 93 public boolean isLast() 94 { 95 return !iterator.hasNext(); 96 } 97 98 /** 99 * @return the {@link ByteBuffer} containing the content at the cursor's position 100 */ 101 public ByteBuffer getByteBuffer() 102 { 103 return buffer; 104 } 105 106 /** 107 * @return a {@link ByteBuffer#slice()} of {@link #getByteBuffer()} at the cursor's position 108 */ 109 public ByteBuffer getContent() 110 { 111 return content; 112 } 113 114 /** 115 * Advances the cursor to the next block of content. 116 * <p /> 117 * The next block of content may be valid (which yields a non-null buffer 118 * returned by {@link #getByteBuffer()}), but may also be deferred 119 * (which yields a null buffer returned by {@link #getByteBuffer()}). 120 * <p /> 121 * If the block of content pointed by the new cursor position is valid, this method returns true. 122 * 123 * @return true if there is content at the new cursor's position, false otherwise. 124 */ 125 public boolean advance() 126 { 127 if (isLast()) 128 { 129 if (content != AFTER) 130 { 131 content = buffer = AFTER; 132 if (LOG.isDebugEnabled()) 133 LOG.debug("Advanced content past last chunk"); 134 } 135 return false; 136 } 137 else 138 { 139 ByteBuffer buffer = this.buffer = iterator.next(); 140 if (LOG.isDebugEnabled()) 141 LOG.debug("Advanced content to {} chunk {}", isLast() ? "last" : "next", buffer); 142 content = buffer == null ? null : buffer.slice(); 143 return buffer != null; 144 } 145 } 146 147 /** 148 * @return whether the cursor has been advanced past the {@link #isLast() last} position. 149 */ 150 public boolean isConsumed() 151 { 152 return content == AFTER; 153 } 154 155 @Override 156 public void succeeded() 157 { 158 if (isConsumed()) 159 return; 160 if (iterator instanceof Callback) 161 ((Callback)iterator).succeeded(); 162 } 163 164 @Override 165 public void failed(Throwable x) 166 { 167 if (isConsumed()) 168 return; 169 if (iterator instanceof Callback) 170 ((Callback)iterator).failed(x); 171 } 172 173 @Override 174 public void close() 175 { 176 try 177 { 178 if (iterator instanceof Closeable) 179 ((Closeable)iterator).close(); 180 } 181 catch (Exception x) 182 { 183 LOG.ignore(x); 184 } 185 } 186 187 @Override 188 public String toString() 189 { 190 return String.format("%s@%x - has=%b,last=%b,consumed=%b,buffer=%s", 191 getClass().getSimpleName(), 192 hashCode(), 193 hasContent(), 194 isLast(), 195 isConsumed(), 196 BufferUtil.toDetailString(getContent())); 197 } 198 }