View Javadoc

1   /*
2    * ====================================================================
3    * Licensed to the Apache Software Foundation (ASF) under one
4    * or more contributor license agreements.  See the NOTICE file
5    * distributed with this work for additional information
6    * regarding copyright ownership.  The ASF licenses this file
7    * to you under the Apache License, Version 2.0 (the
8    * "License"); you may not use this file except in compliance
9    * with the License.  You may obtain a copy of the License at
10   *
11   *   http://www.apache.org/licenses/LICENSE-2.0
12   *
13   * Unless required by applicable law or agreed to in writing,
14   * software distributed under the License is distributed on an
15   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16   * KIND, either express or implied.  See the License for the
17   * specific language governing permissions and limitations
18   * under the License.
19   * ====================================================================
20   *
21   * This software consists of voluntary contributions made by many
22   * individuals on behalf of the Apache Software Foundation.  For more
23   * information on the Apache Software Foundation, please see
24   * <http://www.apache.org/>.
25   *
26   */
27  
28  package org.apache.http.impl.nio.reactor;
29  
30  import java.io.IOException;
31  import java.nio.ByteBuffer;
32  import java.nio.CharBuffer;
33  import java.nio.channels.ReadableByteChannel;
34  import java.nio.channels.WritableByteChannel;
35  import java.nio.charset.CharacterCodingException;
36  import java.nio.charset.Charset;
37  import java.nio.charset.CharsetEncoder;
38  import java.nio.charset.CoderResult;
39  
40  import org.apache.http.annotation.NotThreadSafe;
41  import org.apache.http.nio.reactor.SessionOutputBuffer;
42  import org.apache.http.nio.util.ByteBufferAllocator;
43  import org.apache.http.nio.util.ExpandableBuffer;
44  import org.apache.http.nio.util.HeapByteBufferAllocator;
45  import org.apache.http.params.HttpParams;
46  import org.apache.http.params.HttpProtocolParams;
47  import org.apache.http.protocol.HTTP;
48  import org.apache.http.util.CharArrayBuffer;
49  
50  /**
51   * Default implementation of {@link SessionOutputBuffer} based on
52   * the {@link ExpandableBuffer} class.
53   * <p>
54   * The following parameters can be used to customize the behavior of this
55   * class:
56   * <ul>
57   *  <li>{@link org.apache.http.params.CoreProtocolPNames#HTTP_ELEMENT_CHARSET}</li>
58   * </ul>
59   *
60   * @since 4.0
61   */
62  @NotThreadSafe
63  public class SessionOutputBufferImpl extends ExpandableBuffer implements SessionOutputBuffer {
64  
65      private static final byte[] CRLF = new byte[] {HTTP.CR, HTTP.LF};
66  
67      private CharBuffer charbuffer = null;
68      private Charset charset = null;
69      private CharsetEncoder charencoder = null;
70  
71      public SessionOutputBufferImpl(
72              int buffersize,
73              int linebuffersize,
74              final ByteBufferAllocator allocator,
75              final HttpParams params) {
76          super(buffersize, allocator);
77          this.charbuffer = CharBuffer.allocate(linebuffersize);
78          this.charset = Charset.forName(HttpProtocolParams.getHttpElementCharset(params));
79          this.charencoder = this.charset.newEncoder();
80          this.charencoder.onMalformedInput(HttpProtocolParams.getMalformedInputAction(params));
81          this.charencoder.onUnmappableCharacter(HttpProtocolParams.getUnmappableInputAction(params));
82      }
83  
84      public SessionOutputBufferImpl(
85              int buffersize,
86              int linebuffersize,
87              final HttpParams params) {
88          this(buffersize, linebuffersize, new HeapByteBufferAllocator(), params);
89      }
90  
91      public void reset(final HttpParams params) {
92          clear();
93      }
94  
95      public int flush(final WritableByteChannel channel) throws IOException {
96          if (channel == null) {
97              throw new IllegalArgumentException("Channel may not be null");
98          }
99          setOutputMode();
100         int noWritten = channel.write(this.buffer);
101         return noWritten;
102     }
103 
104     public void write(final ByteBuffer src) {
105         if (src == null) {
106             return;
107         }
108         setInputMode();
109         int requiredCapacity = this.buffer.position() + src.remaining();
110         ensureCapacity(requiredCapacity);
111         this.buffer.put(src);
112     }
113 
114     public void write(final ReadableByteChannel src) throws IOException {
115         if (src == null) {
116             return;
117         }
118         setInputMode();
119         src.read(this.buffer);
120     }
121 
122     private void write(final byte[] b) {
123         if (b == null) {
124             return;
125         }
126         setInputMode();
127         int off = 0;
128         int len = b.length;
129         int requiredCapacity = this.buffer.position() + len;
130         ensureCapacity(requiredCapacity);
131         this.buffer.put(b, off, len);
132     }
133 
134     private void writeCRLF() {
135         write(CRLF);
136     }
137 
138     public void writeLine(final CharArrayBuffer linebuffer) throws CharacterCodingException {
139         if (linebuffer == null) {
140             return;
141         }
142         // Do not bother if the buffer is empty
143         if (linebuffer.length() > 0 ) {
144             setInputMode();
145             this.charencoder.reset();
146             // transfer the string in small chunks
147             int remaining = linebuffer.length();
148             int offset = 0;
149             while (remaining > 0) {
150                 int l = this.charbuffer.remaining();
151                 boolean eol = false;
152                 if (remaining <= l) {
153                     l = remaining;
154                     // terminate the encoding process
155                     eol = true;
156                 }
157                 this.charbuffer.put(linebuffer.buffer(), offset, l);
158                 this.charbuffer.flip();
159 
160                 boolean retry = true;
161                 while (retry) {
162                     CoderResult result = this.charencoder.encode(this.charbuffer, this.buffer, eol);
163                     if (result.isError()) {
164                         result.throwException();
165                     }
166                     if (result.isOverflow()) {
167                         expand();
168                     }
169                     retry = !result.isUnderflow();
170                 }
171                 this.charbuffer.compact();
172                 offset += l;
173                 remaining -= l;
174             }
175             // flush the encoder
176             boolean retry = true;
177             while (retry) {
178                 CoderResult result = this.charencoder.flush(this.buffer);
179                 if (result.isError()) {
180                     result.throwException();
181                 }
182                 if (result.isOverflow()) {
183                     expand();
184                 }
185                 retry = !result.isUnderflow();
186             }
187         }
188         writeCRLF();
189     }
190 
191     public void writeLine(final String s) throws IOException {
192         if (s == null) {
193             return;
194         }
195         if (s.length() > 0) {
196             CharArrayBuffer tmp = new CharArrayBuffer(s.length());
197             tmp.append(s);
198             writeLine(tmp);
199         } else {
200             write(CRLF);
201         }
202     }
203 
204 }