View Javadoc

1   //
2   //  ========================================================================
3   //  Copyright (c) 1995-2013 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.server;
20  
21  import java.io.IOException;
22  import java.io.OutputStreamWriter;
23  import java.io.Writer;
24  
25  import org.eclipse.jetty.http.AbstractGenerator;
26  import org.eclipse.jetty.util.ByteArrayOutputStream2;
27  import org.eclipse.jetty.util.StringUtil;
28  
29  /** OutputWriter.
30   * A writer that can wrap a {@link HttpOutput} stream and provide
31   * character encodings.
32   *
33   * The UTF-8 encoding is done by this class and no additional 
34   * buffers or Writers are used.
35   * The UTF-8 code was inspired by http://javolution.org
36   */
37  public class HttpWriter extends Writer
38  {
39      public static final int MAX_OUTPUT_CHARS = 512; 
40      
41      private static final int WRITE_CONV = 0;
42      private static final int WRITE_ISO1 = 1;
43      private static final int WRITE_UTF8 = 2;
44      
45      final HttpOutput _out;
46      final AbstractGenerator _generator;
47      int _writeMode;
48      int _surrogate;
49  
50      /* ------------------------------------------------------------ */
51      public HttpWriter(HttpOutput out)
52      {
53          _out=out;
54          _generator=_out._generator;
55          _surrogate=0; // AS lastUTF16CodePoint
56      }
57  
58      /* ------------------------------------------------------------ */
59      public void setCharacterEncoding(String encoding)
60      {
61          if (encoding == null || StringUtil.__ISO_8859_1.equalsIgnoreCase(encoding))
62          {
63              _writeMode = WRITE_ISO1;
64          }
65          else if (StringUtil.__UTF8.equalsIgnoreCase(encoding))
66          {
67              _writeMode = WRITE_UTF8;
68          }
69          else
70          {
71              _writeMode = WRITE_CONV;
72              if (_out._characterEncoding == null || !_out._characterEncoding.equalsIgnoreCase(encoding))
73                  _out._converter = null; // Set lazily in getConverter()
74          }
75          
76          _out._characterEncoding = encoding;
77          if (_out._bytes==null)
78              _out._bytes = new ByteArrayOutputStream2(MAX_OUTPUT_CHARS);
79      }
80  
81      /* ------------------------------------------------------------ */
82      @Override
83      public void close() throws IOException
84      {
85          _out.close();
86      }
87  
88      /* ------------------------------------------------------------ */
89      @Override
90      public void flush() throws IOException
91      {
92          _out.flush();
93      }
94  
95      /* ------------------------------------------------------------ */
96      @Override
97      public void write (String s,int offset, int length) throws IOException
98      {   
99          while (length > MAX_OUTPUT_CHARS)
100         {
101             write(s, offset, MAX_OUTPUT_CHARS);
102             offset += MAX_OUTPUT_CHARS;
103             length -= MAX_OUTPUT_CHARS;
104         }
105 
106         if (_out._chars==null)
107         {
108             _out._chars = new char[MAX_OUTPUT_CHARS]; 
109         }
110         char[] chars = _out._chars;
111         s.getChars(offset, offset + length, chars, 0);
112         write(chars, 0, length);
113     }
114 
115     /* ------------------------------------------------------------ */
116     @Override
117     public void write (char[] s,int offset, int length) throws IOException
118     {              
119         HttpOutput out = _out; 
120         
121         while (length > 0)
122         {  
123             out._bytes.reset();
124             int chars = length>MAX_OUTPUT_CHARS?MAX_OUTPUT_CHARS:length;
125             
126             switch (_writeMode)
127             {
128                 case WRITE_CONV:
129                 {
130                     Writer converter=getConverter();
131                     converter.write(s, offset, chars);
132                     converter.flush();
133                 }
134                 break;
135 
136                 case WRITE_ISO1:
137                 {
138                     byte[] buffer=out._bytes.getBuf();
139                     int bytes=out._bytes.getCount();
140                     
141                     if (chars>buffer.length-bytes)
142                         chars=buffer.length-bytes;
143 
144                     for (int i = 0; i < chars; i++)
145                     {
146                         int c = s[offset+i];
147                         buffer[bytes++]=(byte)(c<256?c:'?'); // ISO-1 and UTF-8 match for 0 - 255
148                     }
149                     if (bytes>=0)
150                         out._bytes.setCount(bytes);
151 
152                     break;
153                 }
154 
155                 case WRITE_UTF8:
156                 {
157                     byte[] buffer=out._bytes.getBuf();
158                     int bytes=out._bytes.getCount();
159 
160                     if (bytes+chars>buffer.length)
161                         chars=buffer.length-bytes;
162 
163                     for (int i = 0; i < chars; i++)
164                     {
165                         int code = s[offset+i];
166 
167                         // Do we already have a surrogate?
168                         if(_surrogate==0)
169                         {
170                             // No - is this char code a surrogate?
171                             if(Character.isHighSurrogate((char)code))
172                             {
173                                 _surrogate=code; // UCS-?
174                                 continue;
175                             }                            
176                         }
177                         // else handle a low surrogate
178                         else if(Character.isLowSurrogate((char)code))
179                         {
180                             code = Character.toCodePoint((char)_surrogate, (char)code); // UCS-4
181                         }
182                         // else UCS-2
183                         else
184                         {
185                             code=_surrogate; // UCS-2
186                             _surrogate=0; // USED
187                             i--;
188                         }
189 
190                         if ((code & 0xffffff80) == 0) 
191                         {
192                             // 1b
193                             if (bytes>=buffer.length)
194                             {
195                                 chars=i;
196                                 break;
197                             }
198                             buffer[bytes++]=(byte)(code);
199                         }
200                         else
201                         {
202                             if((code&0xfffff800)==0)
203                             {
204                                 // 2b
205                                 if (bytes+2>buffer.length)
206                                 {
207                                     chars=i;
208                                     break;
209                                 }
210                                 buffer[bytes++]=(byte)(0xc0|(code>>6));
211                                 buffer[bytes++]=(byte)(0x80|(code&0x3f));
212                             }
213                             else if((code&0xffff0000)==0)
214                             {
215                                 // 3b
216                                 if (bytes+3>buffer.length)
217                                 {
218                                     chars=i;
219                                     break;
220                                 }
221                                 buffer[bytes++]=(byte)(0xe0|(code>>12));
222                                 buffer[bytes++]=(byte)(0x80|((code>>6)&0x3f));
223                                 buffer[bytes++]=(byte)(0x80|(code&0x3f));
224                             }
225                             else if((code&0xff200000)==0)
226                             {
227                                 // 4b
228                                 if (bytes+4>buffer.length)
229                                 {
230                                     chars=i;
231                                     break;
232                                 }
233                                 buffer[bytes++]=(byte)(0xf0|(code>>18));
234                                 buffer[bytes++]=(byte)(0x80|((code>>12)&0x3f));
235                                 buffer[bytes++]=(byte)(0x80|((code>>6)&0x3f));
236                                 buffer[bytes++]=(byte)(0x80|(code&0x3f));
237                             }
238                             else if((code&0xf4000000)==0)
239                             {
240                                 // 5b
241                                 if (bytes+5>buffer.length)
242                                 {
243                                     chars=i;
244                                     break;
245                                 }
246                                 buffer[bytes++]=(byte)(0xf8|(code>>24));
247                                 buffer[bytes++]=(byte)(0x80|((code>>18)&0x3f));
248                                 buffer[bytes++]=(byte)(0x80|((code>>12)&0x3f));
249                                 buffer[bytes++]=(byte)(0x80|((code>>6)&0x3f));
250                                 buffer[bytes++]=(byte)(0x80|(code&0x3f));
251                             }
252                             else if((code&0x80000000)==0)
253                             {
254                                 // 6b
255                                 if (bytes+6>buffer.length)
256                                 {
257                                     chars=i;
258                                     break;
259                                 }
260                                 buffer[bytes++]=(byte)(0xfc|(code>>30));
261                                 buffer[bytes++]=(byte)(0x80|((code>>24)&0x3f));
262                                 buffer[bytes++]=(byte)(0x80|((code>>18)&0x3f));
263                                 buffer[bytes++]=(byte)(0x80|((code>>12)&0x3f));
264                                 buffer[bytes++]=(byte)(0x80|((code>>6)&0x3f));
265                                 buffer[bytes++]=(byte)(0x80|(code&0x3f));
266                             }
267                             else
268                             {
269                                 buffer[bytes++]=(byte)('?');
270                             } 
271 
272                             _surrogate=0; // USED
273 
274                             if (bytes==buffer.length)
275                             {
276                                 chars=i+1;
277                                 break;
278                             }
279                         }
280                     }
281                     out._bytes.setCount(bytes);
282                     break;
283                 }
284                 default:
285                     throw new IllegalStateException();
286             }
287             
288             out._bytes.writeTo(out);
289             length-=chars;
290             offset+=chars;
291         }
292     }
293     
294     
295     /* ------------------------------------------------------------ */
296     private Writer getConverter() throws IOException
297     {
298         if (_out._converter == null)
299             _out._converter = new OutputStreamWriter(_out._bytes, _out._characterEncoding);
300         return _out._converter;
301     }   
302 }